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