| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "jwk.h" |
| |
| #include <algorithm> |
| #include <functional> |
| #include <map> |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/strings/string_piece.h" |
| #include "content/child/webcrypto/crypto_data.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. |
| |
| // JSON Web Key Format (JWK) |
| // http://tools.ietf.org/html/draft-ietf-jose-json-web-key-21 |
| // |
| // A JWK is a simple JSON dictionary with the following entries |
| // - "kty" (Key Type) Parameter, REQUIRED |
| // - <kty-specific parameters, see below>, REQUIRED |
| // - "use" (Key Use) Parameter, OPTIONAL |
| // - "key_ops" (Key Operations) Parameter, OPTIONAL |
| // - "alg" (Algorithm) Parameter, OPTIONAL |
| // - "ext" (Key Exportability), OPTIONAL |
| // (all other entries are ignored) |
| // |
| // OPTIONAL here means that this code does not require the entry to be present |
| // in the incoming JWK, because the method input parameters contain similar |
| // information. If the optional JWK entry is present, it will be validated |
| // against the corresponding input parameter for consistency and combined with |
| // it according to rules defined below. |
| // |
| // Input 'key_data' contains the JWK. To build a Web Crypto Key, the JWK |
| // values are parsed out and combined with the method input parameters to |
| // build a Web Crypto Key: |
| // Web Crypto Key type <-- (deduced) |
| // Web Crypto Key extractable <-- JWK ext + input extractable |
| // Web Crypto Key algorithm <-- JWK alg + input algorithm |
| // Web Crypto Key keyUsage <-- JWK use, key_ops + input usage_mask |
| // Web Crypto Key keying material <-- kty-specific parameters |
| // |
| // Values for each JWK entry are case-sensitive and defined in |
| // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18. |
| // Note that not all values specified by JOSE are handled by this code. Only |
| // handled values are listed. |
| // - kty (Key Type) |
| // +-------+--------------------------------------------------------------+ |
| // | "RSA" | RSA [RFC3447] | |
| // | "oct" | Octet sequence (used to represent symmetric keys) | |
| // +-------+--------------------------------------------------------------+ |
| // |
| // - key_ops (Key Use Details) |
| // The key_ops field is an array that contains one or more strings from |
| // the table below, and describes the operations for which this key may be |
| // used. |
| // +-------+--------------------------------------------------------------+ |
| // | "encrypt" | encrypt operations | |
| // | "decrypt" | decrypt operations | |
| // | "sign" | sign (MAC) operations | |
| // | "verify" | verify (MAC) operations | |
| // | "wrapKey" | key wrap | |
| // | "unwrapKey" | key unwrap | |
| // | "deriveKey" | key derivation | |
| // | "deriveBits" | key derivation | |
| // +-------+--------------------------------------------------------------+ |
| // |
| // - use (Key Use) |
| // The use field contains a single entry from the table below. |
| // +-------+--------------------------------------------------------------+ |
| // | "sig" | equivalent to key_ops of [sign, verify] | |
| // | "enc" | equivalent to key_ops of [encrypt, decrypt, wrapKey, | |
| // | | unwrapKey, deriveKey, deriveBits] | |
| // +-------+--------------------------------------------------------------+ |
| // |
| // NOTE: If both "use" and "key_ops" JWK members are present, the usages |
| // specified by them MUST be consistent. In particular, the "use" value |
| // "sig" corresponds to "sign" and/or "verify". The "use" value "enc" |
| // corresponds to all other values defined above. If "key_ops" values |
| // corresponding to both "sig" and "enc" "use" values are present, the "use" |
| // member SHOULD NOT be present, and if present, its value MUST NOT be |
| // either "sig" or "enc". |
| // |
| // - ext (Key Exportability) |
| // +-------+--------------------------------------------------------------+ |
| // | true | Key may be exported from the trusted environment | |
| // | false | Key cannot exit the trusted environment | |
| // +-------+--------------------------------------------------------------+ |
| // |
| // - alg (Algorithm) |
| // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18 |
| // +--------------+-------------------------------------------------------+ |
| // | Digital Signature or MAC Algorithm | |
| // +--------------+-------------------------------------------------------+ |
| // | "HS1" | HMAC using SHA-1 hash algorithm | |
| // | "HS256" | HMAC using SHA-256 hash algorithm | |
| // | "HS384" | HMAC using SHA-384 hash algorithm | |
| // | "HS512" | HMAC using SHA-512 hash algorithm | |
| // | "RS1" | RSASSA using SHA-1 hash algorithm |
| // | "RS256" | RSASSA using SHA-256 hash algorithm | |
| // | "RS384" | RSASSA using SHA-384 hash algorithm | |
| // | "RS512" | RSASSA using SHA-512 hash algorithm | |
| // +--------------+-------------------------------------------------------| |
| // | Key Management Algorithm | |
| // +--------------+-------------------------------------------------------+ |
| // | "RSA-OAEP" | RSAES using Optimal Asymmetric Encryption Padding | |
| // | | (OAEP) [RFC3447], with the default parameters | |
| // | | specified by RFC3447 in Section A.2.1 | |
| // | "A128KW" | Advanced Encryption Standard (AES) Key Wrap Algorithm | |
| // | | [RFC3394] using 128 bit keys | |
| // | "A192KW" | AES Key Wrap Algorithm using 192 bit keys | |
| // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys | |
| // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using | |
| // | | 128 bit keys | |
| // | "A192GCM" | AES GCM using 192 bit keys | |
| // | "A256GCM" | AES GCM using 256 bit keys | |
| // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 | |
| // | | padding [NIST.800-38A] | |
| // | "A192CBC" | AES CBC using 192 bit keys | |
| // | "A256CBC" | AES CBC using 256 bit keys | |
| // +--------------+-------------------------------------------------------+ |
| // |
| // kty-specific parameters |
| // The value of kty determines the type and content of the keying material |
| // carried in the JWK to be imported. |
| // // - kty == "oct" (symmetric or other raw key) |
| // +-------+--------------------------------------------------------------+ |
| // | "k" | Contains the value of the symmetric (or other single-valued) | |
| // | | key. It is represented as the base64url encoding of the | |
| // | | octet sequence containing the key value. | |
| // +-------+--------------------------------------------------------------+ |
| // - kty == "RSA" (RSA public key) |
| // +-------+--------------------------------------------------------------+ |
| // | "n" | Contains the modulus value for the RSA public key. It is | |
| // | | represented as the base64url encoding of the value's | |
| // | | unsigned big endian representation as an octet sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "e" | Contains the exponent value for the RSA public key. It is | |
| // | | represented as the base64url encoding of the value's | |
| // | | unsigned big endian representation as an octet sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // - If key == "RSA" and the "d" parameter is present then it is a private key. |
| // All the parameters above for public keys apply, as well as the following. |
| // (Note that except for "d", all of these are optional): |
| // +-------+--------------------------------------------------------------+ |
| // | "d" | Contains the private exponent value for the RSA private key. | |
| // | | It is represented as the base64url encoding of the value's | |
| // | | unsigned big endian representation as an octet sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "p" | Contains the first prime factor value for the RSA private | |
| // | | key. It is represented as the base64url encoding of the | |
| // | | value's | |
| // | | unsigned big endian representation as an octet sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "q" | Contains the second prime factor value for the RSA private | |
| // | | key. It is represented as the base64url encoding of the | |
| // | | value's unsigned big endian representation as an octet | |
| // | | sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "dp" | Contains the first factor CRT exponent value for the RSA | |
| // | | private key. It is represented as the base64url encoding of | |
| // | | the value's unsigned big endian representation as an octet | |
| // | | sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "dq" | Contains the second factor CRT exponent value for the RSA | |
| // | | private key. It is represented as the base64url encoding of | |
| // | | the value's unsigned big endian representation as an octet | |
| // | | sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // | "dq" | Contains the first CRT coefficient value for the RSA private | |
| // | | key. It is represented as the base64url encoding of the | |
| // | | value's unsigned big endian representation as an octet | |
| // | | sequence. | |
| // +-------+--------------------------------------------------------------+ |
| // |
| // Consistency and conflict resolution |
| // The 'algorithm', 'extractable', and 'usage_mask' input parameters |
| // may be different than the corresponding values inside the JWK. The Web |
| // Crypto spec says that if a JWK value is present but is inconsistent with |
| // the input value, it is an error and the operation must fail. If no |
| // inconsistency is found then the input parameters are used. |
| // |
| // algorithm |
| // If the JWK algorithm is provided, it must match the web crypto input |
| // algorithm (both the algorithm ID and inner hash if applicable). |
| // |
| // extractable |
| // If the JWK ext field is true but the input parameter is false, make the |
| // Web Crypto Key non-extractable. Conversely, if the JWK ext field is |
| // false but the input parameter is true, it is an inconsistency. If both |
| // are true or both are false, use that value. |
| // |
| // usage_mask |
| // The input usage_mask must be a strict subset of the interpreted JWK use |
| // value, else it is judged inconsistent. In all cases the input usage_mask |
| // is used as the final usage_mask. |
| // |
| |
| namespace content { |
| |
| namespace webcrypto { |
| |
| namespace { |
| |
| // Web Crypto equivalent usage mask for JWK 'use' = 'enc'. |
| const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
| blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | |
| blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey | |
| blink::WebCryptoKeyUsageDeriveKey | blink::WebCryptoKeyUsageDeriveBits; |
| // Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
| const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
| blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
| |
| class JwkWriter { |
| 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); |
| } |
| |
| void Set(const std::string& key, const std::string& value) { |
| dict_.SetString(key, value); |
| } |
| |
| void SetBase64Encoded(const std::string& key, const CryptoData& value) { |
| dict_.SetString(key, |
| Base64EncodeUrlSafe(base::StringPiece( |
| reinterpret_cast<const char*>(value.bytes()), |
| value.byte_length()))); |
| } |
| |
| void ToBytes(std::vector<uint8_t>* utf8_bytes) { |
| std::string json; |
| base::JSONWriter::Write(&dict_, &json); |
| utf8_bytes->assign(json.begin(), json.end()); |
| } |
| |
| private: |
| base::DictionaryValue dict_; |
| }; |
| |
| // 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. |
| Status GetJwkString(base::DictionaryValue* dict, |
| const std::string& path, |
| std::string* result) { |
| base::Value* value = NULL; |
| if (!dict->Get(path, &value)) |
| return Status::ErrorJwkPropertyMissing(path); |
| if (!value->GetAsString(result)) |
| return Status::ErrorJwkPropertyWrongType(path, "string"); |
| return Status::Success(); |
| } |
| |
| // Extracts the optional string property with key |path| from |dict| and saves |
| // the result to |*result| if it was found. If the property exists and is not a |
| // string, returns an error. Otherwise returns success, and sets |
| // |*property_exists| if it was found. |
| Status GetOptionalJwkString(base::DictionaryValue* dict, |
| const std::string& path, |
| std::string* result, |
| bool* property_exists) { |
| *property_exists = false; |
| base::Value* value = NULL; |
| if (!dict->Get(path, &value)) |
| return Status::Success(); |
| |
| if (!value->GetAsString(result)) |
| return Status::ErrorJwkPropertyWrongType(path, "string"); |
| |
| *property_exists = true; |
| return Status::Success(); |
| } |
| |
| // Extracts the optional array property with key |path| from |dict| and saves |
| // the result to |*result| if it was found. If the property exists and is not an |
| // array, returns an error. Otherwise returns success, and sets |
| // |*property_exists| if it was found. Note that |*result| is owned by |dict|. |
| Status GetOptionalJwkList(base::DictionaryValue* dict, |
| const std::string& path, |
| base::ListValue** result, |
| bool* property_exists) { |
| *property_exists = false; |
| base::Value* value = NULL; |
| if (!dict->Get(path, &value)) |
| return Status::Success(); |
| |
| if (!value->GetAsList(result)) |
| return Status::ErrorJwkPropertyWrongType(path, "list"); |
| |
| *property_exists = true; |
| return Status::Success(); |
| } |
| |
| // Extracts the required string property with key |path| from |dict| and saves |
| // the base64url-decoded bytes to |*result|. If the property does not exist or |
| // is not a string, or could not be base64url-decoded, returns an error. |
| Status GetJwkBytes(base::DictionaryValue* dict, |
| const std::string& path, |
| std::string* result) { |
| std::string base64_string; |
| Status status = GetJwkString(dict, path, &base64_string); |
| if (status.IsError()) |
| return status; |
| |
| if (!Base64DecodeUrlSafe(base64_string, result)) |
| return Status::ErrorJwkBase64Decode(path); |
| |
| return Status::Success(); |
| } |
| |
| // 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 (result->empty()) |
| return Status::ErrorJwkEmptyBigInteger(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(); |
| } |
| |
| // Extracts the optional boolean property with key |path| from |dict| and saves |
| // the result to |*result| if it was found. If the property exists and is not a |
| // boolean, returns an error. Otherwise returns success, and sets |
| // |*property_exists| if it was found. |
| Status GetOptionalJwkBool(base::DictionaryValue* dict, |
| const std::string& path, |
| bool* result, |
| bool* property_exists) { |
| *property_exists = false; |
| base::Value* value = NULL; |
| if (!dict->Get(path, &value)) |
| return Status::Success(); |
| |
| if (!value->GetAsBoolean(result)) |
| return Status::ErrorJwkPropertyWrongType(path, "boolean"); |
| |
| *property_exists = true; |
| 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(); |
| } |
| |
| 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(); |
| } |
| |
| // 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(); |
| } |
| |
| // 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(); |
| } |
| |
| 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; |
| |
| if (has_jwk_alg && jwk_alg_value != expected_algorithm) |
| return Status::ErrorJwkAlgorithmInconsistent(); |
| |
| 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; |
| |
| status = VerifyExt(dict_value, expected_extractable); |
| if (status.IsError()) |
| return status; |
| |
| status = VerifyUsages(dict_value, expected_usage_mask); |
| if (status.IsError()) |
| return status; |
| |
| return Status::Success(); |
| } |
| |
| 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 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"); |
| |
| 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()); |
| |
| return Status::Success(); |
| } |
| |
| } // namespace |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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; |
| |
| bool 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) { |
| 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(); |
| } |
| |
| // 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); |
| } |
| |
| // 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); |
| } |
| |
| 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; |
| |
| status = VerifyAlg(dict.get(), expected_algorithm); |
| if (status.IsError()) |
| return status; |
| |
| if (kty != "RSA") |
| return Status::ErrorJwkUnexpectedKty("RSA"); |
| |
| // An RSA public key must have an "n" (modulus) and an "e" (exponent) entry |
| // in the JWK, while an RSA private key must have those, plus at least a "d" |
| // (private exponent) entry. |
| // See http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-18, |
| // section 6.3. |
| status = GetJwkBigInteger(dict.get(), "n", &result->n); |
| if (status.IsError()) |
| return status; |
| status = GetJwkBigInteger(dict.get(), "e", &result->e); |
| if (status.IsError()) |
| return status; |
| |
| result->is_private_key = dict->HasKey("d"); |
| if (!result->is_private_key) |
| return Status::Success(); |
| |
| status = GetJwkBigInteger(dict.get(), "d", &result->d); |
| if (status.IsError()) |
| return status; |
| |
| // The "p", "q", "dp", "dq", and "qi" properties are optional in the JWA |
| // spec. However they are required by Chromium's WebCrypto implementation. |
| |
| status = GetJwkBigInteger(dict.get(), "p", &result->p); |
| if (status.IsError()) |
| return status; |
| |
| status = GetJwkBigInteger(dict.get(), "q", &result->q); |
| if (status.IsError()) |
| return status; |
| |
| status = GetJwkBigInteger(dict.get(), "dp", &result->dp); |
| if (status.IsError()) |
| return status; |
| |
| status = GetJwkBigInteger(dict.get(), "dq", &result->dq); |
| if (status.IsError()) |
| return status; |
| |
| status = GetJwkBigInteger(dict.get(), "qi", &result->qi); |
| if (status.IsError()) |
| return status; |
| |
| 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 |