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

Unified Diff: content/renderer/webcrypto/webcrypto_impl.cc

Issue 25906002: [webcrypto] Add JWK import for HMAC and AES-CBC key. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed some DCHECK's and TODO's. Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/renderer/webcrypto/webcrypto_impl.h ('k') | content/renderer/webcrypto/webcrypto_impl_openssl.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/renderer/webcrypto/webcrypto_impl.cc
diff --git a/content/renderer/webcrypto/webcrypto_impl.cc b/content/renderer/webcrypto/webcrypto_impl.cc
index 6eeb3daf5a0cb07de9dc19128ed3e2913338054b..d4562f8fcc67ee8f46243bc50eb3bf9b6537b95b 100644
--- a/content/renderer/webcrypto/webcrypto_impl.cc
+++ b/content/renderer/webcrypto/webcrypto_impl.cc
@@ -4,15 +4,242 @@
#include "content/renderer/webcrypto/webcrypto_impl.h"
+#include <algorithm>
+#include <map>
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/values.h"
#include "third_party/WebKit/public/platform/WebArrayBuffer.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h"
+#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKey.h"
namespace content {
+namespace {
+
+// TODO(padolph) Similar Create*Algorithm() methods are in
+// webcrypto_impl_unittest.cc. Find a common place to put these.
+
+WebKit::WebCryptoAlgorithm CreateAlgorithm(WebKit::WebCryptoAlgorithmId id) {
+ return WebKit::WebCryptoAlgorithm::adoptParamsAndCreate(id, NULL);
+}
+
+WebKit::WebCryptoAlgorithm CreateAlgorithmWithInnerHash(
+ WebKit::WebCryptoAlgorithmId algorithm_id,
+ unsigned short hash_key_length) {
+ WebKit::WebCryptoAlgorithmId hashId;
+ switch (hash_key_length) {
+ case 160:
+ hashId = WebKit::WebCryptoAlgorithmIdSha1;
+ break;
+ case 224:
+ hashId = WebKit::WebCryptoAlgorithmIdSha224;
+ break;
+ case 256:
+ hashId = WebKit::WebCryptoAlgorithmIdSha256;
+ break;
+ case 384:
+ hashId = WebKit::WebCryptoAlgorithmIdSha384;
+ break;
+ case 512:
+ hashId = WebKit::WebCryptoAlgorithmIdSha384;
+ break;
+ }
+ return WebKit::WebCryptoAlgorithm::adoptParamsAndCreate(
+ algorithm_id,
+ new WebKit::WebCryptoHmacParams(CreateAlgorithm(hashId)));
+}
+
+WebKit::WebCryptoAlgorithm CreateHmacAlgorithm(unsigned short hash_key_length) {
+ return CreateAlgorithmWithInnerHash(
+ WebKit::WebCryptoAlgorithmIdHmac,
+ hash_key_length);
+}
+
+WebKit::WebCryptoAlgorithm CreateRsaSsaAlgorithm(
+ unsigned short hash_key_length) {
+ return CreateAlgorithmWithInnerHash(
+ WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
+ hash_key_length);
+}
+
+WebKit::WebCryptoAlgorithm CreateRsaOaepAlgorithm(
+ unsigned short hash_key_length) {
+ return CreateAlgorithmWithInnerHash(
+ WebKit::WebCryptoAlgorithmIdRsaOaep,
+ hash_key_length);
+}
+
+WebKit::WebCryptoAlgorithm CreateAesAlgorithm(
+ WebKit::WebCryptoAlgorithmId aes_alg_id,
+ unsigned short length) {
+ return WebKit::WebCryptoAlgorithm::adoptParamsAndCreate(
+ aes_alg_id,
+ new WebKit::WebCryptoAesKeyGenParams(length));
+}
+
+WebKit::WebCryptoAlgorithm CreateAesCbcAlgorithm(unsigned short length) {
+ return CreateAesAlgorithm(WebKit::WebCryptoAlgorithmIdAesCbc, length);
+}
+
+WebKit::WebCryptoAlgorithm CreateAesGcmAlgorithm(unsigned short length) {
+ return CreateAesAlgorithm(WebKit::WebCryptoAlgorithmIdAesGcm, length);
+}
+
+bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
+ std::string base64EncodedText(input);
+ std::replace(base64EncodedText.begin(), base64EncodedText.end(), '-', '+');
+ std::replace(base64EncodedText.begin(), base64EncodedText.end(), '_', '/');
+ base64EncodedText.append((4 - base64EncodedText.size() % 4) % 4, '=');
+ return base::Base64Decode(base64EncodedText, output);
+}
+
+// Identifiers for all JWK "alg" (algorithm) values handled by this code. The
+// "enum trick" is used to force int type, so a user type is not required in
+// web_crypto_impl.h
+enum {
+ kJwkAlgorithmHs256,
+ kJwkAlgorithmHs384,
+ kJwkAlgorithmHs512,
+ kJwkAlgorithmRs256,
+ kJwkAlgorithmRs384,
+ kJwkAlgorithmRs512,
+ kJwkAlgorithmRsa1_5,
+ kJwkAlgorithmRsaOaep,
+ kJwkAlgorithmA128Kw,
+ kJwkAlgorithmA256Kw,
+ kJwkAlgorithmA128Gcm,
+ kJwkAlgorithmA256Gcm,
+ kJwkAlgorithmA128Cbc,
+ kJwkAlgorithmA256Cbc,
+ kJwkAlgorithmA384Cbc,
+ kJwkAlgorithmA512Cbc
+};
+
+WebKit::WebCryptoAlgorithmId JwkAlgIdToWebCryptoAlgId(int jwk_algorithm_id) {
+ switch (jwk_algorithm_id) {
+ case kJwkAlgorithmHs256:
+ case kJwkAlgorithmHs384:
+ case kJwkAlgorithmHs512:
+ return WebKit::WebCryptoAlgorithmIdHmac;
+ case kJwkAlgorithmRs256:
+ case kJwkAlgorithmRs384:
+ case kJwkAlgorithmRs512:
+ return WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
+ case kJwkAlgorithmRsa1_5:
+ return WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5;
+ case kJwkAlgorithmRsaOaep:
+ return WebKit::WebCryptoAlgorithmIdRsaOaep;
+ case kJwkAlgorithmA128Kw:
+ case kJwkAlgorithmA256Kw:
+ // TODO(padolph) Support AES keywrap algorithm, required for JWK but not
+ // present in the Web Crypto spec.
+ return WebKit::WebCryptoAlgorithmIdNone;
+ case kJwkAlgorithmA128Gcm:
+ case kJwkAlgorithmA256Gcm:
+ return WebKit::WebCryptoAlgorithmIdAesGcm;
+ case kJwkAlgorithmA128Cbc:
+ case kJwkAlgorithmA256Cbc:
+ case kJwkAlgorithmA384Cbc:
+ case kJwkAlgorithmA512Cbc:
+ return WebKit::WebCryptoAlgorithmIdAesCbc;
+ default:
+ DCHECK(false);
+ return WebKit::WebCryptoAlgorithmIdNone;
+ }
+}
+
+unsigned short JwkAlgIdToKeyLengthBits(int jwk_algorithm_id) {
+ switch (jwk_algorithm_id) {
+ case kJwkAlgorithmA128Kw:
+ case kJwkAlgorithmA128Gcm:
+ case kJwkAlgorithmA128Cbc:
+ return 128;
+ case kJwkAlgorithmHs256:
+ case kJwkAlgorithmA256Kw:
+ case kJwkAlgorithmRs256:
+ case kJwkAlgorithmA256Gcm:
+ case kJwkAlgorithmA256Cbc:
+ return 256;
+ case kJwkAlgorithmHs384:
+ case kJwkAlgorithmRs384:
+ case kJwkAlgorithmA384Cbc:
+ return 384;
+ case kJwkAlgorithmHs512:
+ case kJwkAlgorithmRs512:
+ case kJwkAlgorithmA512Cbc:
+ return 512;
+ default:
+ return 0;
+ }
+}
+
+typedef std::map<std::string, int> StringIntMap;
+
+// Syntactic sugar to make runtime initialization of a std::map more palatable,
+// similar to what you can do in C++11 or with Boost.Assign.
+// Example:
+// StringIntMap my_map;
+// // instead of this
+// my_map.insert(std::make_pair("a", 1);
+// my_map.insert(std::make_pair("b", 2);
+// my_map.insert(std::make_pair("c", 3);
+// my_map.insert(std::make_pair("d", 4);
+// // do this
+// map_fill<StringIntMap>
+// ("a", 1)
+// ("b", 2)
+// ("c", 3)
+// ("d", 4)
+// .to(my_map);
Ryan Sleevi 2013/10/04 23:19:11 Why is this syntactic sugar needed, when my_map["
padolph 2013/10/05 02:57:42 Done.
+template <typename MapType>
+class FillMap {
+ public:
+ typedef typename MapType::key_type KeyType;
+ typedef typename MapType::mapped_type MappedType;
+ FillMap(const KeyType& key, const MappedType& val) { operator()(key, val); }
+ FillMap& operator()(const KeyType& key, const MappedType& val) {
+ map_.insert(std::make_pair(key, val));
+ return *this;
+ }
+ void to(MapType& map) { map.swap(map_); }
+ private:
+ MapType map_;
+};
+
+void InitJwkAlgorithmMap(StringIntMap& jwk_algorithm_map)
+{
+ // Note: A*CBC are not yet present in the JOSE JWA spec
+ // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16
+ FillMap<StringIntMap>
+ ("HS256" , kJwkAlgorithmHs256 )
+ ("HS384" , kJwkAlgorithmHs384 )
+ ("HS512" , kJwkAlgorithmHs512 )
+ ("RS256" , kJwkAlgorithmRs256 )
+ ("RS384" , kJwkAlgorithmRs384 )
+ ("RS512" , kJwkAlgorithmRs512 )
+ ("RSA1_5" , kJwkAlgorithmRsa1_5 )
+ ("RSA-OAEP", kJwkAlgorithmRsaOaep)
+ ("A128KW" , kJwkAlgorithmA128Kw )
+ ("A256KW" , kJwkAlgorithmA256Kw )
+ ("A128GCM" , kJwkAlgorithmA128Gcm)
+ ("A256GCM" , kJwkAlgorithmA256Gcm)
+ ("A128CBC" , kJwkAlgorithmA128Cbc)
+ ("A256CBC" , kJwkAlgorithmA256Cbc)
+ ("A384CBC" , kJwkAlgorithmA384Cbc)
+ ("A512CBC" , kJwkAlgorithmA512Cbc)
+ .to(jwk_algorithm_map);
+}
+
+} // namespace
+
WebCryptoImpl::WebCryptoImpl() {
Init();
+ InitJwkAlgorithmMap(jwk_algorithm_map_);
}
void WebCryptoImpl::encrypt(
@@ -66,21 +293,32 @@ void WebCryptoImpl::importKey(
WebKit::WebCryptoResult result) {
WebKit::WebCryptoKeyType type;
scoped_ptr<WebKit::WebCryptoKeyHandle> handle;
+ WebKit::WebCryptoAlgorithm modified_algorithm = algorithm;
- if (!ImportKeyInternal(format,
- key_data,
- key_data_size,
- algorithm,
- usage_mask,
- &handle,
- &type)) {
- result.completeWithError();
- return;
+ if (format == WebKit::WebCryptoKeyFormatJwk) {
+ if (!ImportKeyJwk(key_data,
+ key_data_size,
+ &handle,
+ &type,
+ &extractable,
+ &modified_algorithm,
+ &usage_mask)) {
+ result.completeWithError();
+ }
+ } else {
+ if (!ImportKeyInternal(format,
+ key_data,
+ key_data_size,
+ modified_algorithm,
+ usage_mask,
+ &handle,
+ &type)) {
+ result.completeWithError();
+ }
}
- WebKit::WebCryptoKey key(
- WebKit::WebCryptoKey::create(
- handle.release(), type, extractable, algorithm, usage_mask));
+ WebKit::WebCryptoKey key(WebKit::WebCryptoKey::create(
+ handle.release(), type, extractable, modified_algorithm, usage_mask));
result.completeWithKey(key);
}
@@ -121,4 +359,214 @@ void WebCryptoImpl::verifySignature(
}
}
+bool WebCryptoImpl::ImportKeyJwk(
+ const unsigned char* key_data,
+ unsigned key_data_size,
+ scoped_ptr<WebKit::WebCryptoKeyHandle>* handle,
+ WebKit::WebCryptoKeyType* type,
+ bool* extractable,
+ WebKit::WebCryptoAlgorithm* algorithm,
+ WebKit::WebCryptoKeyUsageMask* usage_mask) {
+
+ // JSON Web Key Format (JWK)
+ // http://self-issued.info/docs/draft-ietf-jose-json-web-key.html (JOSE)
Ryan Sleevi 2013/10/04 23:19:11 Let's refer to a canonical draft URL at the IETF,
padolph 2013/10/05 02:57:42 Done.
+ // TODO(padolph) Not all possible values are handled by this code right now
+ //
+ // 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
+ // - "alg" (Algorithm) Parameter, OPTIONAL
+ // - "extractable" (Key Exportability), OPTIONAL [NOTE: not yet part of JOSE]
+ // (all other entries are ignored)
+ //
+ // Input key_data contains the JWK. To build a Web Crypto Key, the JWK values
+ // are parsed out and used as follows:
+ // Web Crypto Key type <-- (deduced)
+ // Web Crypto Key extractable <-- extractable
+ // Web Crypto Key algorithm <-- alg
+ // Web Crypto Key keyUsage <-- usage
+ // Web Crypto Key keying material <-- kty-specific parameters
+ //
+ // Values for each entry are case-sensitive and defined in
+ // http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-16.
+ // 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) |
+ // +-------+--------------------------------------------------------------+
+ // - use (Key Use)
+ // +-------+--------------------------------------------------------------+
+ // | "enc" | encrypt and decrypt operations |
+ // | "sig" | sign and verify (MAC) operations |
+ // | "wrap"| key wrap and unwrap [not yet part of JOSE] |
+ // +-------+--------------------------------------------------------------+
+ // - extractable (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-16
+ // +--------------+-------------------------------------------------------+
+ // | Digital Signature or MAC Algorithm |
+ // +--------------+-------------------------------------------------------+
+ // | "HS256" | HMAC using SHA-256 hash algorithm |
+ // | "HS384" | HMAC using SHA-384 hash algorithm |
+ // | "HS512" | HMAC using SHA-512 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 |
+ // +--------------+-------------------------------------------------------+
+ // | "RSA1_5" | RSAES-PKCS1-V1_5 [RFC3447] |
+ // | "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 |
+ // | "A256KW" | AES Key Wrap Algorithm using 256 bit keys |
+ // | "A128GCM" | AES in Galois/Counter Mode (GCM) [NIST.800-38D] using |
+ // | | 128 bit keys |
+ // | "A256GCM" | AES GCM using 256 bit keys |
+ // | "A128CBC" | AES in Cipher Block Chaining Mode (CBC) with PKCS #5 |
+ // | | padding [NIST.800-38A] [not yet part of JOSE] |
+ // | "A256CBC" | AES CBC using 256 bit keys [not yet part of JOSE] |
+ // | "A384CBC" | AES CBC using 384 bit keys [not yet part of JOSE] |
+ // | "A512CBC" | AES CBC using 512 bit keys [not yet part of JOSE] |
+ // +--------------+-------------------------------------------------------+
+ //
+ // kty-specific parameters
+ // The value of kty determines the type and content of the keying material
+ // carried in the JWK to be imported. Currently only two possibilities are
+ // supported: a raw key or an RSA public key. RSA private keys are not
+ // supported because typical applications seldom need to import a private key,
+ // and the large number of JWK parameters required to describe one.
+ // - kty == "oct" (symmetric or other raw key)
+ // +-------+--------------------------------------------------------------+
+ // | "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. |
+ // +-------+--------------------------------------------------------------+
+ //
+ // Conflict resolution
+ // The type, algorithm, extractable, and usage_mask input parameters may be
+ // different from similar values inside the JWK. Conflicts are resolved as
+ // follows:
+ // type: Deduce value from JWK contents only, ignore input value
+ // algorithm: Use JWK value if present, otherwise use input value
+ // extractable: If JWK value present, logical AND of the input and JWK values,
+ // otherwise use input value
+ // keyUsage: Use JWK value if present, otherwise use input value
Ryan Sleevi 2013/10/04 23:19:11 These are all things that should be in the spec be
padolph 2013/10/05 02:57:42 The other parameters are easy, but can you please
+
+ std::string jsonStr(key_data, key_data + key_data_size);
Ryan Sleevi 2013/10/04 23:19:11 json_str However, you don't need to use std::stri
padolph 2013/10/05 02:57:42 Done.
+ scoped_ptr<base::Value> value(base::JSONReader::Read(jsonStr));
+ // 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 false;
+
+ // JWK "kty". Exit early if this required JWK parameter is missing.
+ std::string jwk_kty_value;
+ if (!dict_value->GetString("kty", &jwk_kty_value))
+ return false;
+
+ // JWK "extractable" --> extractable parameter
+ bool jwk_extractable_value;
+ if (dict_value->GetBoolean("extractable", &jwk_extractable_value)) {
+ *extractable &= jwk_extractable_value;
+ }
+
+ // JWK "alg" --> algorithm parameter
+ std::string jwk_alg_value;
+ if (dict_value->GetString("alg", &jwk_alg_value)) {
+ const StringIntMap::iterator pos = jwk_algorithm_map_.find(jwk_alg_value);
+ if (pos == jwk_algorithm_map_.end())
+ return false;
+ const int jwk_algorithm_id = pos->second;
+ const unsigned short key_length_bits =
+ JwkAlgIdToKeyLengthBits(jwk_algorithm_id);
+ const WebKit::WebCryptoAlgorithmId webcrypto_algorithm_id =
+ JwkAlgIdToWebCryptoAlgId(jwk_algorithm_id);
+ switch (webcrypto_algorithm_id) {
+ case WebKit::WebCryptoAlgorithmIdHmac:
+ *algorithm = CreateHmacAlgorithm(key_length_bits);
+ break;
+ case WebKit::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
+ *algorithm = CreateRsaSsaAlgorithm(key_length_bits);
+ break;
+ case WebKit::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
+ *algorithm = CreateAlgorithm(webcrypto_algorithm_id);
+ break;
+ case WebKit::WebCryptoAlgorithmIdRsaOaep:
+ *algorithm = CreateRsaOaepAlgorithm(key_length_bits);
+ break;
+ case WebKit::WebCryptoAlgorithmIdAesGcm:
+ *algorithm = CreateAesGcmAlgorithm(key_length_bits);
+ break;
+ case WebKit::WebCryptoAlgorithmIdAesCbc:
+ *algorithm = CreateAesCbcAlgorithm(key_length_bits);
+ break;
+ default:
+ return false;
+ }
+ }
+
+ // JWK "use" --> usage_mask parameter
+ std::string jwk_use_value;
+ if (dict_value->GetString("use", &jwk_use_value)) {
+ if (jwk_use_value == "enc") {
+ *usage_mask =
+ WebKit::WebCryptoKeyUsageEncrypt | WebKit::WebCryptoKeyUsageDecrypt;
+ } else if (jwk_use_value == "sig") {
+ *usage_mask =
+ WebKit::WebCryptoKeyUsageSign | WebKit::WebCryptoKeyUsageVerify;
+ } else if (jwk_use_value == "wrap") {
+ *usage_mask =
+ WebKit::WebCryptoKeyUsageWrapKey | WebKit::WebCryptoKeyUsageUnwrapKey;
+ } else {
+ return false;
+ }
+ }
+
+ // JWK keying material --> ImportKeyInternal()
+ if (jwk_kty_value == "oct") {
+ std::string jwk_k_value_url64;
+ if (!dict_value->GetString("k", &jwk_k_value_url64))
+ return false;
+ std::string jwk_k_value;
+ if (!Base64DecodeUrlSafe(jwk_k_value_url64, &jwk_k_value))
+ return false;
+ const std::vector<uint8> data(jwk_k_value.begin(), jwk_k_value.end());
+ if (!ImportKeyInternal(WebKit::WebCryptoKeyFormatRaw,
+ &data[0],
+ data.size(),
+ *algorithm,
+ *usage_mask,
+ handle,
+ type))
+ return false;
+ } else if (jwk_kty_value == "RSA") {
+ // TODO(padolph): RSA public key
+ return false;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
} // namespace content
« no previous file with comments | « content/renderer/webcrypto/webcrypto_impl.h ('k') | content/renderer/webcrypto/webcrypto_impl_openssl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698