Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1818)

Unified Diff: content/child/webcrypto/jwk.cc

Issue 287133004: [webcrypto] Add JWK import/export of RSA private keys (NSS). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix comment typeo Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/child/webcrypto/platform_crypto.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/child/webcrypto/jwk.cc
diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc
index 53dd787d7a36cc5ebe15fc9aedc7370a1d10aff4..118df718af4eafea663a30c1b2c4e6e7e60f88f0 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,31 @@ 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. In the case
+// where the property does not exist, |result| is guaranteed to be empty.
+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 (status.IsError())
+ return status;
+
+ if (!*property_exists) {
+ result->clear();
+ return Status::Success();
+ }
+
+ 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 +535,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" in the JWA, WebCrypto spec requires them to
+ // be emitted.
+ 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 +694,102 @@ 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();
+Status ImportRsaKey(base::DictionaryValue* dict,
+ const blink::WebCryptoAlgorithm& algorithm,
+ bool extractable,
+ blink::WebCryptoKeyUsageMask usage_mask,
+ blink::WebCryptoKey* key) {
+ // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
+ // in the JWK, while an RSA private key must have those, plus at least a "d"
+ // (private exponent) entry.
+ // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18,
+ // section 6.3.
+ std::string jwk_n_value;
+ Status status = GetJwkBytes(dict, "n", &jwk_n_value);
+ if (status.IsError())
+ return status;
+ std::string jwk_e_value;
+ status = GetJwkBytes(dict, "e", &jwk_e_value);
+ if (status.IsError())
+ return status;
+
+ if (!dict->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, "d", &jwk_d_value);
+ if (status.IsError())
+ return status;
+
+ // The "p", "q", "dp", "dq", and "qi" properties are optional. Treat these
+ // properties the same if they are unspecified, as if they were specified-but
+ // empty, since ImportRsaPrivateKey() doesn't do validation checks anyway.
+
+ std::string jwk_p_value;
+ bool has_p;
+ status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_q_value;
+ bool has_q;
+ status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_dp_value;
+ bool has_dp;
+ status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_dq_value;
+ bool has_dq;
+ status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq);
+ if (status.IsError())
+ return status;
+
+ std::string jwk_qi_value;
+ bool has_qi;
+ status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi);
+ if (status.IsError())
+ return status;
+
+ int num_optional_properties = has_p + has_q + has_dp + has_dq + has_qi;
+ if (num_optional_properties != 0 && num_optional_properties != 5)
+ return Status::ErrorJwkIncompleteOptionalRsaPrivateKey();
+
+ return platform::ImportRsaPrivateKey(
+ algorithm,
+ extractable,
+ usage_mask,
+ CryptoData(jwk_n_value), // modulus
+ CryptoData(jwk_e_value), // public_exponent
+ CryptoData(jwk_d_value), // private_exponent
+ CryptoData(jwk_p_value), // prime1
+ CryptoData(jwk_q_value), // prime2
+ CryptoData(jwk_dp_value), // exponent1
+ CryptoData(jwk_dq_value), // exponent2
+ CryptoData(jwk_qi_value), // coefficient
+ key);
}
} // namespace
+// TODO(eroman): Split this up into smaller functions.
Status ImportKeyJwk(const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
@@ -747,36 +925,9 @@ Status ImportKeyJwk(const CryptoData& key_data,
usage_mask,
key);
}
- if (jwk_kty_value == "RSA") {
- // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry
- // in the JWK, while an RSA private key must have those, plus at least a "d"
- // (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())
- return status;
- std::string jwk_e_value;
- status = GetJwkBytes(dict_value, "e", &jwk_e_value);
- if (status.IsError())
- return status;
-
- return platform::ImportRsaPublicKey(algorithm,
- extractable,
- usage_mask,
- CryptoData(jwk_n_value),
- CryptoData(jwk_e_value),
- key);
- }
+ if (jwk_kty_value == "RSA")
+ return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key);
return Status::ErrorJwkUnrecognizedKty();
}
@@ -797,8 +948,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 +964,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();
}
« no previous file with comments | « no previous file | content/child/webcrypto/platform_crypto.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698