| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/child/webcrypto/nss/rsa_hashed_algorithm_nss.h" | |
| 6 | |
| 7 #include <secasn1.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "content/child/webcrypto/crypto_data.h" | |
| 11 #include "content/child/webcrypto/generate_key_result.h" | |
| 12 #include "content/child/webcrypto/jwk.h" | |
| 13 #include "content/child/webcrypto/nss/key_nss.h" | |
| 14 #include "content/child/webcrypto/nss/util_nss.h" | |
| 15 #include "content/child/webcrypto/status.h" | |
| 16 #include "content/child/webcrypto/webcrypto_util.h" | |
| 17 #include "crypto/scoped_nss_types.h" | |
| 18 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" | |
| 19 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" | |
| 20 | |
| 21 namespace content { | |
| 22 | |
| 23 namespace webcrypto { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 #if defined(USE_NSS) && !defined(OS_CHROMEOS) | |
| 28 Status ErrorRsaPrivateKeyImportNotSupported() { | |
| 29 return Status::ErrorUnsupported( | |
| 30 "NSS version must be at least 3.16.2 for RSA private key import. See " | |
| 31 "http://crbug.com/380424"); | |
| 32 } | |
| 33 | |
| 34 // Prior to NSS 3.16.2 RSA key parameters were not validated. This is | |
| 35 // a security problem for RSA private key import from JWK which uses a | |
| 36 // CKA_ID based on the public modulus to retrieve the private key. | |
| 37 Status NssSupportsRsaPrivateKeyImport() { | |
| 38 if (!NSS_VersionCheck("3.16.2")) | |
| 39 return ErrorRsaPrivateKeyImportNotSupported(); | |
| 40 | |
| 41 // Also ensure that the version of Softoken is 3.16.2 or later. | |
| 42 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
| 43 CK_SLOT_INFO info = {}; | |
| 44 if (PK11_GetSlotInfo(slot.get(), &info) != SECSuccess) | |
| 45 return ErrorRsaPrivateKeyImportNotSupported(); | |
| 46 | |
| 47 // CK_SLOT_INFO.hardwareVersion contains the major.minor | |
| 48 // version info for Softoken in the corresponding .major/.minor | |
| 49 // fields, and .firmwareVersion contains the patch.build | |
| 50 // version info (in the .major/.minor fields) | |
| 51 if ((info.hardwareVersion.major > 3) || | |
| 52 (info.hardwareVersion.major == 3 && | |
| 53 (info.hardwareVersion.minor > 16 || | |
| 54 (info.hardwareVersion.minor == 16 && | |
| 55 info.firmwareVersion.major >= 2)))) { | |
| 56 return Status::Success(); | |
| 57 } | |
| 58 | |
| 59 return ErrorRsaPrivateKeyImportNotSupported(); | |
| 60 } | |
| 61 #else | |
| 62 Status NssSupportsRsaPrivateKeyImport() { | |
| 63 return Status::Success(); | |
| 64 } | |
| 65 #endif | |
| 66 | |
| 67 bool CreateRsaHashedPublicKeyAlgorithm( | |
| 68 blink::WebCryptoAlgorithmId rsa_algorithm, | |
| 69 blink::WebCryptoAlgorithmId hash_algorithm, | |
| 70 SECKEYPublicKey* key, | |
| 71 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 72 // TODO(eroman): What about other key types rsaPss, rsaOaep. | |
| 73 if (!key || key->keyType != rsaKey) | |
| 74 return false; | |
| 75 | |
| 76 unsigned int modulus_length_bits = SECKEY_PublicKeyStrength(key) * 8; | |
| 77 CryptoData public_exponent(key->u.rsa.publicExponent.data, | |
| 78 key->u.rsa.publicExponent.len); | |
| 79 | |
| 80 *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed( | |
| 81 rsa_algorithm, modulus_length_bits, public_exponent.bytes(), | |
| 82 public_exponent.byte_length(), hash_algorithm); | |
| 83 return true; | |
| 84 } | |
| 85 | |
| 86 bool CreateRsaHashedPrivateKeyAlgorithm( | |
| 87 blink::WebCryptoAlgorithmId rsa_algorithm, | |
| 88 blink::WebCryptoAlgorithmId hash_algorithm, | |
| 89 SECKEYPrivateKey* key, | |
| 90 blink::WebCryptoKeyAlgorithm* key_algorithm) { | |
| 91 crypto::ScopedSECKEYPublicKey public_key(SECKEY_ConvertToPublicKey(key)); | |
| 92 if (!public_key) | |
| 93 return false; | |
| 94 return CreateRsaHashedPublicKeyAlgorithm(rsa_algorithm, hash_algorithm, | |
| 95 public_key.get(), key_algorithm); | |
| 96 } | |
| 97 | |
| 98 // From PKCS#1 [http://tools.ietf.org/html/rfc3447]: | |
| 99 // | |
| 100 // RSAPrivateKey ::= SEQUENCE { | |
| 101 // version Version, | |
| 102 // modulus INTEGER, -- n | |
| 103 // publicExponent INTEGER, -- e | |
| 104 // privateExponent INTEGER, -- d | |
| 105 // prime1 INTEGER, -- p | |
| 106 // prime2 INTEGER, -- q | |
| 107 // exponent1 INTEGER, -- d mod (p-1) | |
| 108 // exponent2 INTEGER, -- d mod (q-1) | |
| 109 // coefficient INTEGER, -- (inverse of q) mod p | |
| 110 // otherPrimeInfos OtherPrimeInfos OPTIONAL | |
| 111 // } | |
| 112 // | |
| 113 // Note that otherPrimeInfos is only applicable for version=1. Since NSS | |
| 114 // doesn't use multi-prime can safely use version=0. | |
| 115 struct RSAPrivateKey { | |
| 116 SECItem version; | |
| 117 SECItem modulus; | |
| 118 SECItem public_exponent; | |
| 119 SECItem private_exponent; | |
| 120 SECItem prime1; | |
| 121 SECItem prime2; | |
| 122 SECItem exponent1; | |
| 123 SECItem exponent2; | |
| 124 SECItem coefficient; | |
| 125 }; | |
| 126 | |
| 127 // The system NSS library doesn't have the new PK11_ExportDERPrivateKeyInfo | |
| 128 // function yet (https://bugzilla.mozilla.org/show_bug.cgi?id=519255). So we | |
| 129 // provide a fallback implementation. | |
| 130 #if defined(USE_NSS) | |
| 131 const SEC_ASN1Template RSAPrivateKeyTemplate[] = { | |
| 132 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey)}, | |
| 133 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, version)}, | |
| 134 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, modulus)}, | |
| 135 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, public_exponent)}, | |
| 136 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, private_exponent)}, | |
| 137 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime1)}, | |
| 138 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, prime2)}, | |
| 139 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent1)}, | |
| 140 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, exponent2)}, | |
| 141 {SEC_ASN1_INTEGER, offsetof(RSAPrivateKey, coefficient)}, | |
| 142 {0}}; | |
| 143 #endif // defined(USE_NSS) | |
| 144 | |
| 145 // On success |value| will be filled with data which must be freed by | |
| 146 // SECITEM_FreeItem(value, PR_FALSE); | |
| 147 bool ReadUint(SECKEYPrivateKey* key, | |
| 148 CK_ATTRIBUTE_TYPE attribute, | |
| 149 SECItem* value) { | |
| 150 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, attribute, value); | |
| 151 | |
| 152 // PK11_ReadRawAttribute() returns items of type siBuffer. However in order | |
| 153 // for the ASN.1 encoding to be correct, the items must be of type | |
| 154 // siUnsignedInteger. | |
| 155 value->type = siUnsignedInteger; | |
| 156 | |
| 157 return rv == SECSuccess; | |
| 158 } | |
| 159 | |
| 160 // Fills |out| with the RSA private key properties. Returns true on success. | |
| 161 // Regardless of the return value, the caller must invoke FreeRSAPrivateKey() | |
| 162 // to free up any allocated memory. | |
| 163 // | |
| 164 // The passed in RSAPrivateKey must be zero-initialized. | |
| 165 bool InitRSAPrivateKey(SECKEYPrivateKey* key, RSAPrivateKey* out) { | |
| 166 if (key->keyType != rsaKey) | |
| 167 return false; | |
| 168 | |
| 169 // Everything should be zero-ed out. These are just some spot checks. | |
| 170 DCHECK(!out->version.data); | |
| 171 DCHECK(!out->version.len); | |
| 172 DCHECK(!out->modulus.data); | |
| 173 DCHECK(!out->modulus.len); | |
| 174 | |
| 175 // Always use version=0 since not using multi-prime. | |
| 176 if (!SEC_ASN1EncodeInteger(NULL, &out->version, 0)) | |
| 177 return false; | |
| 178 | |
| 179 if (!ReadUint(key, CKA_MODULUS, &out->modulus)) | |
| 180 return false; | |
| 181 if (!ReadUint(key, CKA_PUBLIC_EXPONENT, &out->public_exponent)) | |
| 182 return false; | |
| 183 if (!ReadUint(key, CKA_PRIVATE_EXPONENT, &out->private_exponent)) | |
| 184 return false; | |
| 185 if (!ReadUint(key, CKA_PRIME_1, &out->prime1)) | |
| 186 return false; | |
| 187 if (!ReadUint(key, CKA_PRIME_2, &out->prime2)) | |
| 188 return false; | |
| 189 if (!ReadUint(key, CKA_EXPONENT_1, &out->exponent1)) | |
| 190 return false; | |
| 191 if (!ReadUint(key, CKA_EXPONENT_2, &out->exponent2)) | |
| 192 return false; | |
| 193 if (!ReadUint(key, CKA_COEFFICIENT, &out->coefficient)) | |
| 194 return false; | |
| 195 | |
| 196 return true; | |
| 197 } | |
| 198 | |
| 199 struct FreeRsaPrivateKey { | |
| 200 void operator()(RSAPrivateKey* out) { | |
| 201 SECITEM_FreeItem(&out->version, PR_FALSE); | |
| 202 SECITEM_FreeItem(&out->modulus, PR_FALSE); | |
| 203 SECITEM_FreeItem(&out->public_exponent, PR_FALSE); | |
| 204 SECITEM_FreeItem(&out->private_exponent, PR_FALSE); | |
| 205 SECITEM_FreeItem(&out->prime1, PR_FALSE); | |
| 206 SECITEM_FreeItem(&out->prime2, PR_FALSE); | |
| 207 SECITEM_FreeItem(&out->exponent1, PR_FALSE); | |
| 208 SECITEM_FreeItem(&out->exponent2, PR_FALSE); | |
| 209 SECITEM_FreeItem(&out->coefficient, PR_FALSE); | |
| 210 } | |
| 211 }; | |
| 212 | |
| 213 typedef scoped_ptr<CERTSubjectPublicKeyInfo, | |
| 214 crypto::NSSDestroyer<CERTSubjectPublicKeyInfo, | |
| 215 SECKEY_DestroySubjectPublicKeyInfo>> | |
| 216 ScopedCERTSubjectPublicKeyInfo; | |
| 217 | |
| 218 struct DestroyGenericObject { | |
| 219 void operator()(PK11GenericObject* o) const { | |
| 220 if (o) | |
| 221 PK11_DestroyGenericObject(o); | |
| 222 } | |
| 223 }; | |
| 224 | |
| 225 typedef scoped_ptr<PK11GenericObject, DestroyGenericObject> | |
| 226 ScopedPK11GenericObject; | |
| 227 | |
| 228 // Helper to add an attribute to a template. | |
| 229 void AddAttribute(CK_ATTRIBUTE_TYPE type, | |
| 230 void* value, | |
| 231 unsigned long length, | |
| 232 std::vector<CK_ATTRIBUTE>* templ) { | |
| 233 CK_ATTRIBUTE attribute = {type, value, length}; | |
| 234 templ->push_back(attribute); | |
| 235 } | |
| 236 | |
| 237 void AddAttribute(CK_ATTRIBUTE_TYPE type, | |
| 238 const CryptoData& data, | |
| 239 std::vector<CK_ATTRIBUTE>* templ) { | |
| 240 CK_ATTRIBUTE attribute = { | |
| 241 type, const_cast<unsigned char*>(data.bytes()), data.byte_length()}; | |
| 242 templ->push_back(attribute); | |
| 243 } | |
| 244 | |
| 245 void AddAttribute(CK_ATTRIBUTE_TYPE type, | |
| 246 const std::string& data, | |
| 247 std::vector<CK_ATTRIBUTE>* templ) { | |
| 248 AddAttribute(type, CryptoData(data), templ); | |
| 249 } | |
| 250 | |
| 251 Status ExportKeyPkcs8Nss(SECKEYPrivateKey* key, std::vector<uint8_t>* buffer) { | |
| 252 if (key->keyType != rsaKey) | |
| 253 return Status::ErrorUnsupported(); | |
| 254 | |
| 255 // TODO(rsleevi): Implement OAEP support according to the spec. | |
| 256 | |
| 257 #if defined(USE_NSS) | |
| 258 // PK11_ExportDERPrivateKeyInfo isn't available. Use our fallback code. | |
| 259 const SECOidTag algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; | |
| 260 const int kPrivateKeyInfoVersion = 0; | |
| 261 | |
| 262 SECKEYPrivateKeyInfo private_key_info = {}; | |
| 263 RSAPrivateKey rsa_private_key = {}; | |
| 264 scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key( | |
| 265 &rsa_private_key); | |
| 266 | |
| 267 // http://crbug.com/366427: the spec does not define any other failures for | |
| 268 // exporting, so none of the subsequent errors are spec compliant. | |
| 269 if (!InitRSAPrivateKey(key, &rsa_private_key)) | |
| 270 return Status::OperationError(); | |
| 271 | |
| 272 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
| 273 if (!arena.get()) | |
| 274 return Status::OperationError(); | |
| 275 | |
| 276 if (!SEC_ASN1EncodeItem(arena.get(), &private_key_info.privateKey, | |
| 277 &rsa_private_key, RSAPrivateKeyTemplate)) { | |
| 278 return Status::OperationError(); | |
| 279 } | |
| 280 | |
| 281 if (SECSuccess != SECOID_SetAlgorithmID(arena.get(), | |
| 282 &private_key_info.algorithm, | |
| 283 algorithm, NULL)) { | |
| 284 return Status::OperationError(); | |
| 285 } | |
| 286 | |
| 287 if (!SEC_ASN1EncodeInteger(arena.get(), &private_key_info.version, | |
| 288 kPrivateKeyInfoVersion)) { | |
| 289 return Status::OperationError(); | |
| 290 } | |
| 291 | |
| 292 crypto::ScopedSECItem encoded_key( | |
| 293 SEC_ASN1EncodeItem(NULL, NULL, &private_key_info, | |
| 294 SEC_ASN1_GET(SECKEY_PrivateKeyInfoTemplate))); | |
| 295 #else // defined(USE_NSS) | |
| 296 crypto::ScopedSECItem encoded_key(PK11_ExportDERPrivateKeyInfo(key, NULL)); | |
| 297 #endif // defined(USE_NSS) | |
| 298 | |
| 299 if (!encoded_key.get()) | |
| 300 return Status::OperationError(); | |
| 301 | |
| 302 buffer->assign(encoded_key->data, encoded_key->data + encoded_key->len); | |
| 303 return Status::Success(); | |
| 304 } | |
| 305 | |
| 306 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, | |
| 307 bool extractable, | |
| 308 blink::WebCryptoKeyUsageMask usages, | |
| 309 const JwkRsaInfo& params, | |
| 310 blink::WebCryptoKey* key) { | |
| 311 Status status = NssSupportsRsaPrivateKeyImport(); | |
| 312 if (status.IsError()) | |
| 313 return status; | |
| 314 | |
| 315 CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; | |
| 316 CK_KEY_TYPE key_type = CKK_RSA; | |
| 317 CK_BBOOL ck_false = CK_FALSE; | |
| 318 | |
| 319 std::vector<CK_ATTRIBUTE> key_template; | |
| 320 | |
| 321 AddAttribute(CKA_CLASS, &obj_class, sizeof(obj_class), &key_template); | |
| 322 AddAttribute(CKA_KEY_TYPE, &key_type, sizeof(key_type), &key_template); | |
| 323 AddAttribute(CKA_TOKEN, &ck_false, sizeof(ck_false), &key_template); | |
| 324 AddAttribute(CKA_SENSITIVE, &ck_false, sizeof(ck_false), &key_template); | |
| 325 AddAttribute(CKA_PRIVATE, &ck_false, sizeof(ck_false), &key_template); | |
| 326 | |
| 327 // Required properties by JWA. | |
| 328 AddAttribute(CKA_MODULUS, params.n, &key_template); | |
| 329 AddAttribute(CKA_PUBLIC_EXPONENT, params.e, &key_template); | |
| 330 AddAttribute(CKA_PRIVATE_EXPONENT, params.d, &key_template); | |
| 331 | |
| 332 // Manufacture a CKA_ID so the created key can be retrieved later as a | |
| 333 // SECKEYPrivateKey using FindKeyByKeyID(). Unfortunately there isn't a more | |
| 334 // direct way to do this in NSS. | |
| 335 // | |
| 336 // For consistency with other NSS key creation methods, set the CKA_ID to | |
| 337 // PK11_MakeIDFromPubKey(). There are some problems with | |
| 338 // this approach: | |
| 339 // | |
| 340 // (1) Prior to NSS 3.16.2, there is no parameter validation when creating | |
| 341 // private keys. It is therefore possible to construct a key using the | |
| 342 // known public modulus, and where all the other parameters are bogus. | |
| 343 // FindKeyByKeyID() returns the first key matching the ID. So this would | |
| 344 // effectively allow an attacker to retrieve a private key of their | |
| 345 // choice. | |
| 346 // | |
| 347 // (2) The ID space is shared by different key types. So theoretically | |
| 348 // possible to retrieve a key of the wrong type which has a matching | |
| 349 // CKA_ID. In practice I am told this is not likely except for small key | |
| 350 // sizes, since would require constructing keys with the same public | |
| 351 // data. | |
| 352 // | |
| 353 // (3) FindKeyByKeyID() doesn't necessarily return the object that was just | |
| 354 // created by CreateGenericObject. If the pre-existing key was | |
| 355 // provisioned with flags incompatible with WebCrypto (for instance | |
| 356 // marked sensitive) then this will break things. | |
| 357 SECItem modulus_item = MakeSECItemForBuffer(CryptoData(params.n)); | |
| 358 crypto::ScopedSECItem object_id(PK11_MakeIDFromPubKey(&modulus_item)); | |
| 359 AddAttribute(CKA_ID, CryptoData(object_id->data, object_id->len), | |
| 360 &key_template); | |
| 361 | |
| 362 // Optional properties by JWA, however guaranteed to be present by Chromium's | |
| 363 // implementation. | |
| 364 AddAttribute(CKA_PRIME_1, params.p, &key_template); | |
| 365 AddAttribute(CKA_PRIME_2, params.q, &key_template); | |
| 366 AddAttribute(CKA_EXPONENT_1, params.dp, &key_template); | |
| 367 AddAttribute(CKA_EXPONENT_2, params.dq, &key_template); | |
| 368 AddAttribute(CKA_COEFFICIENT, params.qi, &key_template); | |
| 369 | |
| 370 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
| 371 | |
| 372 ScopedPK11GenericObject key_object(PK11_CreateGenericObject( | |
| 373 slot.get(), &key_template[0], key_template.size(), PR_FALSE)); | |
| 374 | |
| 375 if (!key_object) | |
| 376 return Status::OperationError(); | |
| 377 | |
| 378 crypto::ScopedSECKEYPrivateKey private_key_tmp( | |
| 379 PK11_FindKeyByKeyID(slot.get(), object_id.get(), NULL)); | |
| 380 | |
| 381 // PK11_FindKeyByKeyID() may return a handle to an existing key, rather than | |
| 382 // the object created by PK11_CreateGenericObject(). | |
| 383 crypto::ScopedSECKEYPrivateKey private_key( | |
| 384 SECKEY_CopyPrivateKey(private_key_tmp.get())); | |
| 385 | |
| 386 if (!private_key) | |
| 387 return Status::OperationError(); | |
| 388 | |
| 389 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 390 if (!CreateRsaHashedPrivateKeyAlgorithm( | |
| 391 algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), | |
| 392 private_key.get(), &key_algorithm)) { | |
| 393 return Status::ErrorUnexpected(); | |
| 394 } | |
| 395 | |
| 396 std::vector<uint8_t> pkcs8_data; | |
| 397 status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); | |
| 398 if (status.IsError()) | |
| 399 return status; | |
| 400 | |
| 401 scoped_ptr<PrivateKeyNss> key_handle( | |
| 402 new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); | |
| 403 | |
| 404 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 405 blink::WebCryptoKeyTypePrivate, | |
| 406 extractable, key_algorithm, usages); | |
| 407 return Status::Success(); | |
| 408 } | |
| 409 | |
| 410 Status ExportKeySpkiNss(SECKEYPublicKey* key, std::vector<uint8_t>* buffer) { | |
| 411 const crypto::ScopedSECItem spki_der( | |
| 412 SECKEY_EncodeDERSubjectPublicKeyInfo(key)); | |
| 413 if (!spki_der) | |
| 414 return Status::OperationError(); | |
| 415 | |
| 416 buffer->assign(spki_der->data, spki_der->data + spki_der->len); | |
| 417 return Status::Success(); | |
| 418 } | |
| 419 | |
| 420 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, | |
| 421 bool extractable, | |
| 422 blink::WebCryptoKeyUsageMask usages, | |
| 423 const CryptoData& modulus_data, | |
| 424 const CryptoData& exponent_data, | |
| 425 blink::WebCryptoKey* key) { | |
| 426 if (!modulus_data.byte_length()) | |
| 427 return Status::ErrorImportRsaEmptyModulus(); | |
| 428 | |
| 429 if (!exponent_data.byte_length()) | |
| 430 return Status::ErrorImportRsaEmptyExponent(); | |
| 431 | |
| 432 DCHECK(modulus_data.bytes()); | |
| 433 DCHECK(exponent_data.bytes()); | |
| 434 | |
| 435 // NSS does not provide a way to create an RSA public key directly from the | |
| 436 // modulus and exponent values, but it can import an DER-encoded ASN.1 blob | |
| 437 // with these values and create the public key from that. The code below | |
| 438 // follows the recommendation described in | |
| 439 // https://developer.mozilla.org/en-US/docs/NSS/NSS_Tech_Notes/nss_tech_note7 | |
| 440 | |
| 441 // Pack the input values into a struct compatible with NSS ASN.1 encoding, and | |
| 442 // set up an ASN.1 encoder template for it. | |
| 443 struct RsaPublicKeyData { | |
| 444 SECItem modulus; | |
| 445 SECItem exponent; | |
| 446 }; | |
| 447 const RsaPublicKeyData pubkey_in = { | |
| 448 {siUnsignedInteger, | |
| 449 const_cast<unsigned char*>(modulus_data.bytes()), | |
| 450 modulus_data.byte_length()}, | |
| 451 {siUnsignedInteger, | |
| 452 const_cast<unsigned char*>(exponent_data.bytes()), | |
| 453 exponent_data.byte_length()}}; | |
| 454 const SEC_ASN1Template rsa_public_key_template[] = { | |
| 455 {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RsaPublicKeyData)}, | |
| 456 { | |
| 457 SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, modulus), | |
| 458 }, | |
| 459 { | |
| 460 SEC_ASN1_INTEGER, offsetof(RsaPublicKeyData, exponent), | |
| 461 }, | |
| 462 { | |
| 463 0, }}; | |
| 464 | |
| 465 // DER-encode the public key. | |
| 466 crypto::ScopedSECItem pubkey_der( | |
| 467 SEC_ASN1EncodeItem(NULL, NULL, &pubkey_in, rsa_public_key_template)); | |
| 468 if (!pubkey_der) | |
| 469 return Status::OperationError(); | |
| 470 | |
| 471 // Import the DER-encoded public key to create an RSA SECKEYPublicKey. | |
| 472 crypto::ScopedSECKEYPublicKey pubkey( | |
| 473 SECKEY_ImportDERPublicKey(pubkey_der.get(), CKK_RSA)); | |
| 474 if (!pubkey) | |
| 475 return Status::OperationError(); | |
| 476 | |
| 477 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 478 if (!CreateRsaHashedPublicKeyAlgorithm( | |
| 479 algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), | |
| 480 pubkey.get(), &key_algorithm)) { | |
| 481 return Status::ErrorUnexpected(); | |
| 482 } | |
| 483 | |
| 484 std::vector<uint8_t> spki_data; | |
| 485 Status status = ExportKeySpkiNss(pubkey.get(), &spki_data); | |
| 486 if (status.IsError()) | |
| 487 return status; | |
| 488 | |
| 489 scoped_ptr<PublicKeyNss> key_handle( | |
| 490 new PublicKeyNss(pubkey.Pass(), CryptoData(spki_data))); | |
| 491 | |
| 492 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 493 blink::WebCryptoKeyTypePublic, extractable, | |
| 494 key_algorithm, usages); | |
| 495 return Status::Success(); | |
| 496 } | |
| 497 | |
| 498 } // namespace | |
| 499 | |
| 500 Status RsaHashedAlgorithm::GenerateKey( | |
| 501 const blink::WebCryptoAlgorithm& algorithm, | |
| 502 bool extractable, | |
| 503 blink::WebCryptoKeyUsageMask combined_usages, | |
| 504 GenerateKeyResult* result) const { | |
| 505 blink::WebCryptoKeyUsageMask public_usages = 0; | |
| 506 blink::WebCryptoKeyUsageMask private_usages = 0; | |
| 507 | |
| 508 Status status = GetUsagesForGenerateAsymmetricKey( | |
| 509 combined_usages, all_public_key_usages_, all_private_key_usages_, | |
| 510 &public_usages, &private_usages); | |
| 511 if (status.IsError()) | |
| 512 return status; | |
| 513 | |
| 514 unsigned int public_exponent = 0; | |
| 515 unsigned int modulus_length_bits = 0; | |
| 516 status = GetRsaKeyGenParameters(algorithm.rsaHashedKeyGenParams(), | |
| 517 &public_exponent, &modulus_length_bits); | |
| 518 if (status.IsError()) | |
| 519 return status; | |
| 520 | |
| 521 crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); | |
| 522 if (!slot) | |
| 523 return Status::OperationError(); | |
| 524 | |
| 525 PK11RSAGenParams rsa_gen_params; | |
| 526 rsa_gen_params.keySizeInBits = modulus_length_bits; | |
| 527 rsa_gen_params.pe = public_exponent; | |
| 528 | |
| 529 // The usages are enforced at the WebCrypto layer, so it isn't necessary to | |
| 530 // create keys with limited usages. | |
| 531 const CK_FLAGS operation_flags_mask = kAllOperationFlags; | |
| 532 | |
| 533 // The private key must be marked as insensitive and extractable, otherwise it | |
| 534 // cannot later be exported in unencrypted form or structured-cloned. | |
| 535 const PK11AttrFlags attribute_flags = | |
| 536 PK11_ATTR_INSENSITIVE | PK11_ATTR_EXTRACTABLE; | |
| 537 | |
| 538 // Note: NSS does not generate an sec_public_key if the call below fails, | |
| 539 // so there is no danger of a leaked sec_public_key. | |
| 540 SECKEYPublicKey* sec_public_key; | |
| 541 crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( | |
| 542 PK11_GenerateKeyPairWithOpFlags(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, | |
| 543 &rsa_gen_params, &sec_public_key, | |
| 544 attribute_flags, generate_flags_, | |
| 545 operation_flags_mask, NULL)); | |
| 546 if (!scoped_sec_private_key) | |
| 547 return Status::OperationError(); | |
| 548 | |
| 549 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 550 if (!CreateRsaHashedPublicKeyAlgorithm( | |
| 551 algorithm.id(), algorithm.rsaHashedKeyGenParams()->hash().id(), | |
| 552 sec_public_key, &key_algorithm)) { | |
| 553 return Status::ErrorUnexpected(); | |
| 554 } | |
| 555 | |
| 556 std::vector<uint8_t> spki_data; | |
| 557 status = ExportKeySpkiNss(sec_public_key, &spki_data); | |
| 558 if (status.IsError()) | |
| 559 return status; | |
| 560 | |
| 561 scoped_ptr<PublicKeyNss> public_key_handle(new PublicKeyNss( | |
| 562 crypto::ScopedSECKEYPublicKey(sec_public_key), CryptoData(spki_data))); | |
| 563 | |
| 564 std::vector<uint8_t> pkcs8_data; | |
| 565 status = ExportKeyPkcs8Nss(scoped_sec_private_key.get(), &pkcs8_data); | |
| 566 if (status.IsError()) | |
| 567 return status; | |
| 568 | |
| 569 scoped_ptr<PrivateKeyNss> private_key_handle( | |
| 570 new PrivateKeyNss(scoped_sec_private_key.Pass(), CryptoData(pkcs8_data))); | |
| 571 | |
| 572 blink::WebCryptoKey public_key = blink::WebCryptoKey::create( | |
| 573 public_key_handle.release(), blink::WebCryptoKeyTypePublic, true, | |
| 574 key_algorithm, public_usages); | |
| 575 | |
| 576 blink::WebCryptoKey private_key = blink::WebCryptoKey::create( | |
| 577 private_key_handle.release(), blink::WebCryptoKeyTypePrivate, extractable, | |
| 578 key_algorithm, private_usages); | |
| 579 | |
| 580 result->AssignKeyPair(public_key, private_key); | |
| 581 return Status::Success(); | |
| 582 } | |
| 583 | |
| 584 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey( | |
| 585 blink::WebCryptoKeyFormat format, | |
| 586 blink::WebCryptoKeyUsageMask usages) const { | |
| 587 return VerifyUsagesBeforeImportAsymmetricKey(format, all_public_key_usages_, | |
| 588 all_private_key_usages_, usages); | |
| 589 } | |
| 590 | |
| 591 Status RsaHashedAlgorithm::ImportKeyPkcs8( | |
| 592 const CryptoData& key_data, | |
| 593 const blink::WebCryptoAlgorithm& algorithm, | |
| 594 bool extractable, | |
| 595 blink::WebCryptoKeyUsageMask usages, | |
| 596 blink::WebCryptoKey* key) const { | |
| 597 Status status = NssSupportsRsaPrivateKeyImport(); | |
| 598 if (status.IsError()) | |
| 599 return status; | |
| 600 | |
| 601 crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); | |
| 602 if (!arena.get()) | |
| 603 return Status::OperationError(); | |
| 604 | |
| 605 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 PKCS#8 | |
| 606 // private key info object. Excess data is illegal, but NSS silently accepts | |
| 607 // it, so first ensure that 'key_data' consists of a single ASN.1 element. | |
| 608 SECItem key_item = MakeSECItemForBuffer(key_data); | |
| 609 SECItem pki_der; | |
| 610 if (SEC_QuickDERDecodeItem(arena.get(), &pki_der, | |
| 611 SEC_ASN1_GET(SEC_AnyTemplate), | |
| 612 &key_item) != SECSuccess) { | |
| 613 return Status::DataError(); | |
| 614 } | |
| 615 | |
| 616 SECKEYPrivateKey* seckey_private_key = NULL; | |
| 617 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); | |
| 618 if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot.get(), &pki_der, | |
| 619 NULL, // nickname | |
| 620 NULL, // publicValue | |
| 621 false, // isPerm | |
| 622 false, // isPrivate | |
| 623 KU_ALL, // usage | |
| 624 &seckey_private_key, | |
| 625 NULL) != SECSuccess) { | |
| 626 return Status::DataError(); | |
| 627 } | |
| 628 DCHECK(seckey_private_key); | |
| 629 crypto::ScopedSECKEYPrivateKey private_key(seckey_private_key); | |
| 630 | |
| 631 const KeyType sec_key_type = SECKEY_GetPrivateKeyType(private_key.get()); | |
| 632 if (sec_key_type != rsaKey) | |
| 633 return Status::DataError(); | |
| 634 | |
| 635 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 636 if (!CreateRsaHashedPrivateKeyAlgorithm( | |
| 637 algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), | |
| 638 private_key.get(), &key_algorithm)) { | |
| 639 return Status::ErrorUnexpected(); | |
| 640 } | |
| 641 | |
| 642 // TODO(eroman): This is probably going to be the same as the input. | |
| 643 std::vector<uint8_t> pkcs8_data; | |
| 644 status = ExportKeyPkcs8Nss(private_key.get(), &pkcs8_data); | |
| 645 if (status.IsError()) | |
| 646 return status; | |
| 647 | |
| 648 scoped_ptr<PrivateKeyNss> key_handle( | |
| 649 new PrivateKeyNss(private_key.Pass(), CryptoData(pkcs8_data))); | |
| 650 | |
| 651 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 652 blink::WebCryptoKeyTypePrivate, | |
| 653 extractable, key_algorithm, usages); | |
| 654 | |
| 655 return Status::Success(); | |
| 656 } | |
| 657 | |
| 658 Status RsaHashedAlgorithm::ImportKeySpki( | |
| 659 const CryptoData& key_data, | |
| 660 const blink::WebCryptoAlgorithm& algorithm, | |
| 661 bool extractable, | |
| 662 blink::WebCryptoKeyUsageMask usages, | |
| 663 blink::WebCryptoKey* key) const { | |
| 664 // The binary blob 'key_data' is expected to be a DER-encoded ASN.1 Subject | |
| 665 // Public Key Info. Decode this to a CERTSubjectPublicKeyInfo. | |
| 666 SECItem spki_item = MakeSECItemForBuffer(key_data); | |
| 667 const ScopedCERTSubjectPublicKeyInfo spki( | |
| 668 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); | |
| 669 if (!spki) | |
| 670 return Status::DataError(); | |
| 671 | |
| 672 crypto::ScopedSECKEYPublicKey sec_public_key( | |
| 673 SECKEY_ExtractPublicKey(spki.get())); | |
| 674 if (!sec_public_key) | |
| 675 return Status::DataError(); | |
| 676 | |
| 677 const KeyType sec_key_type = SECKEY_GetPublicKeyType(sec_public_key.get()); | |
| 678 if (sec_key_type != rsaKey) | |
| 679 return Status::DataError(); | |
| 680 | |
| 681 blink::WebCryptoKeyAlgorithm key_algorithm; | |
| 682 if (!CreateRsaHashedPublicKeyAlgorithm( | |
| 683 algorithm.id(), algorithm.rsaHashedImportParams()->hash().id(), | |
| 684 sec_public_key.get(), &key_algorithm)) { | |
| 685 return Status::ErrorUnexpected(); | |
| 686 } | |
| 687 | |
| 688 // TODO(eroman): This is probably going to be the same as the input. | |
| 689 std::vector<uint8_t> spki_data; | |
| 690 Status status = ExportKeySpkiNss(sec_public_key.get(), &spki_data); | |
| 691 if (status.IsError()) | |
| 692 return status; | |
| 693 | |
| 694 scoped_ptr<PublicKeyNss> key_handle( | |
| 695 new PublicKeyNss(sec_public_key.Pass(), CryptoData(spki_data))); | |
| 696 | |
| 697 *key = blink::WebCryptoKey::create(key_handle.release(), | |
| 698 blink::WebCryptoKeyTypePublic, extractable, | |
| 699 key_algorithm, usages); | |
| 700 | |
| 701 return Status::Success(); | |
| 702 } | |
| 703 | |
| 704 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key, | |
| 705 std::vector<uint8_t>* buffer) const { | |
| 706 if (key.type() != blink::WebCryptoKeyTypePrivate) | |
| 707 return Status::ErrorUnexpectedKeyType(); | |
| 708 *buffer = PrivateKeyNss::Cast(key)->pkcs8_data(); | |
| 709 return Status::Success(); | |
| 710 } | |
| 711 | |
| 712 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key, | |
| 713 std::vector<uint8_t>* buffer) const { | |
| 714 if (key.type() != blink::WebCryptoKeyTypePublic) | |
| 715 return Status::ErrorUnexpectedKeyType(); | |
| 716 *buffer = PublicKeyNss::Cast(key)->spki_data(); | |
| 717 return Status::Success(); | |
| 718 } | |
| 719 | |
| 720 Status RsaHashedAlgorithm::ImportKeyJwk( | |
| 721 const CryptoData& key_data, | |
| 722 const blink::WebCryptoAlgorithm& algorithm, | |
| 723 bool extractable, | |
| 724 blink::WebCryptoKeyUsageMask usages, | |
| 725 blink::WebCryptoKey* key) const { | |
| 726 const char* jwk_algorithm = | |
| 727 GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id()); | |
| 728 | |
| 729 if (!jwk_algorithm) | |
| 730 return Status::ErrorUnexpected(); | |
| 731 | |
| 732 JwkRsaInfo jwk; | |
| 733 Status status = | |
| 734 ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usages, &jwk); | |
| 735 if (status.IsError()) | |
| 736 return status; | |
| 737 | |
| 738 // Once the key type is known, verify the usages. | |
| 739 status = CheckKeyCreationUsages( | |
| 740 jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_, | |
| 741 usages, !jwk.is_private_key); | |
| 742 if (status.IsError()) | |
| 743 return status; | |
| 744 | |
| 745 return jwk.is_private_key | |
| 746 ? ImportRsaPrivateKey(algorithm, extractable, usages, jwk, key) | |
| 747 : ImportRsaPublicKey(algorithm, extractable, usages, | |
| 748 CryptoData(jwk.n), CryptoData(jwk.e), key); | |
| 749 } | |
| 750 | |
| 751 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, | |
| 752 std::vector<uint8_t>* buffer) const { | |
| 753 const char* jwk_algorithm = | |
| 754 GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id()); | |
| 755 | |
| 756 if (!jwk_algorithm) | |
| 757 return Status::ErrorUnexpected(); | |
| 758 | |
| 759 switch (key.type()) { | |
| 760 case blink::WebCryptoKeyTypePublic: { | |
| 761 SECKEYPublicKey* nss_key = PublicKeyNss::Cast(key)->key(); | |
| 762 if (nss_key->keyType != rsaKey) | |
| 763 return Status::ErrorUnsupported(); | |
| 764 | |
| 765 WriteRsaPublicKeyJwk(SECItemToCryptoData(nss_key->u.rsa.modulus), | |
| 766 SECItemToCryptoData(nss_key->u.rsa.publicExponent), | |
| 767 jwk_algorithm, key.extractable(), key.usages(), | |
| 768 buffer); | |
| 769 | |
| 770 return Status::Success(); | |
| 771 } | |
| 772 | |
| 773 case blink::WebCryptoKeyTypePrivate: { | |
| 774 SECKEYPrivateKey* nss_key = PrivateKeyNss::Cast(key)->key(); | |
| 775 RSAPrivateKey key_props = {}; | |
| 776 scoped_ptr<RSAPrivateKey, FreeRsaPrivateKey> free_private_key(&key_props); | |
| 777 | |
| 778 if (!InitRSAPrivateKey(nss_key, &key_props)) | |
| 779 return Status::OperationError(); | |
| 780 | |
| 781 WriteRsaPrivateKeyJwk(SECItemToCryptoData(key_props.modulus), | |
| 782 SECItemToCryptoData(key_props.public_exponent), | |
| 783 SECItemToCryptoData(key_props.private_exponent), | |
| 784 SECItemToCryptoData(key_props.prime1), | |
| 785 SECItemToCryptoData(key_props.prime2), | |
| 786 SECItemToCryptoData(key_props.exponent1), | |
| 787 SECItemToCryptoData(key_props.exponent2), | |
| 788 SECItemToCryptoData(key_props.coefficient), | |
| 789 jwk_algorithm, key.extractable(), key.usages(), | |
| 790 buffer); | |
| 791 | |
| 792 return Status::Success(); | |
| 793 } | |
| 794 default: | |
| 795 return Status::ErrorUnexpected(); | |
| 796 } | |
| 797 } | |
| 798 | |
| 799 Status RsaHashedAlgorithm::SerializeKeyForClone( | |
| 800 const blink::WebCryptoKey& key, | |
| 801 blink::WebVector<uint8_t>* key_data) const { | |
| 802 key_data->assign(static_cast<KeyNss*>(key.handle())->serialized_key_data()); | |
| 803 return Status::Success(); | |
| 804 } | |
| 805 | |
| 806 // TODO(eroman): Defer import to the crypto thread. http://crbug.com/430763 | |
| 807 Status RsaHashedAlgorithm::DeserializeKeyForClone( | |
| 808 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 809 blink::WebCryptoKeyType type, | |
| 810 bool extractable, | |
| 811 blink::WebCryptoKeyUsageMask usages, | |
| 812 const CryptoData& key_data, | |
| 813 blink::WebCryptoKey* key) const { | |
| 814 blink::WebCryptoAlgorithm import_algorithm = CreateRsaHashedImportAlgorithm( | |
| 815 algorithm.id(), algorithm.rsaHashedParams()->hash().id()); | |
| 816 | |
| 817 Status status; | |
| 818 | |
| 819 switch (type) { | |
| 820 case blink::WebCryptoKeyTypePublic: | |
| 821 status = | |
| 822 ImportKeySpki(key_data, import_algorithm, extractable, usages, key); | |
| 823 break; | |
| 824 case blink::WebCryptoKeyTypePrivate: | |
| 825 status = | |
| 826 ImportKeyPkcs8(key_data, import_algorithm, extractable, usages, key); | |
| 827 break; | |
| 828 default: | |
| 829 return Status::ErrorUnexpected(); | |
| 830 } | |
| 831 | |
| 832 // There is some duplicated information in the serialized format used by | |
| 833 // structured clone (since the KeyAlgorithm is serialized separately from the | |
| 834 // key data). Use this extra information to further validate what was | |
| 835 // deserialized from the key data. | |
| 836 | |
| 837 if (algorithm.id() != key->algorithm().id()) | |
| 838 return Status::ErrorUnexpected(); | |
| 839 | |
| 840 if (key->type() != type) | |
| 841 return Status::ErrorUnexpected(); | |
| 842 | |
| 843 if (algorithm.rsaHashedParams()->modulusLengthBits() != | |
| 844 key->algorithm().rsaHashedParams()->modulusLengthBits()) { | |
| 845 return Status::ErrorUnexpected(); | |
| 846 } | |
| 847 | |
| 848 if (algorithm.rsaHashedParams()->publicExponent().size() != | |
| 849 key->algorithm().rsaHashedParams()->publicExponent().size() || | |
| 850 0 != | |
| 851 memcmp(algorithm.rsaHashedParams()->publicExponent().data(), | |
| 852 key->algorithm().rsaHashedParams()->publicExponent().data(), | |
| 853 key->algorithm().rsaHashedParams()->publicExponent().size())) { | |
| 854 return Status::ErrorUnexpected(); | |
| 855 } | |
| 856 | |
| 857 return Status::Success(); | |
| 858 } | |
| 859 | |
| 860 } // namespace webcrypto | |
| 861 | |
| 862 } // namespace content | |
| OLD | NEW |