diff options
author | eroman <eroman@chromium.org> | 2015-09-15 17:31:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-09-16 00:32:36 +0000 |
commit | dceea88ee742f7b4e6599156d7bc77a8d1968d61 (patch) | |
tree | 3afda1e23ac9e202972fed869c36cb254416f38c /components/webcrypto | |
parent | a7c3f51cf5a2808acd9390edbb664ef5692cc20f (diff) | |
download | chromium_src-dceea88ee742f7b4e6599156d7bc77a8d1968d61.zip chromium_src-dceea88ee742f7b4e6599156d7bc77a8d1968d61.tar.gz chromium_src-dceea88ee742f7b4e6599156d7bc77a8d1968d61.tar.bz2 |
[refactor] Move algorithm-specific JWK code into algorithm-specific files.
This is a cleanup possible now that the NSS implementation is gone.
BUG=519504
Review URL: https://codereview.chromium.org/1314033006
Cr-Commit-Position: refs/heads/master@{#349048}
Diffstat (limited to 'components/webcrypto')
-rw-r--r-- | components/webcrypto/jwk.cc | 181 | ||||
-rw-r--r-- | components/webcrypto/jwk.h | 87 | ||||
-rw-r--r-- | components/webcrypto/openssl/aes_algorithm_openssl.cc | 43 | ||||
-rw-r--r-- | components/webcrypto/openssl/hmac_openssl.cc | 23 | ||||
-rw-r--r-- | components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc | 115 |
5 files changed, 167 insertions, 282 deletions
diff --git a/components/webcrypto/jwk.cc b/components/webcrypto/jwk.cc index 7ae5ca2..701b82f 100644 --- a/components/webcrypto/jwk.cc +++ b/components/webcrypto/jwk.cc @@ -16,9 +16,6 @@ #include "components/webcrypto/status.h" #include "components/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. - // JSON Web Key Format (JWK) is defined by: // http://tools.ietf.org/html/draft-ietf-jose-json-web-key // @@ -400,184 +397,6 @@ void WriteSecretKeyJwk(const CryptoData& raw_key_data, writer.ToJson(jwk_key_data); } -Status ReadSecretKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data) { - JwkReader jwk; - Status status = ReadSecretKeyNoExpectedAlg( - key_data, expected_extractable, expected_usages, raw_key_data, &jwk); - if (status.IsError()) - return status; - return jwk.VerifyAlg(expected_alg); -} - -std::string MakeJwkAesAlgorithmName(const std::string& suffix, - size_t 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_usages, - std::vector<uint8_t>* raw_key_data) { - JwkReader jwk; - Status status = ReadSecretKeyNoExpectedAlg( - key_data, expected_extractable, expected_usages, raw_key_data, &jwk); - if (status.IsError()) - return status; - - bool has_jwk_alg; - std::string jwk_alg; - status = jwk.GetAlg(&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 usages, - std::vector<uint8_t>* jwk_key_data) { - JwkWriter writer(algorithm, extractable, usages, "RSA"); - writer.SetBytes("n", n); - writer.SetBytes("e", e); - writer.ToJson(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 usages, - std::vector<uint8_t>* jwk_key_data) { - JwkWriter writer(algorithm, extractable, usages, "RSA"); - - writer.SetBytes("n", n); - writer.SetBytes("e", e); - writer.SetBytes("d", d); - // Although these are "optional" in the JWA, WebCrypto spec requires them to - // be emitted. - writer.SetBytes("p", p); - writer.SetBytes("q", q); - writer.SetBytes("dp", dp); - writer.SetBytes("dq", dq); - writer.SetBytes("qi", qi); - writer.ToJson(jwk_key_data); -} - -JwkRsaInfo::JwkRsaInfo() : is_private_key(false) { -} - -JwkRsaInfo::~JwkRsaInfo() { -} - -Status ReadRsaKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - JwkRsaInfo* result) { - JwkReader jwk; - Status status = jwk.Init(key_data, expected_extractable, expected_usages, - "RSA", expected_alg); - 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 = jwk.GetBigInteger("n", &result->n); - if (status.IsError()) - return status; - status = jwk.GetBigInteger("e", &result->e); - if (status.IsError()) - return status; - - result->is_private_key = jwk.HasMember("d"); - if (!result->is_private_key) - return Status::Success(); - - status = jwk.GetBigInteger("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 = jwk.GetBigInteger("p", &result->p); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("q", &result->q); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("dp", &result->dp); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("dq", &result->dq); - if (status.IsError()) - return status; - - status = jwk.GetBigInteger("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; - } -} - bool Base64DecodeUrlSafe(const std::string& input, std::string* output) { // The JSON web signature spec specifically says that padding is omitted. if (input.find_first_of("+/=") != std::string::npos) diff --git a/components/webcrypto/jwk.h b/components/webcrypto/jwk.h index 5ab62fe..2c55582 100644 --- a/components/webcrypto/jwk.h +++ b/components/webcrypto/jwk.h @@ -140,91 +140,14 @@ void WriteSecretKeyJwk(const CryptoData& raw_key_data, // Parses a UTF-8 encoded JWK (key_data), and extracts the key material to // |*raw_key_data|. Returns Status::Success() on success, otherwise an error. // In order for this to succeed: -// * expected_alg must match the JWK's "alg", if present. // * expected_extractable must be consistent with the JWK's "ext", if // present. // * expected_usages must be a subset of the JWK's "key_ops" if present. -Status ReadSecretKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data); - -// Creates an AES algorithm name for the given key size (in bytes). For -// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. -std::string MakeJwkAesAlgorithmName(const std::string& suffix, - size_t keylen_bytes); - -// This is very similar to ReadSecretKeyJwk(), except instead of specifying an -// absolute "expected_alg", the suffix for an AES algorithm name is given -// (See MakeJwkAesAlgorithmName() for an explanation of what the suffix is). -// -// This is because the algorithm name for AES keys is dependent on the length -// of the key. This function expects key lengths to be either 128, 192, or 256 -// bits. -Status ReadAesSecretKeyJwk(const CryptoData& key_data, - const std::string& algorithm_name_suffix, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - std::vector<uint8_t>* raw_key_data); - -// Writes a JWK-formated RSA public key and saves the result to -// |*jwk_key_data|. -void WriteRsaPublicKeyJwk(const CryptoData& n, - const CryptoData& e, - const std::string& algorithm, - bool extractable, - blink::WebCryptoKeyUsageMask usages, - std::vector<uint8_t>* jwk_key_data); - -// Writes a JWK-formated RSA private key and saves the result to -// |*jwk_key_data|. -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 usages, - std::vector<uint8_t>* jwk_key_data); - -// Describes the RSA components for a parsed key. The names of the properties -// correspond with those from the JWK spec. Note that Chromium's WebCrypto -// implementation does not support multi-primes, so there is no parsed field -// for othinfo. -struct JwkRsaInfo { - JwkRsaInfo(); - ~JwkRsaInfo(); - - bool is_private_key; - std::string n; - std::string e; - std::string d; - std::string p; - std::string q; - std::string dp; - std::string dq; - std::string qi; -}; - -// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to -// |*result|. Returns Status::Success() on success, otherwise an error. -// In order for this to succeed: -// * expected_alg must match the JWK's "alg", if present. -// * expected_extractable must be consistent with the JWK's "ext", if -// present. -// * expected_usages must be a subset of the JWK's "key_ops" if present. -Status ReadRsaKeyJwk(const CryptoData& key_data, - const std::string& expected_alg, - bool expected_extractable, - blink::WebCryptoKeyUsageMask expected_usages, - JwkRsaInfo* result); - -const char* GetJwkHmacAlgorithmName(blink::WebCryptoAlgorithmId hash); +Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + std::vector<uint8_t>* raw_key_data, + JwkReader* jwk); // This decodes JWK's flavor of base64 encoding, as described by: // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-36#section-2 diff --git a/components/webcrypto/openssl/aes_algorithm_openssl.cc b/components/webcrypto/openssl/aes_algorithm_openssl.cc index 05e30c5..0a47286 100644 --- a/components/webcrypto/openssl/aes_algorithm_openssl.cc +++ b/components/webcrypto/openssl/aes_algorithm_openssl.cc @@ -15,6 +15,23 @@ namespace webcrypto { +namespace { + +// Creates an AES algorithm name for the given key size (in bytes). For +// instance "A128CBC" is the result of suffix="CBC", keylen_bytes=16. +std::string MakeJwkAesAlgorithmName(const std::string& suffix, + size_t 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(); +} + +} // namespace + AesAlgorithm::AesAlgorithm(blink::WebCryptoKeyUsageMask all_key_usages, const std::string& jwk_suffix) : all_key_usages_(all_key_usages), jwk_suffix_(jwk_suffix) { @@ -83,11 +100,33 @@ Status AesAlgorithm::ImportKeyJwk(const CryptoData& key_data, blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* key) const { std::vector<uint8_t> raw_data; - Status status = ReadAesSecretKeyJwk(key_data, jwk_suffix_, extractable, - usages, &raw_data); + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlg(key_data, extractable, usages, + &raw_data, &jwk); + if (status.IsError()) + return status; + + bool has_jwk_alg; + std::string jwk_alg; + status = jwk.GetAlg(&jwk_alg, &has_jwk_alg); if (status.IsError()) return status; + if (has_jwk_alg) { + std::string expected_algorithm_name = + MakeJwkAesAlgorithmName(jwk_suffix_, raw_data.size()); + + if (jwk_alg != expected_algorithm_name) { + // Give a different error message if the key length was wrong. + if (jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 16) || + jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 24) || + jwk_alg == MakeJwkAesAlgorithmName(jwk_suffix_, 32)) { + return Status::ErrorJwkIncorrectKeyLength(); + } + return Status::ErrorJwkAlgorithmInconsistent(); + } + } + return ImportKeyRaw(CryptoData(raw_data), algorithm, extractable, usages, key); } diff --git a/components/webcrypto/openssl/hmac_openssl.cc b/components/webcrypto/openssl/hmac_openssl.cc index 9962bfd..d436d46 100644 --- a/components/webcrypto/openssl/hmac_openssl.cc +++ b/components/webcrypto/openssl/hmac_openssl.cc @@ -22,6 +22,21 @@ namespace webcrypto { namespace { +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; + } +} + const blink::WebCryptoKeyUsageMask kAllKeyUsages = blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; @@ -130,8 +145,12 @@ class HmacImplementation : public AlgorithmImplementation { return Status::ErrorUnexpected(); std::vector<uint8_t> raw_data; - Status status = ReadSecretKeyJwk(key_data, algorithm_name, extractable, - usages, &raw_data); + JwkReader jwk; + Status status = ReadSecretKeyNoExpectedAlg(key_data, extractable, usages, + &raw_data, &jwk); + if (status.IsError()) + return status; + status = jwk.VerifyAlg(algorithm_name); if (status.IsError()) return status; diff --git a/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc b/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc index e672e13..1082639 100644 --- a/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc +++ b/components/webcrypto/openssl/rsa_hashed_algorithm_openssl.cc @@ -24,6 +24,86 @@ namespace webcrypto { namespace { +// Describes the RSA components for a parsed key. The names of the properties +// correspond with those from the JWK spec. Note that Chromium's WebCrypto +// implementation does not support multi-primes, so there is no parsed field +// for "oth". +struct JwkRsaInfo { + bool is_private_key = false; + std::string n; + std::string e; + std::string d; + std::string p; + std::string q; + std::string dp; + std::string dq; + std::string qi; +}; + +// Parses a UTF-8 encoded JWK (key_data), and extracts the RSA components to +// |*result|. Returns Status::Success() on success, otherwise an error. +// In order for this to succeed: +// * expected_alg must match the JWK's "alg", if present. +// * expected_extractable must be consistent with the JWK's "ext", if +// present. +// * expected_usages must be a subset of the JWK's "key_ops" if present. +Status ReadRsaKeyJwk(const CryptoData& key_data, + const std::string& expected_alg, + bool expected_extractable, + blink::WebCryptoKeyUsageMask expected_usages, + JwkRsaInfo* result) { + JwkReader jwk; + Status status = jwk.Init(key_data, expected_extractable, expected_usages, + "RSA", expected_alg); + 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 = jwk.GetBigInteger("n", &result->n); + if (status.IsError()) + return status; + status = jwk.GetBigInteger("e", &result->e); + if (status.IsError()) + return status; + + result->is_private_key = jwk.HasMember("d"); + if (!result->is_private_key) + return Status::Success(); + + status = jwk.GetBigInteger("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 = jwk.GetBigInteger("p", &result->p); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("q", &result->q); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("dp", &result->dp); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("dq", &result->dq); + if (status.IsError()) + return status; + + status = jwk.GetBigInteger("qi", &result->qi); + if (status.IsError()) + return status; + + return Status::Success(); +} + // Creates a blink::WebCryptoAlgorithm having the modulus length and public // exponent of |key|. Status CreateRsaHashedKeyAlgorithm( @@ -343,23 +423,28 @@ Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key, return Status::ErrorUnexpected(); switch (key.type()) { - case blink::WebCryptoKeyTypePublic: - WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), - CryptoData(BIGNUMToVector(rsa->e)), jwk_algorithm, - key.extractable(), key.usages(), buffer); + case blink::WebCryptoKeyTypePublic: { + JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA"); + writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n))); + writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e))); + writer.ToJson(buffer); return Status::Success(); - case blink::WebCryptoKeyTypePrivate: - WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)), - CryptoData(BIGNUMToVector(rsa->e)), - CryptoData(BIGNUMToVector(rsa->d)), - CryptoData(BIGNUMToVector(rsa->p)), - CryptoData(BIGNUMToVector(rsa->q)), - CryptoData(BIGNUMToVector(rsa->dmp1)), - CryptoData(BIGNUMToVector(rsa->dmq1)), - CryptoData(BIGNUMToVector(rsa->iqmp)), - jwk_algorithm, key.extractable(), key.usages(), - buffer); + } + case blink::WebCryptoKeyTypePrivate: { + JwkWriter writer(jwk_algorithm, key.extractable(), key.usages(), "RSA"); + writer.SetBytes("n", CryptoData(BIGNUMToVector(rsa->n))); + writer.SetBytes("e", CryptoData(BIGNUMToVector(rsa->e))); + writer.SetBytes("d", CryptoData(BIGNUMToVector(rsa->d))); + // Although these are "optional" in the JWA, WebCrypto spec requires them + // to be emitted. + writer.SetBytes("p", CryptoData(BIGNUMToVector(rsa->p))); + writer.SetBytes("q", CryptoData(BIGNUMToVector(rsa->q))); + writer.SetBytes("dp", CryptoData(BIGNUMToVector(rsa->dmp1))); + writer.SetBytes("dq", CryptoData(BIGNUMToVector(rsa->dmq1))); + writer.SetBytes("qi", CryptoData(BIGNUMToVector(rsa->iqmp))); + writer.ToJson(buffer); return Status::Success(); + } default: return Status::ErrorUnexpected(); |