Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | #include "secitem.h" |
michael@0 | 5 | #include "pkcs11.h" |
michael@0 | 6 | #include "lgdb.h" |
michael@0 | 7 | #include "pcert.h" |
michael@0 | 8 | #include "lowkeyi.h" |
michael@0 | 9 | #include "blapi.h" |
michael@0 | 10 | #include "secder.h" |
michael@0 | 11 | #include "secasn1.h" |
michael@0 | 12 | |
michael@0 | 13 | #include "keydbi.h" |
michael@0 | 14 | |
michael@0 | 15 | /* |
michael@0 | 16 | * ******************** Object Creation Utilities *************************** |
michael@0 | 17 | */ |
michael@0 | 18 | |
michael@0 | 19 | /* |
michael@0 | 20 | * check the consistancy and initialize a Certificate Object |
michael@0 | 21 | */ |
michael@0 | 22 | static CK_RV |
michael@0 | 23 | lg_createCertObject(SDB *sdb, CK_OBJECT_HANDLE *handle, |
michael@0 | 24 | const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 25 | { |
michael@0 | 26 | SECItem derCert; |
michael@0 | 27 | NSSLOWCERTCertificate *cert; |
michael@0 | 28 | NSSLOWCERTCertTrust *trust = NULL; |
michael@0 | 29 | NSSLOWCERTCertTrust userTrust = |
michael@0 | 30 | { CERTDB_USER, CERTDB_USER, CERTDB_USER }; |
michael@0 | 31 | NSSLOWCERTCertTrust defTrust = |
michael@0 | 32 | { CERTDB_TRUSTED_UNKNOWN, |
michael@0 | 33 | CERTDB_TRUSTED_UNKNOWN, CERTDB_TRUSTED_UNKNOWN }; |
michael@0 | 34 | char *label = NULL; |
michael@0 | 35 | char *email = NULL; |
michael@0 | 36 | SECStatus rv; |
michael@0 | 37 | CK_RV crv; |
michael@0 | 38 | PRBool inDB = PR_TRUE; |
michael@0 | 39 | NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb); |
michael@0 | 40 | NSSLOWKEYDBHandle *keyHandle = NULL; |
michael@0 | 41 | CK_CERTIFICATE_TYPE type; |
michael@0 | 42 | const CK_ATTRIBUTE *attribute; |
michael@0 | 43 | |
michael@0 | 44 | /* we can't store any certs private */ |
michael@0 | 45 | if (lg_isTrue(CKA_PRIVATE, templ, count)) { |
michael@0 | 46 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | /* We only support X.509 Certs for now */ |
michael@0 | 50 | crv = lg_GetULongAttribute(CKA_CERTIFICATE_TYPE, templ, count, &type); |
michael@0 | 51 | if (crv != CKR_OK) { |
michael@0 | 52 | return crv; |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | if (type != CKC_X_509) { |
michael@0 | 56 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /* X.509 Certificate */ |
michael@0 | 60 | |
michael@0 | 61 | |
michael@0 | 62 | if (certHandle == NULL) { |
michael@0 | 63 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 64 | } |
michael@0 | 65 | |
michael@0 | 66 | /* get the der cert */ |
michael@0 | 67 | attribute = lg_FindAttribute(CKA_VALUE, templ, count); |
michael@0 | 68 | if (!attribute) { |
michael@0 | 69 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | derCert.type = 0; |
michael@0 | 73 | derCert.data = (unsigned char *)attribute->pValue; |
michael@0 | 74 | derCert.len = attribute->ulValueLen ; |
michael@0 | 75 | |
michael@0 | 76 | label = lg_getString(CKA_LABEL, templ, count); |
michael@0 | 77 | |
michael@0 | 78 | cert = nsslowcert_FindCertByDERCert(certHandle, &derCert); |
michael@0 | 79 | if (cert == NULL) { |
michael@0 | 80 | cert = nsslowcert_DecodeDERCertificate(&derCert, label); |
michael@0 | 81 | inDB = PR_FALSE; |
michael@0 | 82 | } |
michael@0 | 83 | if (cert == NULL) { |
michael@0 | 84 | if (label) PORT_Free(label); |
michael@0 | 85 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | keyHandle = lg_getKeyDB(sdb); |
michael@0 | 89 | if (keyHandle) { |
michael@0 | 90 | if (nsslowkey_KeyForCertExists(keyHandle,cert)) { |
michael@0 | 91 | trust = &userTrust; |
michael@0 | 92 | } |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | if (!inDB) { |
michael@0 | 96 | if (!trust) trust = &defTrust; |
michael@0 | 97 | rv = nsslowcert_AddPermCert(certHandle, cert, label, trust); |
michael@0 | 98 | } else { |
michael@0 | 99 | rv = trust ? nsslowcert_ChangeCertTrust(certHandle,cert,trust) : |
michael@0 | 100 | SECSuccess; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | if (label) PORT_Free(label); |
michael@0 | 104 | |
michael@0 | 105 | if (rv != SECSuccess) { |
michael@0 | 106 | nsslowcert_DestroyCertificate(cert); |
michael@0 | 107 | return CKR_DEVICE_ERROR; |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | /* |
michael@0 | 111 | * Add a NULL S/MIME profile if necessary. |
michael@0 | 112 | */ |
michael@0 | 113 | email = lg_getString(CKA_NSS_EMAIL, templ, count); |
michael@0 | 114 | if (email) { |
michael@0 | 115 | certDBEntrySMime *entry; |
michael@0 | 116 | |
michael@0 | 117 | entry = nsslowcert_ReadDBSMimeEntry(certHandle,email); |
michael@0 | 118 | if (!entry) { |
michael@0 | 119 | nsslowcert_SaveSMimeProfile(certHandle, email, |
michael@0 | 120 | &cert->derSubject, NULL, NULL); |
michael@0 | 121 | } else { |
michael@0 | 122 | nsslowcert_DestroyDBEntry((certDBEntry *)entry); |
michael@0 | 123 | } |
michael@0 | 124 | PORT_Free(email); |
michael@0 | 125 | } |
michael@0 | 126 | *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT); |
michael@0 | 127 | nsslowcert_DestroyCertificate(cert); |
michael@0 | 128 | |
michael@0 | 129 | return CKR_OK; |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | unsigned int |
michael@0 | 133 | lg_MapTrust(CK_TRUST trust, PRBool clientAuth) |
michael@0 | 134 | { |
michael@0 | 135 | unsigned int trustCA = clientAuth ? CERTDB_TRUSTED_CLIENT_CA : |
michael@0 | 136 | CERTDB_TRUSTED_CA; |
michael@0 | 137 | switch (trust) { |
michael@0 | 138 | case CKT_NSS_TRUSTED: |
michael@0 | 139 | return CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED; |
michael@0 | 140 | case CKT_NSS_TRUSTED_DELEGATOR: |
michael@0 | 141 | return CERTDB_VALID_CA|trustCA; |
michael@0 | 142 | case CKT_NSS_MUST_VERIFY_TRUST: |
michael@0 | 143 | return CERTDB_MUST_VERIFY; |
michael@0 | 144 | case CKT_NSS_NOT_TRUSTED: |
michael@0 | 145 | return CERTDB_TERMINAL_RECORD; |
michael@0 | 146 | case CKT_NSS_VALID_DELEGATOR: /* implies must verify */ |
michael@0 | 147 | return CERTDB_VALID_CA; |
michael@0 | 148 | default: |
michael@0 | 149 | break; |
michael@0 | 150 | } |
michael@0 | 151 | return CERTDB_TRUSTED_UNKNOWN; |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | |
michael@0 | 155 | /* |
michael@0 | 156 | * check the consistancy and initialize a Trust Object |
michael@0 | 157 | */ |
michael@0 | 158 | static CK_RV |
michael@0 | 159 | lg_createTrustObject(SDB *sdb, CK_OBJECT_HANDLE *handle, |
michael@0 | 160 | const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 161 | { |
michael@0 | 162 | const CK_ATTRIBUTE *issuer = NULL; |
michael@0 | 163 | const CK_ATTRIBUTE *serial = NULL; |
michael@0 | 164 | NSSLOWCERTCertificate *cert = NULL; |
michael@0 | 165 | const CK_ATTRIBUTE *trust; |
michael@0 | 166 | CK_TRUST sslTrust = CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 167 | CK_TRUST clientTrust = CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 168 | CK_TRUST emailTrust = CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 169 | CK_TRUST signTrust = CKT_NSS_TRUST_UNKNOWN; |
michael@0 | 170 | CK_BBOOL stepUp; |
michael@0 | 171 | NSSLOWCERTCertTrust dbTrust = { 0 }; |
michael@0 | 172 | SECStatus rv; |
michael@0 | 173 | NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb); |
michael@0 | 174 | NSSLOWCERTIssuerAndSN issuerSN; |
michael@0 | 175 | |
michael@0 | 176 | /* we can't store any certs private */ |
michael@0 | 177 | if (lg_isTrue(CKA_PRIVATE, templ, count)) { |
michael@0 | 178 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | if (certHandle == NULL) { |
michael@0 | 182 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | issuer = lg_FindAttribute(CKA_ISSUER, templ, count); |
michael@0 | 186 | serial = lg_FindAttribute(CKA_SERIAL_NUMBER, templ, count); |
michael@0 | 187 | |
michael@0 | 188 | if (issuer && serial) { |
michael@0 | 189 | issuerSN.derIssuer.data = (unsigned char *)issuer->pValue; |
michael@0 | 190 | issuerSN.derIssuer.len = issuer->ulValueLen ; |
michael@0 | 191 | |
michael@0 | 192 | issuerSN.serialNumber.data = (unsigned char *)serial->pValue; |
michael@0 | 193 | issuerSN.serialNumber.len = serial->ulValueLen ; |
michael@0 | 194 | |
michael@0 | 195 | cert = nsslowcert_FindCertByIssuerAndSN(certHandle,&issuerSN); |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | if (cert == NULL) { |
michael@0 | 199 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | lg_GetULongAttribute(CKA_TRUST_SERVER_AUTH, templ, count, &sslTrust); |
michael@0 | 203 | lg_GetULongAttribute(CKA_TRUST_CLIENT_AUTH, templ, count, &clientTrust); |
michael@0 | 204 | lg_GetULongAttribute(CKA_TRUST_EMAIL_PROTECTION, templ, count, &emailTrust); |
michael@0 | 205 | lg_GetULongAttribute(CKA_TRUST_CODE_SIGNING, templ, count, &signTrust); |
michael@0 | 206 | stepUp = CK_FALSE; |
michael@0 | 207 | trust = lg_FindAttribute(CKA_TRUST_STEP_UP_APPROVED, templ, count); |
michael@0 | 208 | if (trust) { |
michael@0 | 209 | if (trust->ulValueLen == sizeof(CK_BBOOL)) { |
michael@0 | 210 | stepUp = *(CK_BBOOL*)trust->pValue; |
michael@0 | 211 | } |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | /* preserve certain old fields */ |
michael@0 | 215 | if (cert->trust) { |
michael@0 | 216 | dbTrust.sslFlags = cert->trust->sslFlags & CERTDB_PRESERVE_TRUST_BITS; |
michael@0 | 217 | dbTrust.emailFlags= |
michael@0 | 218 | cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS; |
michael@0 | 219 | dbTrust.objectSigningFlags = |
michael@0 | 220 | cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS; |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | dbTrust.sslFlags |= lg_MapTrust(sslTrust,PR_FALSE); |
michael@0 | 224 | dbTrust.sslFlags |= lg_MapTrust(clientTrust,PR_TRUE); |
michael@0 | 225 | dbTrust.emailFlags |= lg_MapTrust(emailTrust,PR_FALSE); |
michael@0 | 226 | dbTrust.objectSigningFlags |= lg_MapTrust(signTrust,PR_FALSE); |
michael@0 | 227 | if (stepUp) { |
michael@0 | 228 | dbTrust.sslFlags |= CERTDB_GOVT_APPROVED_CA; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | rv = nsslowcert_ChangeCertTrust(certHandle,cert,&dbTrust); |
michael@0 | 232 | *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST); |
michael@0 | 233 | nsslowcert_DestroyCertificate(cert); |
michael@0 | 234 | if (rv != SECSuccess) { |
michael@0 | 235 | return CKR_DEVICE_ERROR; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | return CKR_OK; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | /* |
michael@0 | 242 | * check the consistancy and initialize a Trust Object |
michael@0 | 243 | */ |
michael@0 | 244 | static CK_RV |
michael@0 | 245 | lg_createSMimeObject(SDB *sdb, CK_OBJECT_HANDLE *handle, |
michael@0 | 246 | const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 247 | { |
michael@0 | 248 | SECItem derSubj,rawProfile,rawTime,emailKey; |
michael@0 | 249 | SECItem *pRawProfile = NULL; |
michael@0 | 250 | SECItem *pRawTime = NULL; |
michael@0 | 251 | char *email = NULL; |
michael@0 | 252 | const CK_ATTRIBUTE *subject = NULL, |
michael@0 | 253 | *profile = NULL, |
michael@0 | 254 | *time = NULL; |
michael@0 | 255 | SECStatus rv; |
michael@0 | 256 | NSSLOWCERTCertDBHandle *certHandle; |
michael@0 | 257 | CK_RV ck_rv = CKR_OK; |
michael@0 | 258 | |
michael@0 | 259 | /* we can't store any certs private */ |
michael@0 | 260 | if (lg_isTrue(CKA_PRIVATE,templ,count)) { |
michael@0 | 261 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | certHandle = lg_getCertDB(sdb); |
michael@0 | 265 | if (certHandle == NULL) { |
michael@0 | 266 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | /* lookup SUBJECT */ |
michael@0 | 270 | subject = lg_FindAttribute(CKA_SUBJECT,templ,count); |
michael@0 | 271 | PORT_Assert(subject); |
michael@0 | 272 | if (!subject) { |
michael@0 | 273 | ck_rv = CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 274 | goto loser; |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | derSubj.data = (unsigned char *)subject->pValue; |
michael@0 | 278 | derSubj.len = subject->ulValueLen ; |
michael@0 | 279 | derSubj.type = 0; |
michael@0 | 280 | |
michael@0 | 281 | /* lookup VALUE */ |
michael@0 | 282 | profile = lg_FindAttribute(CKA_VALUE,templ,count); |
michael@0 | 283 | if (profile) { |
michael@0 | 284 | rawProfile.data = (unsigned char *)profile->pValue; |
michael@0 | 285 | rawProfile.len = profile->ulValueLen ; |
michael@0 | 286 | rawProfile.type = siBuffer; |
michael@0 | 287 | pRawProfile = &rawProfile; |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | /* lookup Time */ |
michael@0 | 291 | time = lg_FindAttribute(CKA_NSS_SMIME_TIMESTAMP,templ,count); |
michael@0 | 292 | if (time) { |
michael@0 | 293 | rawTime.data = (unsigned char *)time->pValue; |
michael@0 | 294 | rawTime.len = time->ulValueLen ; |
michael@0 | 295 | rawTime.type = siBuffer; |
michael@0 | 296 | pRawTime = &rawTime; |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | |
michael@0 | 300 | email = lg_getString(CKA_NSS_EMAIL,templ,count); |
michael@0 | 301 | if (!email) { |
michael@0 | 302 | ck_rv = CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 303 | goto loser; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | /* Store S/MIME Profile by SUBJECT */ |
michael@0 | 307 | rv = nsslowcert_SaveSMimeProfile(certHandle, email, &derSubj, |
michael@0 | 308 | pRawProfile,pRawTime); |
michael@0 | 309 | if (rv != SECSuccess) { |
michael@0 | 310 | ck_rv = CKR_DEVICE_ERROR; |
michael@0 | 311 | goto loser; |
michael@0 | 312 | } |
michael@0 | 313 | emailKey.data = (unsigned char *)email; |
michael@0 | 314 | emailKey.len = PORT_Strlen(email)+1; |
michael@0 | 315 | |
michael@0 | 316 | *handle = lg_mkHandle(sdb, &emailKey, LG_TOKEN_TYPE_SMIME); |
michael@0 | 317 | |
michael@0 | 318 | loser: |
michael@0 | 319 | if (email) PORT_Free(email); |
michael@0 | 320 | |
michael@0 | 321 | return ck_rv; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | /* |
michael@0 | 325 | * check the consistancy and initialize a Trust Object |
michael@0 | 326 | */ |
michael@0 | 327 | static CK_RV |
michael@0 | 328 | lg_createCrlObject(SDB *sdb, CK_OBJECT_HANDLE *handle, |
michael@0 | 329 | const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 330 | { |
michael@0 | 331 | PRBool isKRL = PR_FALSE; |
michael@0 | 332 | SECItem derSubj,derCrl; |
michael@0 | 333 | char *url = NULL; |
michael@0 | 334 | const CK_ATTRIBUTE *subject,*crl; |
michael@0 | 335 | SECStatus rv; |
michael@0 | 336 | NSSLOWCERTCertDBHandle *certHandle; |
michael@0 | 337 | |
michael@0 | 338 | certHandle = lg_getCertDB(sdb); |
michael@0 | 339 | |
michael@0 | 340 | /* we can't store any private crls */ |
michael@0 | 341 | if (lg_isTrue(CKA_PRIVATE,templ,count)) { |
michael@0 | 342 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | if (certHandle == NULL) { |
michael@0 | 346 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 347 | } |
michael@0 | 348 | |
michael@0 | 349 | /* lookup SUBJECT */ |
michael@0 | 350 | subject = lg_FindAttribute(CKA_SUBJECT,templ,count); |
michael@0 | 351 | if (!subject) { |
michael@0 | 352 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | derSubj.data = (unsigned char *)subject->pValue; |
michael@0 | 356 | derSubj.len = subject->ulValueLen ; |
michael@0 | 357 | |
michael@0 | 358 | /* lookup VALUE */ |
michael@0 | 359 | crl = lg_FindAttribute(CKA_VALUE,templ,count); |
michael@0 | 360 | PORT_Assert(crl); |
michael@0 | 361 | if (!crl) { |
michael@0 | 362 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 363 | } |
michael@0 | 364 | derCrl.data = (unsigned char *)crl->pValue; |
michael@0 | 365 | derCrl.len = crl->ulValueLen ; |
michael@0 | 366 | |
michael@0 | 367 | url = lg_getString(CKA_NSS_URL,templ,count); |
michael@0 | 368 | isKRL = lg_isTrue(CKA_NSS_KRL,templ,count); |
michael@0 | 369 | |
michael@0 | 370 | /* Store CRL by SUBJECT */ |
michael@0 | 371 | rv = nsslowcert_AddCrl(certHandle, &derCrl, &derSubj, url, isKRL); |
michael@0 | 372 | |
michael@0 | 373 | if (url) { |
michael@0 | 374 | PORT_Free(url); |
michael@0 | 375 | } |
michael@0 | 376 | if (rv != SECSuccess) { |
michael@0 | 377 | return CKR_DEVICE_ERROR; |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | /* if we overwrote the existing CRL, poison the handle entry so we get |
michael@0 | 381 | * a new object handle */ |
michael@0 | 382 | (void) lg_poisonHandle(sdb, &derSubj, |
michael@0 | 383 | isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL); |
michael@0 | 384 | *handle = lg_mkHandle(sdb, &derSubj, |
michael@0 | 385 | isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL); |
michael@0 | 386 | |
michael@0 | 387 | return CKR_OK; |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | /* |
michael@0 | 391 | * check the consistancy and initialize a Public Key Object |
michael@0 | 392 | */ |
michael@0 | 393 | static CK_RV |
michael@0 | 394 | lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type, |
michael@0 | 395 | CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 396 | { |
michael@0 | 397 | CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE; |
michael@0 | 398 | CK_RV crv = CKR_OK; |
michael@0 | 399 | NSSLOWKEYPrivateKey *priv; |
michael@0 | 400 | SECItem pubKeySpace = {siBuffer, NULL, 0}; |
michael@0 | 401 | SECItem *pubKey; |
michael@0 | 402 | #ifndef NSS_DISABLE_ECC |
michael@0 | 403 | SECItem pubKey2Space = {siBuffer, NULL, 0}; |
michael@0 | 404 | PLArenaPool *arena = NULL; |
michael@0 | 405 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 406 | NSSLOWKEYDBHandle *keyHandle = NULL; |
michael@0 | 407 | |
michael@0 | 408 | |
michael@0 | 409 | switch (key_type) { |
michael@0 | 410 | case CKK_RSA: |
michael@0 | 411 | pubKeyAttr = CKA_MODULUS; |
michael@0 | 412 | break; |
michael@0 | 413 | #ifndef NSS_DISABLE_ECC |
michael@0 | 414 | case CKK_EC: |
michael@0 | 415 | pubKeyAttr = CKA_EC_POINT; |
michael@0 | 416 | break; |
michael@0 | 417 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 418 | case CKK_DSA: |
michael@0 | 419 | case CKK_DH: |
michael@0 | 420 | break; |
michael@0 | 421 | default: |
michael@0 | 422 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 423 | } |
michael@0 | 424 | |
michael@0 | 425 | |
michael@0 | 426 | pubKey = &pubKeySpace; |
michael@0 | 427 | crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,pubKey); |
michael@0 | 428 | if (crv != CKR_OK) return crv; |
michael@0 | 429 | |
michael@0 | 430 | #ifndef NSS_DISABLE_ECC |
michael@0 | 431 | if (key_type == CKK_EC) { |
michael@0 | 432 | SECStatus rv; |
michael@0 | 433 | /* |
michael@0 | 434 | * for ECC, use the decoded key first. |
michael@0 | 435 | */ |
michael@0 | 436 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
michael@0 | 437 | if (arena == NULL) { |
michael@0 | 438 | crv = CKR_HOST_MEMORY; |
michael@0 | 439 | goto done; |
michael@0 | 440 | } |
michael@0 | 441 | rv= SEC_QuickDERDecodeItem(arena, &pubKey2Space, |
michael@0 | 442 | SEC_ASN1_GET(SEC_OctetStringTemplate), |
michael@0 | 443 | pubKey); |
michael@0 | 444 | if (rv != SECSuccess) { |
michael@0 | 445 | /* decode didn't work, just try the pubKey */ |
michael@0 | 446 | PORT_FreeArena(arena, PR_FALSE); |
michael@0 | 447 | arena = NULL; |
michael@0 | 448 | } else { |
michael@0 | 449 | /* try the decoded pub key first */ |
michael@0 | 450 | pubKey = &pubKey2Space; |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 454 | |
michael@0 | 455 | PORT_Assert(pubKey->data); |
michael@0 | 456 | if (pubKey->data == NULL) { |
michael@0 | 457 | crv = CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 458 | goto done; |
michael@0 | 459 | } |
michael@0 | 460 | keyHandle = lg_getKeyDB(sdb); |
michael@0 | 461 | if (keyHandle == NULL) { |
michael@0 | 462 | crv = CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 463 | goto done; |
michael@0 | 464 | } |
michael@0 | 465 | if (keyHandle->version != 3) { |
michael@0 | 466 | unsigned char buf[SHA1_LENGTH]; |
michael@0 | 467 | SHA1_HashBuf(buf,pubKey->data,pubKey->len); |
michael@0 | 468 | PORT_Memcpy(pubKey->data,buf,sizeof(buf)); |
michael@0 | 469 | pubKey->len = sizeof(buf); |
michael@0 | 470 | } |
michael@0 | 471 | /* make sure the associated private key already exists */ |
michael@0 | 472 | /* only works if we are logged in */ |
michael@0 | 473 | priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, sdb /*password*/); |
michael@0 | 474 | #ifndef NSS_DISABLE_ECC |
michael@0 | 475 | if (priv == NULL && pubKey == &pubKey2Space) { |
michael@0 | 476 | /* no match on the decoded key, match the original pubkey */ |
michael@0 | 477 | pubKey = &pubKeySpace; |
michael@0 | 478 | priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, |
michael@0 | 479 | sdb /*password*/); |
michael@0 | 480 | } |
michael@0 | 481 | #endif |
michael@0 | 482 | if (priv == NULL) { |
michael@0 | 483 | /* the legacy database can only 'store' public keys which already |
michael@0 | 484 | * have their corresponding private keys in the database */ |
michael@0 | 485 | crv = CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 486 | goto done; |
michael@0 | 487 | } |
michael@0 | 488 | lg_nsslowkey_DestroyPrivateKey(priv); |
michael@0 | 489 | crv = CKR_OK; |
michael@0 | 490 | |
michael@0 | 491 | *handle = lg_mkHandle(sdb, pubKey, LG_TOKEN_TYPE_PUB); |
michael@0 | 492 | |
michael@0 | 493 | done: |
michael@0 | 494 | PORT_Free(pubKeySpace.data); |
michael@0 | 495 | #ifndef NSS_DISABLE_ECC |
michael@0 | 496 | if (arena) |
michael@0 | 497 | PORT_FreeArena(arena, PR_FALSE); |
michael@0 | 498 | #endif |
michael@0 | 499 | |
michael@0 | 500 | return crv; |
michael@0 | 501 | } |
michael@0 | 502 | |
michael@0 | 503 | /* make a private key from a verified object */ |
michael@0 | 504 | static NSSLOWKEYPrivateKey * |
michael@0 | 505 | lg_mkPrivKey(SDB *sdb, const CK_ATTRIBUTE *templ, CK_ULONG count, |
michael@0 | 506 | CK_KEY_TYPE key_type, CK_RV *crvp) |
michael@0 | 507 | { |
michael@0 | 508 | NSSLOWKEYPrivateKey *privKey; |
michael@0 | 509 | PLArenaPool *arena; |
michael@0 | 510 | CK_RV crv = CKR_OK; |
michael@0 | 511 | SECStatus rv; |
michael@0 | 512 | |
michael@0 | 513 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
michael@0 | 514 | if (arena == NULL) { |
michael@0 | 515 | *crvp = CKR_HOST_MEMORY; |
michael@0 | 516 | return NULL; |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | privKey = (NSSLOWKEYPrivateKey *) |
michael@0 | 520 | PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey)); |
michael@0 | 521 | if (privKey == NULL) { |
michael@0 | 522 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 523 | *crvp = CKR_HOST_MEMORY; |
michael@0 | 524 | return NULL; |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | /* in future this would be a switch on key_type */ |
michael@0 | 528 | privKey->arena = arena; |
michael@0 | 529 | switch (key_type) { |
michael@0 | 530 | case CKK_RSA: |
michael@0 | 531 | privKey->keyType = NSSLOWKEYRSAKey; |
michael@0 | 532 | crv=lg_Attribute2SSecItem(arena,CKA_MODULUS,templ,count, |
michael@0 | 533 | &privKey->u.rsa.modulus); |
michael@0 | 534 | if (crv != CKR_OK) break; |
michael@0 | 535 | crv=lg_Attribute2SSecItem(arena,CKA_PUBLIC_EXPONENT,templ,count, |
michael@0 | 536 | &privKey->u.rsa.publicExponent); |
michael@0 | 537 | if (crv != CKR_OK) break; |
michael@0 | 538 | crv=lg_PrivAttr2SSecItem(arena,CKA_PRIVATE_EXPONENT,templ,count, |
michael@0 | 539 | &privKey->u.rsa.privateExponent, sdb); |
michael@0 | 540 | if (crv != CKR_OK) break; |
michael@0 | 541 | crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_1,templ,count, |
michael@0 | 542 | &privKey->u.rsa.prime1, sdb); |
michael@0 | 543 | if (crv != CKR_OK) break; |
michael@0 | 544 | crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_2,templ,count, |
michael@0 | 545 | &privKey->u.rsa.prime2, sdb); |
michael@0 | 546 | if (crv != CKR_OK) break; |
michael@0 | 547 | crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_1,templ,count, |
michael@0 | 548 | &privKey->u.rsa.exponent1, sdb); |
michael@0 | 549 | if (crv != CKR_OK) break; |
michael@0 | 550 | crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_2,templ,count, |
michael@0 | 551 | &privKey->u.rsa.exponent2, sdb); |
michael@0 | 552 | if (crv != CKR_OK) break; |
michael@0 | 553 | crv=lg_PrivAttr2SSecItem(arena,CKA_COEFFICIENT,templ,count, |
michael@0 | 554 | &privKey->u.rsa.coefficient, sdb); |
michael@0 | 555 | if (crv != CKR_OK) break; |
michael@0 | 556 | rv = DER_SetUInteger(privKey->arena, &privKey->u.rsa.version, |
michael@0 | 557 | NSSLOWKEY_VERSION); |
michael@0 | 558 | if (rv != SECSuccess) crv = CKR_HOST_MEMORY; |
michael@0 | 559 | break; |
michael@0 | 560 | |
michael@0 | 561 | case CKK_DSA: |
michael@0 | 562 | privKey->keyType = NSSLOWKEYDSAKey; |
michael@0 | 563 | crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count, |
michael@0 | 564 | &privKey->u.dsa.params.prime); |
michael@0 | 565 | if (crv != CKR_OK) break; |
michael@0 | 566 | crv = lg_Attribute2SSecItem(arena,CKA_SUBPRIME,templ,count, |
michael@0 | 567 | &privKey->u.dsa.params.subPrime); |
michael@0 | 568 | if (crv != CKR_OK) break; |
michael@0 | 569 | crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count, |
michael@0 | 570 | &privKey->u.dsa.params.base); |
michael@0 | 571 | if (crv != CKR_OK) break; |
michael@0 | 572 | crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count, |
michael@0 | 573 | &privKey->u.dsa.privateValue, sdb); |
michael@0 | 574 | if (crv != CKR_OK) break; |
michael@0 | 575 | if (lg_hasAttribute(CKA_NETSCAPE_DB, templ,count)) { |
michael@0 | 576 | crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count, |
michael@0 | 577 | &privKey->u.dsa.publicValue); |
michael@0 | 578 | /* privKey was zero'd so public value is already set to NULL, 0 |
michael@0 | 579 | * if we don't set it explicitly */ |
michael@0 | 580 | } |
michael@0 | 581 | break; |
michael@0 | 582 | |
michael@0 | 583 | case CKK_DH: |
michael@0 | 584 | privKey->keyType = NSSLOWKEYDHKey; |
michael@0 | 585 | crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count, |
michael@0 | 586 | &privKey->u.dh.prime); |
michael@0 | 587 | if (crv != CKR_OK) break; |
michael@0 | 588 | crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count, |
michael@0 | 589 | &privKey->u.dh.base); |
michael@0 | 590 | if (crv != CKR_OK) break; |
michael@0 | 591 | crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count, |
michael@0 | 592 | &privKey->u.dh.privateValue, sdb); |
michael@0 | 593 | if (crv != CKR_OK) break; |
michael@0 | 594 | if (lg_hasAttribute(CKA_NETSCAPE_DB, templ, count)) { |
michael@0 | 595 | crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count, |
michael@0 | 596 | &privKey->u.dh.publicValue); |
michael@0 | 597 | /* privKey was zero'd so public value is already set to NULL, 0 |
michael@0 | 598 | * if we don't set it explicitly */ |
michael@0 | 599 | } |
michael@0 | 600 | break; |
michael@0 | 601 | |
michael@0 | 602 | #ifndef NSS_DISABLE_ECC |
michael@0 | 603 | case CKK_EC: |
michael@0 | 604 | privKey->keyType = NSSLOWKEYECKey; |
michael@0 | 605 | crv = lg_Attribute2SSecItem(arena, CKA_EC_PARAMS,templ,count, |
michael@0 | 606 | &privKey->u.ec.ecParams.DEREncoding); |
michael@0 | 607 | if (crv != CKR_OK) break; |
michael@0 | 608 | |
michael@0 | 609 | /* Fill out the rest of the ecParams structure |
michael@0 | 610 | * based on the encoded params |
michael@0 | 611 | */ |
michael@0 | 612 | if (LGEC_FillParams(arena, &privKey->u.ec.ecParams.DEREncoding, |
michael@0 | 613 | &privKey->u.ec.ecParams) != SECSuccess) { |
michael@0 | 614 | crv = CKR_DOMAIN_PARAMS_INVALID; |
michael@0 | 615 | break; |
michael@0 | 616 | } |
michael@0 | 617 | crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count, |
michael@0 | 618 | &privKey->u.ec.privateValue, sdb); |
michael@0 | 619 | if (crv != CKR_OK) break; |
michael@0 | 620 | if (lg_hasAttribute(CKA_NETSCAPE_DB,templ,count)) { |
michael@0 | 621 | crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count, |
michael@0 | 622 | &privKey->u.ec.publicValue); |
michael@0 | 623 | if (crv != CKR_OK) break; |
michael@0 | 624 | /* privKey was zero'd so public value is already set to NULL, 0 |
michael@0 | 625 | * if we don't set it explicitly */ |
michael@0 | 626 | } |
michael@0 | 627 | rv = DER_SetUInteger(privKey->arena, &privKey->u.ec.version, |
michael@0 | 628 | NSSLOWKEY_EC_PRIVATE_KEY_VERSION); |
michael@0 | 629 | if (rv != SECSuccess) crv = CKR_HOST_MEMORY; |
michael@0 | 630 | break; |
michael@0 | 631 | #endif /* NSS_DISABLE_ECC */ |
michael@0 | 632 | |
michael@0 | 633 | default: |
michael@0 | 634 | crv = CKR_KEY_TYPE_INCONSISTENT; |
michael@0 | 635 | break; |
michael@0 | 636 | } |
michael@0 | 637 | *crvp = crv; |
michael@0 | 638 | if (crv != CKR_OK) { |
michael@0 | 639 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 640 | return NULL; |
michael@0 | 641 | } |
michael@0 | 642 | return privKey; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | /* |
michael@0 | 646 | * check the consistancy and initialize a Private Key Object |
michael@0 | 647 | */ |
michael@0 | 648 | static CK_RV |
michael@0 | 649 | lg_createPrivateKeyObject(SDB *sdb, CK_KEY_TYPE key_type, |
michael@0 | 650 | CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 651 | { |
michael@0 | 652 | NSSLOWKEYPrivateKey *privKey; |
michael@0 | 653 | char *label; |
michael@0 | 654 | SECStatus rv = SECSuccess; |
michael@0 | 655 | CK_RV crv = CKR_DEVICE_ERROR; |
michael@0 | 656 | SECItem pubKey; |
michael@0 | 657 | NSSLOWKEYDBHandle *keyHandle = lg_getKeyDB(sdb); |
michael@0 | 658 | |
michael@0 | 659 | if (keyHandle == NULL) { |
michael@0 | 660 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | privKey=lg_mkPrivKey(sdb, templ,count,key_type,&crv); |
michael@0 | 664 | if (privKey == NULL) return crv; |
michael@0 | 665 | label = lg_getString(CKA_LABEL,templ,count); |
michael@0 | 666 | |
michael@0 | 667 | crv = lg_Attribute2SSecItem(NULL,CKA_NETSCAPE_DB,templ,count,&pubKey); |
michael@0 | 668 | if (crv != CKR_OK) { |
michael@0 | 669 | crv = CKR_TEMPLATE_INCOMPLETE; |
michael@0 | 670 | rv = SECFailure; |
michael@0 | 671 | goto fail; |
michael@0 | 672 | } |
michael@0 | 673 | #ifdef notdef |
michael@0 | 674 | if (keyHandle->version != 3) { |
michael@0 | 675 | unsigned char buf[SHA1_LENGTH]; |
michael@0 | 676 | SHA1_HashBuf(buf,pubKey.data,pubKey.len); |
michael@0 | 677 | PORT_Memcpy(pubKey.data,buf,sizeof(buf)); |
michael@0 | 678 | pubKey.len = sizeof(buf); |
michael@0 | 679 | } |
michael@0 | 680 | #endif |
michael@0 | 681 | /* get the key type */ |
michael@0 | 682 | if (key_type == CKK_RSA) { |
michael@0 | 683 | rv = RSA_PrivateKeyCheck(&privKey->u.rsa); |
michael@0 | 684 | if (rv == SECFailure) { |
michael@0 | 685 | goto fail; |
michael@0 | 686 | } |
michael@0 | 687 | } |
michael@0 | 688 | rv = nsslowkey_StoreKeyByPublicKey(keyHandle, privKey, &pubKey, |
michael@0 | 689 | label, sdb /*->password*/); |
michael@0 | 690 | |
michael@0 | 691 | fail: |
michael@0 | 692 | if (label) PORT_Free(label); |
michael@0 | 693 | *handle = lg_mkHandle(sdb,&pubKey,LG_TOKEN_TYPE_PRIV); |
michael@0 | 694 | if (pubKey.data) PORT_Free(pubKey.data); |
michael@0 | 695 | lg_nsslowkey_DestroyPrivateKey(privKey); |
michael@0 | 696 | if (rv != SECSuccess) return crv; |
michael@0 | 697 | |
michael@0 | 698 | return CKR_OK; |
michael@0 | 699 | } |
michael@0 | 700 | |
michael@0 | 701 | |
michael@0 | 702 | #define LG_KEY_MAX_RETRIES 10 /* don't hang if we are having problems with the rng */ |
michael@0 | 703 | #define LG_KEY_ID_SIZE 18 /* don't use either SHA1 or MD5 sizes */ |
michael@0 | 704 | /* |
michael@0 | 705 | * Secret keys must have a CKA_ID value to be stored in the database. This code |
michael@0 | 706 | * will generate one if there wasn't one already. |
michael@0 | 707 | */ |
michael@0 | 708 | static CK_RV |
michael@0 | 709 | lg_GenerateSecretCKA_ID(NSSLOWKEYDBHandle *handle, SECItem *id, char *label) |
michael@0 | 710 | { |
michael@0 | 711 | unsigned int retries; |
michael@0 | 712 | SECStatus rv = SECSuccess; |
michael@0 | 713 | CK_RV crv = CKR_OK; |
michael@0 | 714 | |
michael@0 | 715 | id->data = NULL; |
michael@0 | 716 | if (label) { |
michael@0 | 717 | id->data = (unsigned char *)PORT_Strdup(label); |
michael@0 | 718 | if (id->data == NULL) { |
michael@0 | 719 | return CKR_HOST_MEMORY; |
michael@0 | 720 | } |
michael@0 | 721 | id->len = PORT_Strlen(label)+1; |
michael@0 | 722 | if (!nsslowkey_KeyForIDExists(handle,id)) { |
michael@0 | 723 | return CKR_OK; |
michael@0 | 724 | } |
michael@0 | 725 | PORT_Free(id->data); |
michael@0 | 726 | id->data = NULL; |
michael@0 | 727 | id->len = 0; |
michael@0 | 728 | } |
michael@0 | 729 | id->data = (unsigned char *)PORT_Alloc(LG_KEY_ID_SIZE); |
michael@0 | 730 | if (id->data == NULL) { |
michael@0 | 731 | return CKR_HOST_MEMORY; |
michael@0 | 732 | } |
michael@0 | 733 | id->len = LG_KEY_ID_SIZE; |
michael@0 | 734 | |
michael@0 | 735 | retries = 0; |
michael@0 | 736 | do { |
michael@0 | 737 | rv = RNG_GenerateGlobalRandomBytes(id->data,id->len); |
michael@0 | 738 | } while (rv == SECSuccess && nsslowkey_KeyForIDExists(handle,id) && |
michael@0 | 739 | (++retries <= LG_KEY_MAX_RETRIES)); |
michael@0 | 740 | |
michael@0 | 741 | if ((rv != SECSuccess) || (retries > LG_KEY_MAX_RETRIES)) { |
michael@0 | 742 | crv = CKR_DEVICE_ERROR; /* random number generator is bad */ |
michael@0 | 743 | PORT_Free(id->data); |
michael@0 | 744 | id->data = NULL; |
michael@0 | 745 | id->len = 0; |
michael@0 | 746 | } |
michael@0 | 747 | return crv; |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | |
michael@0 | 751 | static NSSLOWKEYPrivateKey *lg_mkSecretKeyRep(const CK_ATTRIBUTE *templ, |
michael@0 | 752 | CK_ULONG count, CK_KEY_TYPE key_type, |
michael@0 | 753 | SECItem *pubkey, SDB *sdbpw) |
michael@0 | 754 | { |
michael@0 | 755 | NSSLOWKEYPrivateKey *privKey = 0; |
michael@0 | 756 | PLArenaPool *arena = 0; |
michael@0 | 757 | CK_KEY_TYPE keyType; |
michael@0 | 758 | PRUint32 keyTypeStorage; |
michael@0 | 759 | SECItem keyTypeItem; |
michael@0 | 760 | CK_RV crv; |
michael@0 | 761 | SECStatus rv; |
michael@0 | 762 | static unsigned char derZero[1] = { 0 }; |
michael@0 | 763 | |
michael@0 | 764 | arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
michael@0 | 765 | if (arena == NULL) { crv = CKR_HOST_MEMORY; goto loser; } |
michael@0 | 766 | |
michael@0 | 767 | privKey = (NSSLOWKEYPrivateKey *) |
michael@0 | 768 | PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey)); |
michael@0 | 769 | if (privKey == NULL) { crv = CKR_HOST_MEMORY; goto loser; } |
michael@0 | 770 | |
michael@0 | 771 | privKey->arena = arena; |
michael@0 | 772 | |
michael@0 | 773 | /* Secret keys are represented in the database as "fake" RSA keys. |
michael@0 | 774 | * The RSA key is marked as a secret key representation by setting the |
michael@0 | 775 | * public exponent field to 0, which is an invalid RSA exponent. |
michael@0 | 776 | * The other fields are set as follows: |
michael@0 | 777 | * modulus - CKA_ID value for the secret key |
michael@0 | 778 | * private exponent - CKA_VALUE (the key itself) |
michael@0 | 779 | * coefficient - CKA_KEY_TYPE, which indicates what encryption algorithm |
michael@0 | 780 | * is used for the key. |
michael@0 | 781 | * all others - set to integer 0 |
michael@0 | 782 | */ |
michael@0 | 783 | privKey->keyType = NSSLOWKEYRSAKey; |
michael@0 | 784 | |
michael@0 | 785 | /* The modulus is set to the key id of the symmetric key */ |
michael@0 | 786 | privKey->u.rsa.modulus.data = |
michael@0 | 787 | (unsigned char *) PORT_ArenaAlloc(arena, pubkey->len); |
michael@0 | 788 | if (privKey->u.rsa.modulus.data == NULL) { |
michael@0 | 789 | crv = CKR_HOST_MEMORY; |
michael@0 | 790 | goto loser; |
michael@0 | 791 | } |
michael@0 | 792 | privKey->u.rsa.modulus.len = pubkey->len; |
michael@0 | 793 | PORT_Memcpy(privKey->u.rsa.modulus.data, pubkey->data, pubkey->len); |
michael@0 | 794 | |
michael@0 | 795 | /* The public exponent is set to 0 to indicate a special key */ |
michael@0 | 796 | privKey->u.rsa.publicExponent.len = sizeof derZero; |
michael@0 | 797 | privKey->u.rsa.publicExponent.data = derZero; |
michael@0 | 798 | |
michael@0 | 799 | /* The private exponent is the actual key value */ |
michael@0 | 800 | crv = lg_PrivAttr2SecItem(arena, CKA_VALUE, templ, count, |
michael@0 | 801 | &privKey->u.rsa.privateExponent, sdbpw); |
michael@0 | 802 | if (crv != CKR_OK) goto loser; |
michael@0 | 803 | |
michael@0 | 804 | /* All other fields empty - needs testing */ |
michael@0 | 805 | privKey->u.rsa.prime1.len = sizeof derZero; |
michael@0 | 806 | privKey->u.rsa.prime1.data = derZero; |
michael@0 | 807 | |
michael@0 | 808 | privKey->u.rsa.prime2.len = sizeof derZero; |
michael@0 | 809 | privKey->u.rsa.prime2.data = derZero; |
michael@0 | 810 | |
michael@0 | 811 | privKey->u.rsa.exponent1.len = sizeof derZero; |
michael@0 | 812 | privKey->u.rsa.exponent1.data = derZero; |
michael@0 | 813 | |
michael@0 | 814 | privKey->u.rsa.exponent2.len = sizeof derZero; |
michael@0 | 815 | privKey->u.rsa.exponent2.data = derZero; |
michael@0 | 816 | |
michael@0 | 817 | /* Coeficient set to KEY_TYPE */ |
michael@0 | 818 | crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &keyType); |
michael@0 | 819 | if (crv != CKR_OK) goto loser; |
michael@0 | 820 | /* on 64 bit platforms, we still want to store 32 bits of keyType (This is |
michael@0 | 821 | * safe since the PKCS #11 defines for all types are 32 bits or less). */ |
michael@0 | 822 | keyTypeStorage = (PRUint32) keyType; |
michael@0 | 823 | keyTypeStorage = PR_htonl(keyTypeStorage); |
michael@0 | 824 | keyTypeItem.data = (unsigned char *)&keyTypeStorage; |
michael@0 | 825 | keyTypeItem.len = sizeof (keyTypeStorage); |
michael@0 | 826 | rv = SECITEM_CopyItem(arena, &privKey->u.rsa.coefficient, &keyTypeItem); |
michael@0 | 827 | if (rv != SECSuccess) { |
michael@0 | 828 | crv = CKR_HOST_MEMORY; |
michael@0 | 829 | goto loser; |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | /* Private key version field set normally for compatibility */ |
michael@0 | 833 | rv = DER_SetUInteger(privKey->arena, |
michael@0 | 834 | &privKey->u.rsa.version, NSSLOWKEY_VERSION); |
michael@0 | 835 | if (rv != SECSuccess) { crv = CKR_HOST_MEMORY; goto loser; } |
michael@0 | 836 | |
michael@0 | 837 | loser: |
michael@0 | 838 | if (crv != CKR_OK) { |
michael@0 | 839 | PORT_FreeArena(arena,PR_FALSE); |
michael@0 | 840 | privKey = 0; |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | return privKey; |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | /* |
michael@0 | 847 | * check the consistancy and initialize a Secret Key Object |
michael@0 | 848 | */ |
michael@0 | 849 | static CK_RV |
michael@0 | 850 | lg_createSecretKeyObject(SDB *sdb, CK_KEY_TYPE key_type, |
michael@0 | 851 | CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 852 | { |
michael@0 | 853 | CK_RV crv; |
michael@0 | 854 | NSSLOWKEYPrivateKey *privKey = NULL; |
michael@0 | 855 | NSSLOWKEYDBHandle *keyHandle = NULL; |
michael@0 | 856 | SECItem pubKey; |
michael@0 | 857 | char *label = NULL; |
michael@0 | 858 | SECStatus rv = SECSuccess; |
michael@0 | 859 | |
michael@0 | 860 | pubKey.data = 0; |
michael@0 | 861 | |
michael@0 | 862 | /* If the object is a TOKEN object, store in the database */ |
michael@0 | 863 | keyHandle = lg_getKeyDB(sdb); |
michael@0 | 864 | |
michael@0 | 865 | if (keyHandle == NULL) { |
michael@0 | 866 | return CKR_TOKEN_WRITE_PROTECTED; |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | label = lg_getString(CKA_LABEL,templ,count); |
michael@0 | 870 | |
michael@0 | 871 | crv = lg_Attribute2SecItem(NULL,CKA_ID,templ,count,&pubKey); |
michael@0 | 872 | /* Should this be ID? */ |
michael@0 | 873 | if (crv != CKR_OK) goto loser; |
michael@0 | 874 | |
michael@0 | 875 | /* if we don't have an ID, generate one */ |
michael@0 | 876 | if (pubKey.len == 0) { |
michael@0 | 877 | if (pubKey.data) { |
michael@0 | 878 | PORT_Free(pubKey.data); |
michael@0 | 879 | pubKey.data = NULL; |
michael@0 | 880 | } |
michael@0 | 881 | crv = lg_GenerateSecretCKA_ID(keyHandle, &pubKey, label); |
michael@0 | 882 | if (crv != CKR_OK) goto loser; |
michael@0 | 883 | } |
michael@0 | 884 | |
michael@0 | 885 | privKey = lg_mkSecretKeyRep(templ, count, key_type, &pubKey, sdb); |
michael@0 | 886 | if (privKey == NULL) { |
michael@0 | 887 | crv = CKR_HOST_MEMORY; |
michael@0 | 888 | goto loser; |
michael@0 | 889 | } |
michael@0 | 890 | |
michael@0 | 891 | rv = nsslowkey_StoreKeyByPublicKey(keyHandle, |
michael@0 | 892 | privKey, &pubKey, label, sdb /*->password*/); |
michael@0 | 893 | if (rv != SECSuccess) { |
michael@0 | 894 | crv = CKR_DEVICE_ERROR; |
michael@0 | 895 | goto loser; |
michael@0 | 896 | } |
michael@0 | 897 | |
michael@0 | 898 | *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_KEY); |
michael@0 | 899 | |
michael@0 | 900 | loser: |
michael@0 | 901 | if (label) PORT_Free(label); |
michael@0 | 902 | if (privKey) lg_nsslowkey_DestroyPrivateKey(privKey); |
michael@0 | 903 | if (pubKey.data) PORT_Free(pubKey.data); |
michael@0 | 904 | |
michael@0 | 905 | return crv; |
michael@0 | 906 | } |
michael@0 | 907 | |
michael@0 | 908 | /* |
michael@0 | 909 | * check the consistancy and initialize a Key Object |
michael@0 | 910 | */ |
michael@0 | 911 | static CK_RV |
michael@0 | 912 | lg_createKeyObject(SDB *sdb, CK_OBJECT_CLASS objclass, |
michael@0 | 913 | CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 914 | { |
michael@0 | 915 | CK_RV crv; |
michael@0 | 916 | CK_KEY_TYPE key_type; |
michael@0 | 917 | |
michael@0 | 918 | /* get the key type */ |
michael@0 | 919 | crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &key_type); |
michael@0 | 920 | if (crv != CKR_OK) { |
michael@0 | 921 | return crv; |
michael@0 | 922 | } |
michael@0 | 923 | |
michael@0 | 924 | switch (objclass) { |
michael@0 | 925 | case CKO_PUBLIC_KEY: |
michael@0 | 926 | return lg_createPublicKeyObject(sdb,key_type,handle,templ,count); |
michael@0 | 927 | case CKO_PRIVATE_KEY: |
michael@0 | 928 | return lg_createPrivateKeyObject(sdb,key_type,handle,templ,count); |
michael@0 | 929 | case CKO_SECRET_KEY: |
michael@0 | 930 | return lg_createSecretKeyObject(sdb,key_type,handle,templ,count); |
michael@0 | 931 | default: |
michael@0 | 932 | break; |
michael@0 | 933 | } |
michael@0 | 934 | return CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 935 | } |
michael@0 | 936 | |
michael@0 | 937 | /* |
michael@0 | 938 | * Parse the template and create an object stored in the DB that reflects. |
michael@0 | 939 | * the object specified in the database. |
michael@0 | 940 | */ |
michael@0 | 941 | CK_RV |
michael@0 | 942 | lg_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *handle, |
michael@0 | 943 | const CK_ATTRIBUTE *templ, CK_ULONG count) |
michael@0 | 944 | { |
michael@0 | 945 | CK_RV crv; |
michael@0 | 946 | CK_OBJECT_CLASS objclass; |
michael@0 | 947 | |
michael@0 | 948 | /* get the object class */ |
michael@0 | 949 | crv = lg_GetULongAttribute(CKA_CLASS, templ, count, &objclass); |
michael@0 | 950 | if (crv != CKR_OK) { |
michael@0 | 951 | return crv; |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | /* Now handle the specific object class. |
michael@0 | 955 | */ |
michael@0 | 956 | switch (objclass) { |
michael@0 | 957 | case CKO_CERTIFICATE: |
michael@0 | 958 | crv = lg_createCertObject(sdb,handle,templ,count); |
michael@0 | 959 | break; |
michael@0 | 960 | case CKO_NSS_TRUST: |
michael@0 | 961 | crv = lg_createTrustObject(sdb,handle,templ,count); |
michael@0 | 962 | break; |
michael@0 | 963 | case CKO_NSS_CRL: |
michael@0 | 964 | crv = lg_createCrlObject(sdb,handle,templ,count); |
michael@0 | 965 | break; |
michael@0 | 966 | case CKO_NSS_SMIME: |
michael@0 | 967 | crv = lg_createSMimeObject(sdb,handle,templ,count); |
michael@0 | 968 | break; |
michael@0 | 969 | case CKO_PRIVATE_KEY: |
michael@0 | 970 | case CKO_PUBLIC_KEY: |
michael@0 | 971 | case CKO_SECRET_KEY: |
michael@0 | 972 | crv = lg_createKeyObject(sdb,objclass,handle,templ,count); |
michael@0 | 973 | break; |
michael@0 | 974 | default: |
michael@0 | 975 | crv = CKR_ATTRIBUTE_VALUE_INVALID; |
michael@0 | 976 | break; |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | return crv; |
michael@0 | 980 | } |
michael@0 | 981 |