Index: content/child/webcrypto/jwk.cc |
diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc |
index 24683aa6d8da89535cf6dccd562b713792d4b3b7..b6e8731933ae57a9ed2f534569df64250434e961 100644 |
--- a/content/child/webcrypto/jwk.cc |
+++ b/content/child/webcrypto/jwk.cc |
@@ -10,15 +10,15 @@ |
#include "base/json/json_reader.h" |
#include "base/json/json_writer.h" |
-#include "base/lazy_instance.h" |
#include "base/strings/string_piece.h" |
-#include "base/strings/stringprintf.h" |
#include "content/child/webcrypto/crypto_data.h" |
-#include "content/child/webcrypto/platform_crypto.h" |
-#include "content/child/webcrypto/shared_crypto.h" |
#include "content/child/webcrypto/status.h" |
#include "content/child/webcrypto/webcrypto_util.h" |
-#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" |
+ |
+// TODO(eroman): The algorithm-specific logic in this file for AES and RSA |
+// should be moved into the corresponding AlgorithmImplementation file. It |
+// exists in this file to avoid duplication between OpenSSL and NSS |
+// implementations. |
// JSON Web Key Format (JWK) |
// http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 |
@@ -209,22 +209,6 @@ namespace webcrypto { |
namespace { |
-// Creates an RSASSA-PKCS1-v1_5 algorithm. It is an error to call this with a |
-// hash_id that is not a SHA*. |
-blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm( |
- blink::WebCryptoAlgorithmId hash_id) { |
- return CreateRsaHashedImportAlgorithm( |
- blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id); |
-} |
- |
-// Creates an RSA-OAEP algorithm. It is an error to call this with a hash_id |
-// that is not a SHA*. |
-blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm( |
- blink::WebCryptoAlgorithmId hash_id) { |
- return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, |
- hash_id); |
-} |
- |
// Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
@@ -234,165 +218,39 @@ const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
-typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)(); |
- |
-class JwkAlgorithmInfo { |
+class JwkWriter { |
public: |
- JwkAlgorithmInfo() |
- : creation_func_(NULL), |
- required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
- |
- explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func) |
- : creation_func_(algorithm_creation_func), |
- required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {} |
- |
- JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func, |
- unsigned int required_key_length_bits) |
- : creation_func_(algorithm_creation_func), |
- required_key_length_bytes_(required_key_length_bits / 8) { |
- DCHECK_EQ(0u, required_key_length_bits % 8); |
- } |
- |
- bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const { |
- *algorithm = creation_func_(); |
- return !algorithm->isNull(); |
+ JwkWriter(const std::string& algorithm, |
+ bool extractable, |
+ blink::WebCryptoKeyUsageMask usage_mask, |
+ const std::string& kty) { |
+ dict_.SetString("alg", algorithm); |
+ dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usage_mask)); |
+ dict_.SetBoolean("ext", extractable); |
+ dict_.SetString("kty", kty); |
} |
- bool IsInvalidKeyByteLength(size_t byte_length) const { |
- if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT) |
- return false; |
- return required_key_length_bytes_ != byte_length; |
+ void Set(const std::string& key, const std::string& value) { |
+ dict_.SetString(key, value); |
} |
- private: |
- static const unsigned int NO_KEY_SIZE_REQUIREMENT = UINT_MAX; |
- |
- AlgorithmCreationFunc creation_func_; |
- |
- // The expected key size for the algorithm or NO_KEY_SIZE_REQUIREMENT. |
- unsigned int required_key_length_bytes_; |
-}; |
- |
-typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap; |
- |
-class JwkAlgorithmRegistry { |
- public: |
- JwkAlgorithmRegistry() { |
- // TODO(eroman): |
- // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-20 |
- // says HMAC with SHA-2 should have a key size at least as large as the |
- // hash output. |
- alg_to_info_["HS1"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha1>); |
- alg_to_info_["HS256"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha256>); |
- alg_to_info_["HS384"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha384>); |
- alg_to_info_["HS512"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha512>); |
- alg_to_info_["RS1"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha1>); |
- alg_to_info_["RS256"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha256>); |
- alg_to_info_["RS384"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha384>); |
- alg_to_info_["RS512"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha512>); |
- alg_to_info_["RSA-OAEP"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha1>); |
- alg_to_info_["RSA-OAEP-256"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha256>); |
- alg_to_info_["RSA-OAEP-384"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha384>); |
- alg_to_info_["RSA-OAEP-512"] = |
- JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm, |
- blink::WebCryptoAlgorithmIdSha512>); |
- alg_to_info_["A128KW"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
- 128); |
- alg_to_info_["A192KW"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
- 192); |
- alg_to_info_["A256KW"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>, |
- 256); |
- alg_to_info_["A128GCM"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
- 128); |
- alg_to_info_["A192GCM"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
- 192); |
- alg_to_info_["A256GCM"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>, |
- 256); |
- alg_to_info_["A128CBC"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
- 128); |
- alg_to_info_["A192CBC"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
- 192); |
- alg_to_info_["A256CBC"] = JwkAlgorithmInfo( |
- &BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>, |
- 256); |
+ void SetBase64Encoded(const std::string& key, const CryptoData& value) { |
+ dict_.SetString(key, |
+ Base64EncodeUrlSafe(base::StringPiece( |
+ reinterpret_cast<const char*>(value.bytes()), |
+ value.byte_length()))); |
} |
- // Returns NULL if the algorithm name was not registered. |
- const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const { |
- const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg); |
- if (pos == alg_to_info_.end()) |
- return NULL; |
- return &pos->second; |
+ void ToBytes(std::vector<uint8>* utf8_bytes) { |
+ std::string json; |
+ base::JSONWriter::Write(&dict_, &json); |
+ utf8_bytes->assign(json.begin(), json.end()); |
} |
private: |
- // Binds a WebCryptoAlgorithmId value to a compatible factory function. |
- typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)( |
- blink::WebCryptoAlgorithmId); |
- template <FuncWithWebCryptoAlgIdArg func, |
- blink::WebCryptoAlgorithmId algorithm_id> |
- static blink::WebCryptoAlgorithm BindAlgorithmId() { |
- return func(algorithm_id); |
- } |
- |
- JwkAlgorithmInfoMap alg_to_info_; |
+ base::DictionaryValue dict_; |
}; |
-base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry = |
- LAZY_INSTANCE_INITIALIZER; |
- |
-bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1, |
- const blink::WebCryptoAlgorithm& alg2) { |
- DCHECK(!alg1.isNull()); |
- DCHECK(!alg2.isNull()); |
- if (alg1.id() != alg2.id()) |
- return false; |
- if (alg1.paramsType() != alg2.paramsType()) |
- return false; |
- switch (alg1.paramsType()) { |
- case blink::WebCryptoAlgorithmParamsTypeNone: |
- return true; |
- case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams: |
- return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(), |
- alg2.rsaHashedImportParams()->hash()); |
- case blink::WebCryptoAlgorithmParamsTypeHmacImportParams: |
- return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(), |
- alg2.hmacImportParams()->hash()); |
- default: |
- return false; |
- } |
-} |
- |
// Extracts the required string property with key |path| from |dict| and saves |
// the result to |*result|. If the property does not exist or is not a string, |
// returns an error. |
@@ -509,508 +367,353 @@ Status GetOptionalJwkBool(base::DictionaryValue* dict, |
return Status::Success(); |
} |
-// Writes a secret/symmetric key to a JWK dictionary. |
-void WriteSecretKey(const std::vector<uint8>& raw_key, |
- base::DictionaryValue* jwk_dict) { |
- DCHECK(jwk_dict); |
- jwk_dict->SetString("kty", "oct"); |
- // For a secret/symmetric key, the only extra JWK field is 'k', containing the |
- // base64url encoding of the raw key. |
- const base::StringPiece key_str( |
- reinterpret_cast<const char*>(Uint8VectorStart(raw_key)), raw_key.size()); |
- jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str)); |
-} |
- |
-// Writes an RSA public key to a JWK dictionary |
-void WriteRsaPublicKey(const std::vector<uint8>& modulus, |
- const std::vector<uint8>& public_exponent, |
- base::DictionaryValue* jwk_dict) { |
- DCHECK(jwk_dict); |
- DCHECK(modulus.size()); |
- DCHECK(public_exponent.size()); |
- jwk_dict->SetString("kty", "RSA"); |
- jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus)); |
- jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent)); |
+Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { |
+ // JWK "ext" (optional) --> extractable parameter |
+ bool jwk_ext_value = false; |
+ bool has_jwk_ext; |
+ Status status = GetOptionalJwkBool(dict, "ext", &jwk_ext_value, &has_jwk_ext); |
+ if (status.IsError()) |
+ return status; |
+ if (has_jwk_ext && expected_extractable && !jwk_ext_value) |
+ return Status::ErrorJwkExtInconsistent(); |
+ return Status::Success(); |
} |
-// 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); |
+Status VerifyUsages(base::DictionaryValue* dict, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask) { |
+ // JWK "key_ops" (optional) --> usage_mask parameter |
+ base::ListValue* jwk_key_ops_value = NULL; |
+ bool has_jwk_key_ops; |
+ Status status = |
+ GetOptionalJwkList(dict, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
if (status.IsError()) |
return status; |
+ blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
+ if (has_jwk_key_ops) { |
+ status = |
+ GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
+ if (status.IsError()) |
+ return status; |
+ // The input usage_mask must be a subset of jwk_key_ops_mask. |
+ if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usage_mask)) |
+ return Status::ErrorJwkKeyopsInconsistent(); |
+ } |
- // 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); |
+ // JWK "use" (optional) --> usage_mask parameter |
+ std::string jwk_use_value; |
+ bool has_jwk_use; |
+ status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); |
if (status.IsError()) |
return status; |
+ blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
+ if (has_jwk_use) { |
+ if (jwk_use_value == "enc") |
+ jwk_use_mask = kJwkEncUsage; |
+ else if (jwk_use_value == "sig") |
+ jwk_use_mask = kJwkSigUsage; |
+ else |
+ return Status::ErrorJwkUnrecognizedUse(); |
+ // The input usage_mask must be a subset of jwk_use_mask. |
+ if (!ContainsKeyUsages(jwk_use_mask, expected_usage_mask)) |
+ return Status::ErrorJwkUseInconsistent(); |
+ } |
- 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)); |
+ // If both 'key_ops' and 'use' are present, ensure they are consistent. |
+ if (has_jwk_key_ops && has_jwk_use && |
+ !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
+ return Status::ErrorJwkUseAndKeyopsInconsistent(); |
return Status::Success(); |
} |
-// Writes a Web Crypto usage mask to a JWK dictionary. |
-void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages, |
- base::DictionaryValue* jwk_dict) { |
- jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages)); |
-} |
+Status VerifyAlg(base::DictionaryValue* dict, |
+ const std::string& expected_algorithm) { |
+ // JWK "alg" --> algorithm parameter |
+ bool has_jwk_alg; |
+ std::string jwk_alg_value; |
+ Status status = |
+ GetOptionalJwkString(dict, "alg", &jwk_alg_value, &has_jwk_alg); |
+ if (status.IsError()) |
+ return status; |
-// Writes a Web Crypto extractable value to a JWK dictionary. |
-void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) { |
- jwk_dict->SetBoolean("ext", extractable); |
-} |
+ if (has_jwk_alg && jwk_alg_value != expected_algorithm) |
+ return Status::ErrorJwkAlgorithmInconsistent(); |
-// Writes a Web Crypto algorithm to a JWK dictionary. |
-Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, |
- base::DictionaryValue* jwk_dict) { |
- switch (algorithm.paramsType()) { |
- case blink::WebCryptoKeyAlgorithmParamsTypeAes: { |
- DCHECK(algorithm.aesParams()); |
- const char* aes_prefix = ""; |
- switch (algorithm.aesParams()->lengthBits()) { |
- case 128: |
- aes_prefix = "A128"; |
- break; |
- case 192: |
- aes_prefix = "A192"; |
- break; |
- case 256: |
- aes_prefix = "A256"; |
- break; |
- default: |
- NOTREACHED(); // bad key length means algorithm was built improperly |
- return Status::ErrorUnexpected(); |
- } |
- const char* aes_suffix = ""; |
- switch (algorithm.id()) { |
- case blink::WebCryptoAlgorithmIdAesCbc: |
- aes_suffix = "CBC"; |
- break; |
- case blink::WebCryptoAlgorithmIdAesCtr: |
- aes_suffix = "CTR"; |
- break; |
- case blink::WebCryptoAlgorithmIdAesGcm: |
- aes_suffix = "GCM"; |
- break; |
- case blink::WebCryptoAlgorithmIdAesKw: |
- aes_suffix = "KW"; |
- break; |
- default: |
- return Status::ErrorUnsupported(); |
- } |
- jwk_dict->SetString("alg", |
- base::StringPrintf("%s%s", aes_prefix, aes_suffix)); |
- break; |
- } |
- case blink::WebCryptoKeyAlgorithmParamsTypeHmac: { |
- DCHECK(algorithm.hmacParams()); |
- switch (algorithm.hmacParams()->hash().id()) { |
- case blink::WebCryptoAlgorithmIdSha1: |
- jwk_dict->SetString("alg", "HS1"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha256: |
- jwk_dict->SetString("alg", "HS256"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha384: |
- jwk_dict->SetString("alg", "HS384"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha512: |
- jwk_dict->SetString("alg", "HS512"); |
- break; |
- default: |
- NOTREACHED(); |
- return Status::ErrorUnexpected(); |
- } |
- break; |
- } |
- case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: |
- switch (algorithm.id()) { |
- case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: { |
- switch (algorithm.rsaHashedParams()->hash().id()) { |
- case blink::WebCryptoAlgorithmIdSha1: |
- jwk_dict->SetString("alg", "RS1"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha256: |
- jwk_dict->SetString("alg", "RS256"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha384: |
- jwk_dict->SetString("alg", "RS384"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha512: |
- jwk_dict->SetString("alg", "RS512"); |
- break; |
- default: |
- NOTREACHED(); |
- return Status::ErrorUnexpected(); |
- } |
- break; |
- } |
- case blink::WebCryptoAlgorithmIdRsaOaep: { |
- switch (algorithm.rsaHashedParams()->hash().id()) { |
- case blink::WebCryptoAlgorithmIdSha1: |
- jwk_dict->SetString("alg", "RSA-OAEP"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha256: |
- jwk_dict->SetString("alg", "RSA-OAEP-256"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha384: |
- jwk_dict->SetString("alg", "RSA-OAEP-384"); |
- break; |
- case blink::WebCryptoAlgorithmIdSha512: |
- jwk_dict->SetString("alg", "RSA-OAEP-512"); |
- break; |
- default: |
- NOTREACHED(); |
- return Status::ErrorUnexpected(); |
- } |
- break; |
- } |
- default: |
- NOTREACHED(); |
- return Status::ErrorUnexpected(); |
- } |
- break; |
- default: |
- return Status::ErrorUnsupported(); |
- } |
return Status::Success(); |
} |
-bool IsRsaKey(const blink::WebCryptoKey& key) { |
- return IsAlgorithmRsa(key.algorithm().id()); |
-} |
+Status ParseJwkCommon(const CryptoData& bytes, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask, |
+ std::string* kty, |
+ scoped_ptr<base::DictionaryValue>* dict) { |
+ // Parse the incoming JWK JSON. |
+ base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), |
+ bytes.byte_length()); |
-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; |
+ scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
+ base::DictionaryValue* dict_value = NULL; |
- bool is_public_key = !dict->HasKey("d"); |
+ if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
+ return Status::ErrorJwkNotDictionary(); |
- // Now that the key type is known, do an additional check on the usages to |
- // make sure they are all applicable for this algorithm + key type. |
- status = CheckKeyUsages(algorithm.id(), |
- is_public_key ? blink::WebCryptoKeyTypePublic |
- : blink::WebCryptoKeyTypePrivate, |
- usage_mask); |
+ // Release |value|, as ownership will be transferred to |dict| via |
+ // |dict_value|, which points to the same object as |value|. |
+ ignore_result(value.release()); |
+ dict->reset(dict_value); |
+ // JWK "kty". Exit early if this required JWK parameter is missing. |
+ Status status = GetJwkString(dict_value, "kty", kty); |
if (status.IsError()) |
return status; |
- if (is_public_key) { |
- return platform::ImportRsaPublicKey(algorithm, |
- extractable, |
- usage_mask, |
- CryptoData(jwk_n_value), |
- CryptoData(jwk_e_value), |
- key); |
- } |
+ status = VerifyExt(dict_value, expected_extractable); |
+ if (status.IsError()) |
+ return status; |
- std::string jwk_d_value; |
- status = GetJwkBytes(dict, "d", &jwk_d_value); |
+ status = VerifyUsages(dict_value, expected_usage_mask); |
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. |
+ return Status::Success(); |
+} |
- std::string jwk_p_value; |
- bool has_p; |
- status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p); |
+Status ReadSecretKeyNoExpectedAlg( |
+ const CryptoData& key_data, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask, |
+ std::vector<uint8>* raw_key_data, |
+ scoped_ptr<base::DictionaryValue>* dict) { |
+ if (!key_data.byte_length()) |
+ return Status::ErrorImportEmptyKeyData(); |
+ |
+ std::string kty; |
+ Status status = ParseJwkCommon( |
+ key_data, expected_extractable, expected_usage_mask, &kty, dict); |
if (status.IsError()) |
return status; |
- std::string jwk_q_value; |
- bool has_q; |
- status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q); |
+ if (kty != "oct") |
+ return Status::ErrorJwkUnexpectedKty("oct"); |
+ |
+ std::string jwk_k_value; |
+ status = GetJwkBytes(dict->get(), "k", &jwk_k_value); |
if (status.IsError()) |
return status; |
+ raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); |
- std::string jwk_dp_value; |
- bool has_dp; |
- status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp); |
+ return Status::Success(); |
+} |
+ |
+} // namespace |
+ |
+void WriteSecretKeyJwk(const CryptoData& raw_key_data, |
+ const std::string& algorithm, |
+ bool extractable, |
+ blink::WebCryptoKeyUsageMask usage_mask, |
+ std::vector<uint8>* jwk_key_data) { |
+ JwkWriter writer(algorithm, extractable, usage_mask, "oct"); |
+ writer.SetBase64Encoded("k", raw_key_data); |
+ writer.ToBytes(jwk_key_data); |
+} |
+ |
+Status ReadSecretKeyJwk(const CryptoData& key_data, |
+ const std::string& expected_algorithm, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask, |
+ std::vector<uint8>* raw_key_data) { |
+ scoped_ptr<base::DictionaryValue> dict; |
+ Status status = ReadSecretKeyNoExpectedAlg( |
+ key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); |
if (status.IsError()) |
return status; |
+ return VerifyAlg(dict.get(), expected_algorithm); |
+} |
- std::string jwk_dq_value; |
- bool has_dq; |
- status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq); |
+std::string MakeJwkAesAlgorithmName(const std::string& suffix, |
+ unsigned int keylen_bytes) { |
+ if (keylen_bytes == 16) |
+ return std::string("A128") + suffix; |
+ if (keylen_bytes == 24) |
+ return std::string("A192") + suffix; |
+ if (keylen_bytes == 32) |
+ return std::string("A256") + suffix; |
+ return std::string(); |
+} |
+ |
+Status ReadAesSecretKeyJwk(const CryptoData& key_data, |
+ const std::string& algorithm_name_suffix, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask, |
+ std::vector<uint8>* raw_key_data) { |
+ scoped_ptr<base::DictionaryValue> dict; |
+ Status status = ReadSecretKeyNoExpectedAlg( |
+ key_data, expected_extractable, expected_usage_mask, raw_key_data, &dict); |
if (status.IsError()) |
return status; |
- std::string jwk_qi_value; |
- bool has_qi; |
- status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi); |
+ bool has_jwk_alg; |
+ std::string jwk_alg; |
+ status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); |
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(); |
+ if (has_jwk_alg) { |
+ std::string expected_algorithm_name = |
+ MakeJwkAesAlgorithmName(algorithm_name_suffix, raw_key_data->size()); |
+ |
+ if (jwk_alg != expected_algorithm_name) { |
+ // Give a different error message if the key length was wrong. |
+ if (jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 16) || |
+ jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 24) || |
+ jwk_alg == MakeJwkAesAlgorithmName(algorithm_name_suffix, 32)) { |
+ return Status::ErrorJwkIncorrectKeyLength(); |
+ } |
+ return Status::ErrorJwkAlgorithmInconsistent(); |
+ } |
+ } |
- 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); |
+ return Status::Success(); |
} |
-} // namespace |
+// Writes an RSA public key to a JWK dictionary |
+void WriteRsaPublicKeyJwk(const CryptoData& n, |
+ const CryptoData& e, |
+ const std::string& algorithm, |
+ bool extractable, |
+ blink::WebCryptoKeyUsageMask usage_mask, |
+ std::vector<uint8>* jwk_key_data) { |
+ JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
+ writer.SetBase64Encoded("n", n); |
+ writer.SetBase64Encoded("e", e); |
+ writer.ToBytes(jwk_key_data); |
+} |
+ |
+// Writes an RSA private key to a JWK dictionary |
+void WriteRsaPrivateKeyJwk(const CryptoData& n, |
+ const CryptoData& e, |
+ const CryptoData& d, |
+ const CryptoData& p, |
+ const CryptoData& q, |
+ const CryptoData& dp, |
+ const CryptoData& dq, |
+ const CryptoData& qi, |
+ const std::string& algorithm, |
+ bool extractable, |
+ blink::WebCryptoKeyUsageMask usage_mask, |
+ std::vector<uint8>* jwk_key_data) { |
+ JwkWriter writer(algorithm, extractable, usage_mask, "RSA"); |
+ |
+ writer.SetBase64Encoded("n", n); |
+ writer.SetBase64Encoded("e", e); |
+ writer.SetBase64Encoded("d", d); |
+ // Although these are "optional" in the JWA, WebCrypto spec requires them to |
+ // be emitted. |
+ writer.SetBase64Encoded("p", p); |
+ writer.SetBase64Encoded("q", q); |
+ writer.SetBase64Encoded("dp", dp); |
+ writer.SetBase64Encoded("dq", dq); |
+ writer.SetBase64Encoded("qi", qi); |
+ writer.ToBytes(jwk_key_data); |
+} |
+ |
+JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { |
+} |
-// TODO(eroman): Split this up into smaller functions. |
-Status ImportKeyJwk(const CryptoData& key_data, |
- const blink::WebCryptoAlgorithm& algorithm, |
- bool extractable, |
- blink::WebCryptoKeyUsageMask usage_mask, |
- blink::WebCryptoKey* key) { |
+JwkRsaInfo::~JwkRsaInfo() { |
+} |
+ |
+Status ReadRsaKeyJwk(const CryptoData& key_data, |
+ const std::string& expected_algorithm, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usage_mask, |
+ JwkRsaInfo* result) { |
if (!key_data.byte_length()) |
return Status::ErrorImportEmptyKeyData(); |
- DCHECK(key); |
- // Parse the incoming JWK JSON. |
- base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()), |
- key_data.byte_length()); |
- scoped_ptr<base::Value> value(base::JSONReader::Read(json_string)); |
- // Note, bare pointer dict_value is ok since it points into scoped value. |
- base::DictionaryValue* dict_value = NULL; |
- if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value) |
- return Status::ErrorJwkNotDictionary(); |
+ scoped_ptr<base::DictionaryValue> dict; |
+ std::string kty; |
+ Status status = ParseJwkCommon( |
+ key_data, expected_extractable, expected_usage_mask, &kty, &dict); |
+ if (status.IsError()) |
+ return status; |
- // JWK "kty". Exit early if this required JWK parameter is missing. |
- std::string jwk_kty_value; |
- Status status = GetJwkString(dict_value, "kty", &jwk_kty_value); |
+ status = VerifyAlg(dict.get(), expected_algorithm); |
if (status.IsError()) |
return status; |
- // JWK "ext" (optional) --> extractable parameter |
- { |
- bool jwk_ext_value = false; |
- bool has_jwk_ext; |
- status = |
- GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext); |
- if (status.IsError()) |
- return status; |
- if (has_jwk_ext && !jwk_ext_value && extractable) |
- return Status::ErrorJwkExtInconsistent(); |
- } |
+ if (kty != "RSA") |
+ return Status::ErrorJwkUnexpectedKty("RSA"); |
- // JWK "alg" --> algorithm parameter |
- // 1. JWK alg present but unrecognized: error |
- // 2. JWK alg valid and inconsistent with input algorithm: error |
- // 3. JWK alg valid and consistent with input algorithm: use input value |
- // 4. JWK alg is missing: use input value |
- const JwkAlgorithmInfo* algorithm_info = NULL; |
- std::string jwk_alg_value; |
- bool has_jwk_alg; |
- status = |
- GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg); |
+ // 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. |
+ status = GetJwkBytes(dict.get(), "n", &result->n); |
+ if (status.IsError()) |
+ return status; |
+ status = GetJwkBytes(dict.get(), "e", &result->e); |
if (status.IsError()) |
return status; |
- if (has_jwk_alg) { |
- // JWK alg present |
- |
- // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can |
- // only be from the RSA family. |
+ result->is_private_key = dict->HasKey("d"); |
+ if (!result->is_private_key) |
+ return Status::Success(); |
- blink::WebCryptoAlgorithm jwk_algorithm = |
- blink::WebCryptoAlgorithm::createNull(); |
- algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value); |
- if (!algorithm_info || |
- !algorithm_info->CreateImportAlgorithm(&jwk_algorithm)) |
- return Status::ErrorJwkUnrecognizedAlgorithm(); |
+ status = GetJwkBytes(dict.get(), "d", &result->d); |
+ if (status.IsError()) |
+ return status; |
- if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm)) |
- return Status::ErrorJwkAlgorithmInconsistent(); |
- } |
- DCHECK(!algorithm.isNull()); |
+ // 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. |
- // JWK "key_ops" (optional) --> usage_mask parameter |
- base::ListValue* jwk_key_ops_value = NULL; |
- bool has_jwk_key_ops; |
- status = GetOptionalJwkList( |
- dict_value, "key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
+ bool has_p; |
+ status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p); |
if (status.IsError()) |
return status; |
- blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
- if (has_jwk_key_ops) { |
- status = |
- GetWebCryptoUsagesFromJwkKeyOps(jwk_key_ops_value, &jwk_key_ops_mask); |
- if (status.IsError()) |
- return status; |
- // The input usage_mask must be a subset of jwk_key_ops_mask. |
- if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask)) |
- return Status::ErrorJwkKeyopsInconsistent(); |
- } |
- // JWK "use" (optional) --> usage_mask parameter |
- std::string jwk_use_value; |
- bool has_jwk_use; |
- status = |
- GetOptionalJwkString(dict_value, "use", &jwk_use_value, &has_jwk_use); |
+ bool has_q; |
+ status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q); |
if (status.IsError()) |
return status; |
- blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
- if (has_jwk_use) { |
- if (jwk_use_value == "enc") |
- jwk_use_mask = kJwkEncUsage; |
- else if (jwk_use_value == "sig") |
- jwk_use_mask = kJwkSigUsage; |
- else |
- return Status::ErrorJwkUnrecognizedUse(); |
- // The input usage_mask must be a subset of jwk_use_mask. |
- if (!ContainsKeyUsages(jwk_use_mask, usage_mask)) |
- return Status::ErrorJwkUseInconsistent(); |
- } |
- // If both 'key_ops' and 'use' are present, ensure they are consistent. |
- if (has_jwk_key_ops && has_jwk_use && |
- !ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask)) |
- return Status::ErrorJwkUseAndKeyopsInconsistent(); |
- |
- // JWK keying material --> ImportKeyInternal() |
- if (jwk_kty_value == "oct") { |
- std::string jwk_k_value; |
- status = GetJwkBytes(dict_value, "k", &jwk_k_value); |
- if (status.IsError()) |
- return status; |
+ bool has_dp; |
+ status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp); |
+ if (status.IsError()) |
+ return status; |
- // Some JWK alg ID's embed information about the key length in the alg ID |
- // string. For example "A128CBC" implies the JWK carries 128 bits |
- // of key material. For such keys validate that enough bytes were provided. |
- // If this validation is not done, then it would be possible to select a |
- // different algorithm by passing a different lengthed key, since that is |
- // how WebCrypto interprets things. |
- if (algorithm_info && |
- algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) { |
- return Status::ErrorJwkIncorrectKeyLength(); |
- } |
+ bool has_dq; |
+ status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq); |
+ if (status.IsError()) |
+ return status; |
- return ImportKey(blink::WebCryptoKeyFormatRaw, |
- CryptoData(jwk_k_value), |
- algorithm, |
- extractable, |
- usage_mask, |
- key); |
- } |
+ bool has_qi; |
+ status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi); |
+ if (status.IsError()) |
+ return status; |
- if (jwk_kty_value == "RSA") |
- return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key); |
+ 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 Status::ErrorJwkUnrecognizedKty(); |
+ return Status::Success(); |
} |
-Status ExportKeyJwk(const blink::WebCryptoKey& key, |
- std::vector<uint8>* buffer) { |
- DCHECK(key.extractable()); |
- base::DictionaryValue jwk_dict; |
- Status status = Status::OperationError(); |
- |
- switch (key.type()) { |
- case blink::WebCryptoKeyTypeSecret: { |
- std::vector<uint8> exported_key; |
- status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key); |
- if (status.IsError()) |
- return status; |
- WriteSecretKey(exported_key, &jwk_dict); |
- break; |
- } |
- case blink::WebCryptoKeyTypePublic: { |
- // 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); |
- if (status.IsError()) |
- return status; |
- std::vector<uint8> modulus; |
- std::vector<uint8> public_exponent; |
- status = |
- platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent); |
- if (status.IsError()) |
- return status; |
- WriteRsaPublicKey(modulus, public_exponent, &jwk_dict); |
- break; |
- } |
- 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; |
- } |
- |
+const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash) { |
+ switch (hash) { |
+ case blink::WebCryptoAlgorithmIdSha1: |
+ return "HS1"; |
+ case blink::WebCryptoAlgorithmIdSha256: |
+ return "HS256"; |
+ case blink::WebCryptoAlgorithmIdSha384: |
+ return "HS384"; |
+ case blink::WebCryptoAlgorithmIdSha512: |
+ return "HS512"; |
default: |
- return Status::ErrorUnsupported(); |
+ return NULL; |
} |
- |
- WriteKeyOps(key.usages(), &jwk_dict); |
- WriteExt(key.extractable(), &jwk_dict); |
- status = WriteAlg(key.algorithm(), &jwk_dict); |
- if (status.IsError()) |
- return status; |
- |
- std::string json; |
- base::JSONWriter::Write(&jwk_dict, &json); |
- buffer->assign(json.data(), json.data() + json.size()); |
- return Status::Success(); |
} |
} // namespace webcrypto |