| Index: content/child/webcrypto/jwk.cc
|
| diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc
|
| index b6e8731933ae57a9ed2f534569df64250434e961..24683aa6d8da89535cf6dccd562b713792d4b3b7 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"
|
| -
|
| -// 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.
|
| +#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
|
|
|
| // JSON Web Key Format (JWK)
|
| // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21
|
| @@ -209,6 +209,22 @@ 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 |
|
| @@ -218,39 +234,165 @@ const blink::WebCryptoKeyUsageMask kJwkEncUsage =
|
| const blink::WebCryptoKeyUsageMask kJwkSigUsage =
|
| blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
|
|
|
| -class JwkWriter {
|
| +typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
|
| +
|
| +class JwkAlgorithmInfo {
|
| public:
|
| - 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);
|
| + 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();
|
| }
|
|
|
| - void Set(const std::string& key, const std::string& value) {
|
| - dict_.SetString(key, value);
|
| + 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 SetBase64Encoded(const std::string& key, const CryptoData& value) {
|
| - dict_.SetString(key,
|
| - Base64EncodeUrlSafe(base::StringPiece(
|
| - reinterpret_cast<const char*>(value.bytes()),
|
| - value.byte_length())));
|
| + 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 ToBytes(std::vector<uint8>* utf8_bytes) {
|
| - std::string json;
|
| - base::JSONWriter::Write(&dict_, &json);
|
| - utf8_bytes->assign(json.begin(), json.end());
|
| + // 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;
|
| }
|
|
|
| private:
|
| - base::DictionaryValue dict_;
|
| + // 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::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.
|
| @@ -367,353 +509,508 @@ Status GetOptionalJwkBool(base::DictionaryValue* dict,
|
| return Status::Success();
|
| }
|
|
|
| -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 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));
|
| }
|
|
|
| -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);
|
| +// 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));
|
| +}
|
| +
|
| +// 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;
|
| - 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();
|
| - }
|
|
|
| - // 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);
|
| + // 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;
|
| - 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();
|
| - }
|
|
|
| - // 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_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();
|
| }
|
|
|
| -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 usage mask to a JWK dictionary.
|
| +void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages,
|
| + base::DictionaryValue* jwk_dict) {
|
| + jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages));
|
| +}
|
|
|
| - if (has_jwk_alg && jwk_alg_value != expected_algorithm)
|
| - return Status::ErrorJwkAlgorithmInconsistent();
|
| +// Writes a Web Crypto extractable value to a JWK dictionary.
|
| +void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) {
|
| + jwk_dict->SetBoolean("ext", extractable);
|
| +}
|
|
|
| +// 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();
|
| }
|
|
|
| -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());
|
| -
|
| - scoped_ptr<base::Value> value(base::JSONReader::Read(json_string));
|
| - base::DictionaryValue* dict_value = NULL;
|
| -
|
| - if (!value.get() || !value->GetAsDictionary(&dict_value) || !dict_value)
|
| - return Status::ErrorJwkNotDictionary();
|
| -
|
| - // 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;
|
| +bool IsRsaKey(const blink::WebCryptoKey& key) {
|
| + return IsAlgorithmRsa(key.algorithm().id());
|
| +}
|
|
|
| - status = VerifyExt(dict_value, expected_extractable);
|
| +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;
|
| -
|
| - status = VerifyUsages(dict_value, expected_usage_mask);
|
| + std::string jwk_e_value;
|
| + status = GetJwkBytes(dict, "e", &jwk_e_value);
|
| if (status.IsError())
|
| return status;
|
|
|
| - return Status::Success();
|
| -}
|
| + bool is_public_key = !dict->HasKey("d");
|
|
|
| -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();
|
| + // 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);
|
|
|
| - std::string kty;
|
| - Status status = ParseJwkCommon(
|
| - key_data, expected_extractable, expected_usage_mask, &kty, dict);
|
| if (status.IsError())
|
| return status;
|
|
|
| - if (kty != "oct")
|
| - return Status::ErrorJwkUnexpectedKty("oct");
|
| + if (is_public_key) {
|
| + return platform::ImportRsaPublicKey(algorithm,
|
| + extractable,
|
| + usage_mask,
|
| + CryptoData(jwk_n_value),
|
| + CryptoData(jwk_e_value),
|
| + key);
|
| + }
|
|
|
| - std::string jwk_k_value;
|
| - status = GetJwkBytes(dict->get(), "k", &jwk_k_value);
|
| + std::string jwk_d_value;
|
| + status = GetJwkBytes(dict, "d", &jwk_d_value);
|
| if (status.IsError())
|
| return status;
|
| - raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end());
|
|
|
| - 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);
|
| -}
|
| + // 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.
|
|
|
| -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);
|
| + std::string jwk_p_value;
|
| + bool has_p;
|
| + status = GetOptionalJwkBytes(dict, "p", &jwk_p_value, &has_p);
|
| if (status.IsError())
|
| return status;
|
| - return VerifyAlg(dict.get(), expected_algorithm);
|
| -}
|
| -
|
| -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);
|
| + std::string jwk_q_value;
|
| + bool has_q;
|
| + status = GetOptionalJwkBytes(dict, "q", &jwk_q_value, &has_q);
|
| if (status.IsError())
|
| return status;
|
|
|
| - bool has_jwk_alg;
|
| - std::string jwk_alg;
|
| - status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg);
|
| + std::string jwk_dp_value;
|
| + bool has_dp;
|
| + status = GetOptionalJwkBytes(dict, "dp", &jwk_dp_value, &has_dp);
|
| if (status.IsError())
|
| return status;
|
|
|
| - 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 Status::Success();
|
| -}
|
| + std::string jwk_dq_value;
|
| + bool has_dq;
|
| + status = GetOptionalJwkBytes(dict, "dq", &jwk_dq_value, &has_dq);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| -// 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);
|
| -}
|
| + std::string jwk_qi_value;
|
| + bool has_qi;
|
| + status = GetOptionalJwkBytes(dict, "qi", &jwk_qi_value, &has_qi);
|
| + if (status.IsError())
|
| + return status;
|
|
|
| -// 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);
|
| -}
|
| + 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();
|
|
|
| -JwkRsaInfo::JwkRsaInfo() : is_private_key(false) {
|
| + 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);
|
| }
|
|
|
| -JwkRsaInfo::~JwkRsaInfo() {
|
| -}
|
| +} // namespace
|
|
|
| -Status ReadRsaKeyJwk(const CryptoData& key_data,
|
| - const std::string& expected_algorithm,
|
| - bool expected_extractable,
|
| - blink::WebCryptoKeyUsageMask expected_usage_mask,
|
| - JwkRsaInfo* result) {
|
| +// 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);
|
|
|
| - 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;
|
| + // 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();
|
|
|
| - status = VerifyAlg(dict.get(), expected_algorithm);
|
| + // 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);
|
| if (status.IsError())
|
| return status;
|
|
|
| - if (kty != "RSA")
|
| - return Status::ErrorJwkUnexpectedKty("RSA");
|
| + // 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();
|
| + }
|
|
|
| - // 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);
|
| + // 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);
|
| if (status.IsError())
|
| return status;
|
|
|
| - result->is_private_key = dict->HasKey("d");
|
| - if (!result->is_private_key)
|
| - return Status::Success();
|
| + if (has_jwk_alg) {
|
| + // JWK alg present
|
|
|
| - status = GetJwkBytes(dict.get(), "d", &result->d);
|
| - if (status.IsError())
|
| - return status;
|
| + // TODO(padolph): Validate alg vs kty. For example kty="RSA" implies alg can
|
| + // only be from the RSA family.
|
|
|
| - // 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.
|
| + 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();
|
|
|
| - bool has_p;
|
| - status = GetOptionalJwkBytes(dict.get(), "p", &result->p, &has_p);
|
| - if (status.IsError())
|
| - return status;
|
| + if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm))
|
| + return Status::ErrorJwkAlgorithmInconsistent();
|
| + }
|
| + DCHECK(!algorithm.isNull());
|
|
|
| - bool has_q;
|
| - status = GetOptionalJwkBytes(dict.get(), "q", &result->q, &has_q);
|
| + // 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);
|
| 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();
|
| + }
|
|
|
| - bool has_dp;
|
| - status = GetOptionalJwkBytes(dict.get(), "dp", &result->dp, &has_dp);
|
| + // 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);
|
| 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();
|
| + }
|
|
|
| - bool has_dq;
|
| - status = GetOptionalJwkBytes(dict.get(), "dq", &result->dq, &has_dq);
|
| - if (status.IsError())
|
| - return status;
|
| + // 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();
|
|
|
| - bool has_qi;
|
| - status = GetOptionalJwkBytes(dict.get(), "qi", &result->qi, &has_qi);
|
| - if (status.IsError())
|
| - return status;
|
| + // 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;
|
|
|
| - 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();
|
| + // 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();
|
| + }
|
|
|
| - return Status::Success();
|
| + return ImportKey(blink::WebCryptoKeyFormatRaw,
|
| + CryptoData(jwk_k_value),
|
| + algorithm,
|
| + extractable,
|
| + usage_mask,
|
| + key);
|
| + }
|
| +
|
| + if (jwk_kty_value == "RSA")
|
| + return ImportRsaKey(dict_value, algorithm, extractable, usage_mask, key);
|
| +
|
| + return Status::ErrorJwkUnrecognizedKty();
|
| }
|
|
|
| -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";
|
| +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;
|
| + }
|
| +
|
| default:
|
| - return NULL;
|
| + return Status::ErrorUnsupported();
|
| }
|
| +
|
| + 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
|
|
|