Chromium Code Reviews| Index: content/child/webcrypto/jwk.cc |
| diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc |
| index 53dd787d7a36cc5ebe15fc9aedc7370a1d10aff4..2e67ddd6e94c5df7337fe14026a424c3d0520c8d 100644 |
| --- a/content/child/webcrypto/jwk.cc |
| +++ b/content/child/webcrypto/jwk.cc |
| @@ -130,11 +130,8 @@ |
| // |
| // kty-specific parameters |
| // The value of kty determines the type and content of the keying material |
| -// carried in the JWK to be imported. Currently only two possibilities are |
| -// supported: a raw key or an RSA public key. RSA private keys are not |
| -// supported because typical applications seldom need to import a private key, |
| -// and the large number of JWK parameters required to describe one. |
| -// - kty == "oct" (symmetric or other raw key) |
| +// carried in the JWK to be imported. |
| +// // - kty == "oct" (symmetric or other raw key) |
| // +-------+--------------------------------------------------------------+ |
| // | "k" | Contains the value of the symmetric (or other single-valued) | |
| // | | key. It is represented as the base64url encoding of the | |
| @@ -150,6 +147,39 @@ |
| // | | represented as the base64url encoding of the value's | |
| // | | unsigned big endian representation as an octet sequence. | |
| // +-------+--------------------------------------------------------------+ |
| +// - If key == "RSA" and the "d" parameter is present then it is a private key. |
| +// All the parameters above for public keys apply, as well as the following. |
| +// (Note that except for "d", all of these are optional): |
| +// +-------+--------------------------------------------------------------+ |
| +// | "d" | Contains the private exponent value for the RSA private key. | |
| +// | | It is represented as the base64url encoding of the value's | |
| +// | | unsigned big endian representation as an octet sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| +// | "p" | Contains the first prime factor value for the RSA private | |
| +// | | key. It is represented as the base64url encoding of the | |
| +// | | value's | |
| +// | | unsigned big endian representation as an octet sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| +// | "q" | Contains the second prime factor value for the RSA private | |
| +// | | key. It is represented as the base64url encoding of the | |
| +// | | value's unsigned big endian representation as an octet | |
| +// | | sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| +// | "dp" | Contains the first factor CRT exponent value for the RSA | |
| +// | | private key. It is represented as the base64url encoding of | |
| +// | | the value's unsigned big endian representation as an octet | |
| +// | | sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| +// | "dq" | Contains the second factor CRT exponent value for the RSA | |
| +// | | private key. It is represented as the base64url encoding of | |
| +// | | the value's unsigned big endian representation as an octet | |
| +// | | sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| +// | "dq" | Contains the first CRT coefficient value for the RSA private | |
| +// | | key. It is represented as the base64url encoding of the | |
| +// | | value's unsigned big endian representation as an octet | |
| +// | | sequence. | |
| +// +-------+--------------------------------------------------------------+ |
| // |
| // Consistency and conflict resolution |
| // The 'algorithm', 'extractable', and 'usage_mask' input parameters |
| @@ -430,6 +460,25 @@ Status GetJwkBytes(base::DictionaryValue* dict, |
| return Status::Success(); |
| } |
| +// Extracts the optional string property with key |path| from |dict| and saves |
| +// the base64url-decoded bytes to |*result|. If the property exist and is not a |
| +// string, or could not be base64url-decoded, returns an error. |
| +Status GetOptionalJwkBytes(base::DictionaryValue* dict, |
| + const std::string& path, |
| + std::string* result, |
| + bool* property_exists) { |
| + std::string base64_string; |
| + Status status = |
| + GetOptionalJwkString(dict, path, &base64_string, property_exists); |
| + if (!*property_exists || status.IsError()) |
|
Ryan Sleevi
2014/05/19 00:10:34
nit: Check status.IsError() before doing *property
eroman
2014/05/19 18:51:09
Good point! Done.
Reading the boolean first could
|
| + return status; |
| + |
| + if (!Base64DecodeUrlSafe(base64_string, result)) |
| + return Status::ErrorJwkBase64Decode(path); |
| + |
| + return Status::Success(); |
| +} |
| + |
| // Extracts the optional boolean property with key |path| from |dict| and saves |
| // the result to |*result| if it was found. If the property exists and is not a |
| // boolean, returns an error. Otherwise returns success, and sets |
| @@ -480,6 +529,53 @@ void WriteRsaPublicKey(const std::vector<uint8>& modulus, |
| jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); |
| } |
| +// Writes an RSA private key to a JWK dictionary |
| +Status ExportRsaPrivateKeyJwk(const blink::WebCryptoKey& key, |
| + base::DictionaryValue* jwk_dict) { |
| + platform::PrivateKey* private_key; |
| + Status status = ToPlatformPrivateKey(key, &private_key); |
| + if (status.IsError()) |
| + return status; |
| + |
| + // TODO(eroman): Copying the key properties to temporary vectors is |
| + // inefficient. Once there aren't two implementations of platform_crypto this |
| + // and other code will be easier to streamline. |
| + std::vector<uint8> modulus; |
| + std::vector<uint8> public_exponent; |
| + std::vector<uint8> private_exponent; |
| + std::vector<uint8> prime1; |
| + std::vector<uint8> prime2; |
| + std::vector<uint8> exponent1; |
| + std::vector<uint8> exponent2; |
| + std::vector<uint8> coefficient; |
| + |
| + status = platform::ExportRsaPrivateKey(private_key, |
| + &modulus, |
| + &public_exponent, |
| + &private_exponent, |
| + &prime1, |
| + &prime2, |
| + &exponent1, |
| + &exponent2, |
| + &coefficient); |
| + if (status.IsError()) |
| + return status; |
| + |
| + jwk_dict->SetString("kty", "RSA"); |
| + jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); |
| + jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); |
| + jwk_dict->SetString("d", Base64EncodeUrlSafe(private_exponent)); |
| + // Although these are "optional", the JWA spec says that producers |
| + // SHOULD include them. |
|
Ryan Sleevi
2014/05/19 00:10:34
Doesn't matter what JWA says. The WebCrypto spec i
eroman
2014/05/19 18:51:09
Done, updated the comment.
|
| + jwk_dict->SetString("p", Base64EncodeUrlSafe(prime1)); |
| + jwk_dict->SetString("q", Base64EncodeUrlSafe(prime2)); |
| + jwk_dict->SetString("dp", Base64EncodeUrlSafe(exponent1)); |
| + jwk_dict->SetString("dq", Base64EncodeUrlSafe(exponent2)); |
| + jwk_dict->SetString("qi", Base64EncodeUrlSafe(coefficient)); |
| + |
| + return Status::Success(); |
| +} |
| + |
| // Writes a Web Crypto usage mask to a JWK dictionary. |
| void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages, |
| base::DictionaryValue* jwk_dict) { |
| @@ -592,26 +688,16 @@ Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, |
| return Status::Success(); |
| } |
| -bool IsRsaPublicKey(const blink::WebCryptoKey& key) { |
| - if (key.type() != blink::WebCryptoKeyTypePublic) |
| - return false; |
| +bool IsRsaKey(const blink::WebCryptoKey& key) { |
| const blink::WebCryptoAlgorithmId algorithm_id = key.algorithm().id(); |
| return algorithm_id == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 || |
| algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 || |
| algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep; |
| } |
| -// TODO(padolph): This function is duplicated in shared_crypto.cc |
| -Status ToPlatformPublicKey(const blink::WebCryptoKey& key, |
| - platform::PublicKey** out) { |
| - *out = static_cast<platform::Key*>(key.handle())->AsPublicKey(); |
| - if (!*out) |
| - return Status::ErrorUnexpectedKeyType(); |
| - return Status::Success(); |
| -} |
| - |
| } // namespace |
| +// TODO(eroman): Split this up into smaller functions. |
|
Ryan Sleevi
2014/05/19 00:10:34
I would rather see RSA Private key import split ou
eroman
2014/05/19 18:51:09
Done.
|
| Status ImportKeyJwk(const CryptoData& key_data, |
| const blink::WebCryptoAlgorithm& algorithm, |
| bool extractable, |
| @@ -753,13 +839,6 @@ Status ImportKeyJwk(const CryptoData& key_data, |
| // (private exponent) entry. |
| // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| // section 6.3. |
| - |
| - // RSA private key import is not currently supported, so fail here if a "d" |
| - // entry is found. |
| - // TODO(padolph): Support RSA private key import. |
| - if (dict_value->HasKey("d")) |
| - return Status::ErrorJwkRsaPrivateKeyUnsupported(); |
| - |
| std::string jwk_n_value; |
| status = GetJwkBytes(dict_value, "n", &jwk_n_value); |
| if (status.IsError()) |
| @@ -769,13 +848,68 @@ Status ImportKeyJwk(const CryptoData& key_data, |
| if (status.IsError()) |
| return status; |
| - return platform::ImportRsaPublicKey(algorithm, |
| - extractable, |
| - usage_mask, |
| - CryptoData(jwk_n_value), |
| - CryptoData(jwk_e_value), |
| - key); |
| + if (!dict_value->HasKey("d")) { |
| + return platform::ImportRsaPublicKey(algorithm, |
| + extractable, |
| + usage_mask, |
| + CryptoData(jwk_n_value), |
| + CryptoData(jwk_e_value), |
| + key); |
| + } |
| + |
| + std::string jwk_d_value; |
| + status = GetJwkBytes(dict_value, "d", &jwk_d_value); |
| + if (status.IsError()) |
| + return status; |
| + |
| + std::string jwk_p_value; |
| + bool has_p; |
| + status = GetOptionalJwkBytes(dict_value, "p", &jwk_p_value, &has_p); |
| + if (status.IsError()) |
| + return status; |
| + |
| + std::string jwk_q_value; |
| + bool has_q; |
| + status = GetOptionalJwkBytes(dict_value, "q", &jwk_q_value, &has_q); |
| + if (status.IsError()) |
| + return status; |
| + std::string jwk_dp_value; |
| + bool has_dp; |
| + status = GetOptionalJwkBytes(dict_value, "dp", &jwk_dp_value, &has_dp); |
| + if (status.IsError()) |
| + return status; |
| + |
| + std::string jwk_dq_value; |
| + bool has_dq; |
| + status = GetOptionalJwkBytes(dict_value, "dq", &jwk_dq_value, &has_dq); |
| + if (status.IsError()) |
| + return status; |
| + |
| + std::string jwk_qi_value; |
| + bool has_qi; |
| + status = GetOptionalJwkBytes(dict_value, "qi", &jwk_qi_value, &has_qi); |
| + if (status.IsError()) |
| + return status; |
| + |
| + return platform::ImportRsaPrivateKey( |
| + algorithm, |
| + extractable, |
| + usage_mask, |
| + CryptoData(jwk_n_value), // modulus |
| + CryptoData(jwk_e_value), // public_exponent |
| + CryptoData(jwk_d_value), // private_exponent |
| + has_p, |
| + CryptoData(jwk_p_value), // prime1 |
| + has_q, |
| + CryptoData(jwk_q_value), // prime2 |
| + has_dp, |
| + CryptoData(jwk_dp_value), // exponent1 |
| + has_dq, |
| + CryptoData(jwk_dq_value), // exponent2 |
| + has_qi, |
| + CryptoData(jwk_qi_value), // coefficient |
| + key); |
| } |
| return Status::ErrorJwkUnrecognizedKty(); |
| @@ -797,8 +931,8 @@ Status ExportKeyJwk(const blink::WebCryptoKey& key, |
| break; |
| } |
| case blink::WebCryptoKeyTypePublic: { |
| - // Currently only RSA public key export is supported. |
| - if (!IsRsaPublicKey(key)) |
| + // TODO(eroman): Update when there are asymmetric keys other than RSA. |
| + if (!IsRsaKey(key)) |
| return Status::ErrorUnsupported(); |
| platform::PublicKey* public_key; |
| status = ToPlatformPublicKey(key, &public_key); |
| @@ -813,7 +947,17 @@ Status ExportKeyJwk(const blink::WebCryptoKey& key, |
| WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); |
| break; |
| } |
| - case blink::WebCryptoKeyTypePrivate: // TODO(padolph) |
| + case blink::WebCryptoKeyTypePrivate: { |
| + // TODO(eroman): Update when there are asymmetric keys other than RSA. |
| + if (!IsRsaKey(key)) |
| + return Status::ErrorUnsupported(); |
| + |
| + status = ExportRsaPrivateKeyJwk(key, &jwk_dict); |
| + if (status.IsError()) |
| + return status; |
| + break; |
| + } |
| + |
| default: |
| return Status::ErrorUnsupported(); |
| } |