#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
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 |
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:
- enum { 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_t>* 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.
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);
+// Extracts the required base64url property, which is interpreted as being a
+// big-endian unsigned integer.
+Status GetJwkBigInteger(base::DictionaryValue* dict,
+ const std::string& path,
+ std::string* result) {
+ Status status = GetJwkBytes(dict, path, result);
if (status.IsError())
return status;
- if (!*property_exists) {
- result->clear();
- return Status::Success();
- }
+ if (result->empty())
+ return Status::ErrorJwkEmptyBigInteger(path);
- if (!Base64DecodeUrlSafe(base64_string, result))
- return Status::ErrorJwkBase64Decode(path);
+ // The JWA spec says that "The octet sequence MUST utilize the minimum number
+ // of octets to represent the value." This means there shouldn't be any
+ // leading zeros.
+ if (result->size() > 1 && (*result)[0] == 0)
+ return Status::ErrorJwkBigIntegerHasLeadingZero(path);
return Status::Success();
}
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);
- }
-
- std::string jwk_d_value;
- status = GetJwkBytes(dict, "d", &jwk_d_value);
+ status = VerifyExt(dict_value, expected_extractable);
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);
+ status = VerifyUsages(dict_value, expected_usage_mask);
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;
+ return Status::Success();
+}
- std::string jwk_dp_value;
- bool has_dp;
- status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp);
- if (status.IsError())
- return status;
+Status ReadSecretKeyNoExpectedAlg(
+ const CryptoData& key_data,
+ bool expected_extractable,
+ blink::WebCryptoKeyUsageMask expected_usage_mask,
+ std::vector<uint8_t>* raw_key_data,
+ scoped_ptr<base::DictionaryValue>* dict) {
+ if (!key_data.byte_length())
+ return Status::ErrorImportEmptyKeyData();
- std::string jwk_dq_value;
- bool has_dq;
- status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq);
+ std::string kty;
+ Status status = ParseJwkCommon(
+ key_data, expected_extractable, expected_usage_mask, &kty, dict);
if (status.IsError())
return status;
- std::string jwk_qi_value;
- bool has_qi;
- status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi);
+ 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());
- 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);
+ return Status::Success();
}
} // namespace
-// 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) {
- 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();
+void WriteSecretKeyJwk(const CryptoData& raw_key_data,
+ const std::string& algorithm,
+ bool extractable,
+ blink::WebCryptoKeyUsageMask usage_mask,
+ std::vector<uint8_t>* jwk_key_data) {
+ JwkWriter writer(algorithm, extractable, usage_mask, "oct");
+ writer.SetBase64Encoded("k", raw_key_data);
+ writer.ToBytes(jwk_key_data);
+}
- // 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 ReadSecretKeyJwk(const CryptoData& key_data,
+ const std::string& expected_algorithm,
+ bool expected_extractable,
+ blink::WebCryptoKeyUsageMask expected_usage_mask,
+ std::vector<uint8_t>* 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);
+}
- // 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();
- }
+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_t>* 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;
- // 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);
+ std::string jwk_alg;
+ status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg);
if (status.IsError())
return status;
if (has_jwk_alg) {
- // JWK alg present
+ 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();
+ }
+ }
- // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
- // only be from the RSA family.
+ 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();
+// 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_t>* jwk_key_data) {
+ JwkWriter writer(algorithm, extractable, usage_mask, "RSA");
+ writer.SetBase64Encoded("n", n);
+ writer.SetBase64Encoded("e", e);
+ writer.ToBytes(jwk_key_data);
+}
- if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm))
- return Status::ErrorJwkAlgorithmInconsistent();
- }
- DCHECK(!algorithm.isNull());
+// 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_t>* 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);
+}
- // 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);
+JwkRsaInfo::JwkRsaInfo() : is_private_key(false) {
+}
+
+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();
+
+ 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;
- 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);
+ status = VerifyAlg(dict.get(), expected_algorithm);
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();
+ if (kty != "RSA")
+ return Status::ErrorJwkUnexpectedKty("RSA");
- // 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;
+ // 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 = GetJwkBigInteger(dict.get(), "n", &result->n);
+ if (status.IsError())
+ return status;
+ status = GetJwkBigInteger(dict.get(), "e", &result->e);
+ 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();
- }
+ result->is_private_key = dict->HasKey("d");
+ if (!result->is_private_key)
+ return Status::Success();
- return ImportKey(blink::WebCryptoKeyFormatRaw,
- CryptoData(jwk_k_value),
- algorithm,
- extractable,
- usage_mask,
- key);
- }
+ status = GetJwkBigInteger(dict.get(), "d", &result->d);
+ if (status.IsError())
+ return status;
- if (jwk_kty_value == "RSA")
- return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key);
+ // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA
+ // spec. However they are required by Chromium's WebCrypto implementation.
- return Status::ErrorJwkUnrecognizedKty();
-}
+ status = GetJwkBigInteger(dict.get(), "p", &result->p);
+ if (status.IsError())
+ return status;
-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;
- }
+ status = GetJwkBigInteger(dict.get(), "q", &result->q);
+ if (status.IsError())
+ return status;
- default:
- return Status::ErrorUnsupported();
- }
+ status = GetJwkBigInteger(dict.get(), "dp", &result->dp);
+ if (status.IsError())
+ return status;
+
+ status = GetJwkBigInteger(dict.get(), "dq", &result->dq);
+ if (status.IsError())
+ return status;
- WriteKeyOps(key.usages(), &jwk_dict);
- WriteExt(key.extractable(), &jwk_dict);
- status = WriteAlg(key.algorithm(), &jwk_dict);
+ status = GetJwkBigInteger(dict.get(), "qi", &result->qi);
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();
}
+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 NULL;
+ }
+}
+
} // namespace webcrypto
} // namespace content