diff options
Diffstat (limited to 'content/renderer/webcrypto/webcrypto_impl_nss.cc')
-rw-r--r-- | content/renderer/webcrypto/webcrypto_impl_nss.cc | 146 |
1 files changed, 141 insertions, 5 deletions
diff --git a/content/renderer/webcrypto/webcrypto_impl_nss.cc b/content/renderer/webcrypto/webcrypto_impl_nss.cc index 452d2a7..2b143a5 100644 --- a/content/renderer/webcrypto/webcrypto_impl_nss.cc +++ b/content/renderer/webcrypto/webcrypto_impl_nss.cc @@ -24,10 +24,7 @@ namespace { class SymKeyHandle : public WebKit::WebCryptoKeyHandle { public: - explicit SymKeyHandle(crypto::ScopedPK11SymKey key) { - DCHECK(!key_.get()); - key_ = key.Pass(); - } + explicit SymKeyHandle(crypto::ScopedPK11SymKey key) : key_(key.Pass()) {} PK11SymKey* key() { return key_.get(); } @@ -37,6 +34,32 @@ class SymKeyHandle : public WebKit::WebCryptoKeyHandle { DISALLOW_COPY_AND_ASSIGN(SymKeyHandle); }; +class PublicKeyHandle : public WebKit::WebCryptoKeyHandle { + public: + explicit PublicKeyHandle(crypto::ScopedSECKEYPublicKey key) + : key_(key.Pass()) {} + + SECKEYPublicKey* key() { return key_.get(); } + + private: + crypto::ScopedSECKEYPublicKey key_; + + DISALLOW_COPY_AND_ASSIGN(PublicKeyHandle); +}; + +class PrivateKeyHandle : public WebKit::WebCryptoKeyHandle { + public: + explicit PrivateKeyHandle(crypto::ScopedSECKEYPrivateKey key) + : key_(key.Pass()) {} + + SECKEYPrivateKey* key() { return key_.get(); } + + private: + crypto::ScopedSECKEYPrivateKey key_; + + DISALLOW_COPY_AND_ASSIGN(PrivateKeyHandle); +}; + HASH_HashType WebCryptoAlgorithmToNSSHashType( const WebKit::WebCryptoAlgorithm& algorithm) { switch (algorithm.id()) { @@ -102,7 +125,7 @@ bool AesCbcEncryptDecrypt( if (!context.get()) return false; - // Oddly PK11_CipherOp takes input and output lenths as "int" rather than + // Oddly PK11_CipherOp takes input and output lengths as "int" rather than // "unsigned". Do some checks now to avoid integer overflowing. if (data_size >= INT_MAX - AES_BLOCK_SIZE) { // TODO(eroman): Handle this by chunking the input fed into NSS. Right now @@ -193,6 +216,28 @@ unsigned int WebCryptoHmacAlgorithmToBlockSize( } } +// Converts a (big-endian) WebCrypto BigInteger, with or without leading zeros, +// to unsigned long. +bool BigIntegerToLong(const uint8* data, + unsigned data_size, + unsigned long* result) { + // TODO(padolph): Is it correct to say that empty data is an error, or does it + // mean value 0? See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23655 + if (data_size == 0) + return false; + + *result = 0; + for (size_t i = 0; i < data_size; ++i) { + size_t reverse_i = data_size - i - 1; + + if (reverse_i >= sizeof(unsigned long) && data[i]) + return false; // Too large for a long. + + *result |= data[i] << 8 * reverse_i; + } + return true; +} + } // namespace void WebCryptoImpl::Init() { @@ -324,6 +369,97 @@ bool WebCryptoImpl::GenerateKeyInternal( return true; } +bool WebCryptoImpl::GenerateKeyPairInternal( + const WebKit::WebCryptoAlgorithm& algorithm, + bool extractable, + WebKit::WebCryptoKeyUsageMask usage_mask, + WebKit::WebCryptoKey* public_key, + WebKit::WebCryptoKey* private_key) { + + // TODO(padolph): Handle other asymmetric algorithm key generation. + switch (algorithm.id()) { + case WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: + case WebKit::WebCryptoAlgorithmIdRsaOaep: + case WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: { + const WebKit::WebCryptoRsaKeyGenParams* const params = + algorithm.rsaKeyGenParams(); + DCHECK(params); + + crypto::ScopedPK11Slot slot(PK11_GetInternalKeySlot()); + unsigned long public_exponent; + if (!slot || !params->modulusLength() || + !BigIntegerToLong(params->publicExponent().data(), + params->publicExponent().size(), + &public_exponent) || + !public_exponent) { + return false; + } + + PK11RSAGenParams rsa_gen_params; + rsa_gen_params.keySizeInBits = params->modulusLength(); + rsa_gen_params.pe = public_exponent; + + // Flags are verified at the Blink layer; here the flags are set to all + // possible operations for the given key type. + CK_FLAGS operation_flags; + switch (algorithm.id()) { + case WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: + case WebKit::WebCryptoAlgorithmIdRsaOaep: + operation_flags = CKF_ENCRYPT | CKF_DECRYPT | CKF_WRAP | CKF_UNWRAP; + break; + case WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: + operation_flags = CKF_SIGN | CKF_VERIFY; + break; + default: + NOTREACHED(); + return false; + } + const CK_FLAGS operation_flags_mask = CKF_ENCRYPT | CKF_DECRYPT | + CKF_SIGN | CKF_VERIFY | CKF_WRAP | + CKF_UNWRAP; + const PK11AttrFlags attribute_flags = 0; // Default all PK11_ATTR_ flags. + + // Note: NSS does not generate an sec_public_key if the call below fails, + // so there is no danger of a leaked sec_public_key. + SECKEYPublicKey* sec_public_key; + crypto::ScopedSECKEYPrivateKey scoped_sec_private_key( + PK11_GenerateKeyPairWithOpFlags(slot.get(), + CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsa_gen_params, + &sec_public_key, + attribute_flags, + operation_flags, + operation_flags_mask, + NULL)); + if (!private_key) { + return false; + } + + // One extractable input parameter is provided, and the Web Crypto API + // spec at this time says it applies to both members of the key pair. + // This is probably not correct: it makes more operational sense to have + // extractable apply only to the private key and make the public key + // always extractable. For now implement what the spec says and track the + // spec bug here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23695 + *public_key = WebKit::WebCryptoKey::create( + new PublicKeyHandle(crypto::ScopedSECKEYPublicKey(sec_public_key)), + WebKit::WebCryptoKeyTypePublic, + extractable, // probably should be 'true' always + algorithm, + usage_mask); + *private_key = WebKit::WebCryptoKey::create( + new PrivateKeyHandle(scoped_sec_private_key.Pass()), + WebKit::WebCryptoKeyTypePrivate, + extractable, + algorithm, + usage_mask); + + return true; + } + default: + return false; + } +} bool WebCryptoImpl::ImportKeyInternal( WebKit::WebCryptoKeyFormat format, |