Index: content/child/webcrypto/jwk.cc |
diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc |
index eb6b3920d53e21142d602fab78490c3b9c059d88..4469a2efad2677dbe5750eca8c463b8fcb0e9524 100644 |
--- a/content/child/webcrypto/jwk.cc |
+++ b/content/child/webcrypto/jwk.cc |
@@ -2,11 +2,7 @@ |
// 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 "content/child/webcrypto/jwk.h" |
#include "base/base64.h" |
#include "base/json/json_reader.h" |
@@ -220,156 +216,13 @@ const blink::WebCryptoKeyUsageMask kJwkEncUsage = |
const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
-class JwkWriter { |
eroman
2014/10/29 02:35:11
This class declaration was moved into the header
|
- public: |
- JwkWriter(const std::string& algorithm, |
- bool extractable, |
- blink::WebCryptoKeyUsageMask usages, |
- const std::string& kty) { |
- dict_.SetString("alg", algorithm); |
- dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages)); |
- 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) { |
eroman
2014/10/29 02:35:11
Renamed this to SetBytes()
|
- dict_.SetString(key, |
- Base64EncodeUrlSafe(base::StringPiece( |
- reinterpret_cast<const char*>(value.bytes()), |
- value.byte_length()))); |
- } |
- |
- void ToBytes(std::vector<uint8_t>* utf8_bytes) { |
eroman
2014/10/29 02:35:11
Renamed this to ToJson()
|
- 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, |
eroman
2014/10/29 02:35:11
The following functions were moved further down in
|
- 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) { |
+// Checks that the "ext" property of the JWK is consistent with |
+// "expected_extractable". |
+Status VerifyExt(const JwkReader& jwk, 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); |
+ Status status = jwk.GetOptionalBool("ext", &jwk_ext_value, &has_jwk_ext); |
if (status.IsError()) |
return status; |
if (has_jwk_ext && expected_extractable && !jwk_ext_value) |
@@ -377,13 +230,15 @@ Status VerifyExt(base::DictionaryValue* dict, bool expected_extractable) { |
return Status::Success(); |
} |
-Status VerifyUsages(base::DictionaryValue* dict, |
+// Checks that the usages ("use" and "key_ops") of the JWK is consistent with |
+// "expected_usages". |
+Status VerifyUsages(const JwkReader& jwk, |
blink::WebCryptoKeyUsageMask expected_usages) { |
// JWK "key_ops" (optional) --> usages 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); |
+ jwk.GetOptionalList("key_ops", &jwk_key_ops_value, &has_jwk_key_ops); |
if (status.IsError()) |
return status; |
blink::WebCryptoKeyUsageMask jwk_key_ops_mask = 0; |
@@ -400,7 +255,7 @@ Status VerifyUsages(base::DictionaryValue* dict, |
// JWK "use" (optional) --> usages parameter |
std::string jwk_use_value; |
bool has_jwk_use; |
- status = GetOptionalJwkString(dict, "use", &jwk_use_value, &has_jwk_use); |
+ status = jwk.GetOptionalString("use", &jwk_use_value, &has_jwk_use); |
if (status.IsError()) |
return status; |
blink::WebCryptoKeyUsageMask jwk_use_mask = 0; |
@@ -424,27 +279,21 @@ Status VerifyUsages(base::DictionaryValue* dict, |
return Status::Success(); |
} |
-Status VerifyAlg(base::DictionaryValue* dict, |
eroman
2014/10/29 02:35:11
This was moved further down.
|
- 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; |
+} // namespace |
- if (has_jwk_alg && jwk_alg_value != expected_algorithm) |
- return Status::ErrorJwkAlgorithmInconsistent(); |
+JwkReader::JwkReader() { |
+} |
- return Status::Success(); |
+JwkReader::~JwkReader() { |
} |
-Status ParseJwkCommon(const CryptoData& bytes, |
- bool expected_extractable, |
- blink::WebCryptoKeyUsageMask expected_usages, |
- std::string* kty, |
- scoped_ptr<base::DictionaryValue>* dict) { |
+Status JwkReader::Init(const CryptoData& bytes, |
+ bool expected_extractable, |
+ blink::WebCryptoKeyUsageMask expected_usages, |
+ const std::string& expected_kty) { |
eroman
2014/10/29 02:35:11
Made 2 small changes here:
(1) Doesn't pass out
|
+ if (!bytes.byte_length()) |
+ return Status::ErrorImportEmptyKeyData(); |
+ |
// Parse the incoming JWK JSON. |
base::StringPiece json_string(reinterpret_cast<const char*>(bytes.bytes()), |
bytes.byte_length()); |
@@ -458,43 +307,168 @@ Status ParseJwkCommon(const CryptoData& bytes, |
// 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); |
+ dict_.reset(dict_value); |
// JWK "kty". Exit early if this required JWK parameter is missing. |
- Status status = GetJwkString(dict_value, "kty", kty); |
+ std::string kty; |
+ Status status = GetString("kty", &kty); |
if (status.IsError()) |
return status; |
- status = VerifyExt(dict_value, expected_extractable); |
+ if (kty != expected_kty) |
+ return Status::ErrorJwkUnexpectedKty(expected_kty); |
+ |
+ status = VerifyExt(*this, expected_extractable); |
if (status.IsError()) |
return status; |
- status = VerifyUsages(dict_value, expected_usages); |
+ status = VerifyUsages(*this, expected_usages); |
if (status.IsError()) |
return status; |
return Status::Success(); |
} |
+bool JwkReader::HasKey(const std::string& key) const { |
eroman
2014/10/29 02:35:11
This block of code was moved from above, to match
|
+ return dict_->HasKey(key); |
+} |
+ |
+Status JwkReader::GetString(const std::string& key, std::string* result) const { |
+ base::Value* value = NULL; |
+ if (!dict_->Get(key, &value)) |
+ return Status::ErrorJwkPropertyMissing(key); |
+ if (!value->GetAsString(result)) |
+ return Status::ErrorJwkPropertyWrongType(key, "string"); |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::GetOptionalString(const std::string& key, |
+ std::string* result, |
+ bool* property_exists) const { |
+ *property_exists = false; |
+ base::Value* value = NULL; |
+ if (!dict_->Get(key, &value)) |
+ return Status::Success(); |
+ |
+ if (!value->GetAsString(result)) |
+ return Status::ErrorJwkPropertyWrongType(key, "string"); |
+ |
+ *property_exists = true; |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::GetOptionalList(const std::string& key, |
+ base::ListValue** result, |
+ bool* property_exists) const { |
+ *property_exists = false; |
+ base::Value* value = NULL; |
+ if (!dict_->Get(key, &value)) |
+ return Status::Success(); |
+ |
+ if (!value->GetAsList(result)) |
+ return Status::ErrorJwkPropertyWrongType(key, "list"); |
+ |
+ *property_exists = true; |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::GetBytes(const std::string& key, std::string* result) const { |
+ std::string base64_string; |
+ Status status = GetString(key, &base64_string); |
+ if (status.IsError()) |
+ return status; |
+ |
+ if (!Base64DecodeUrlSafe(base64_string, result)) |
+ return Status::ErrorJwkBase64Decode(key); |
+ |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::GetBigInteger(const std::string& key, |
+ std::string* result) const { |
+ Status status = GetBytes(key, result); |
+ if (status.IsError()) |
+ return status; |
+ |
+ if (result->empty()) |
+ return Status::ErrorJwkEmptyBigInteger(key); |
+ |
+ // 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(key); |
+ |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::GetOptionalBool(const std::string& key, |
+ bool* result, |
+ bool* property_exists) const { |
+ *property_exists = false; |
+ base::Value* value = NULL; |
+ if (!dict_->Get(key, &value)) |
+ return Status::Success(); |
+ |
+ if (!value->GetAsBoolean(result)) |
+ return Status::ErrorJwkPropertyWrongType(key, "boolean"); |
+ |
+ *property_exists = true; |
+ return Status::Success(); |
+} |
+ |
+Status JwkReader::VerifyAlg(const std::string& expected_algorithm) const { |
+ bool has_jwk_alg; |
+ std::string jwk_alg_value; |
+ Status status = GetOptionalString("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(); |
+} |
+ |
+JwkWriter::JwkWriter(const std::string& algorithm, |
+ bool extractable, |
+ blink::WebCryptoKeyUsageMask usages, |
+ const std::string& kty) { |
+ dict_.SetString("alg", algorithm); |
+ dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages)); |
+ dict_.SetBoolean("ext", extractable); |
+ dict_.SetString("kty", kty); |
+} |
+ |
+void JwkWriter::SetString(const std::string& key, const std::string& value) { |
+ dict_.SetString(key, value); |
+} |
+ |
+void JwkWriter::SetBytes(const std::string& key, const CryptoData& value) { |
+ dict_.SetString( |
+ key, |
+ Base64EncodeUrlSafe(base::StringPiece( |
+ reinterpret_cast<const char*>(value.bytes()), value.byte_length()))); |
+} |
+ |
+void JwkWriter::ToJson(std::vector<uint8_t>* utf8_bytes) const { |
+ std::string json; |
+ base::JSONWriter::Write(&dict_, &json); |
+ utf8_bytes->assign(json.begin(), json.end()); |
+} |
+ |
Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, |
bool expected_extractable, |
blink::WebCryptoKeyUsageMask expected_usages, |
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_usages, &kty, dict); |
+ JwkReader* jwk) { |
+ Status status = |
+ jwk->Init(key_data, expected_extractable, expected_usages, "oct"); |
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); |
+ status = jwk->GetBytes("k", &jwk_k_value); |
if (status.IsError()) |
return status; |
raw_key_data->assign(jwk_k_value.begin(), jwk_k_value.end()); |
@@ -502,16 +476,14 @@ Status ReadSecretKeyNoExpectedAlg(const CryptoData& key_data, |
return Status::Success(); |
} |
-} // namespace |
- |
void WriteSecretKeyJwk(const CryptoData& raw_key_data, |
const std::string& algorithm, |
bool extractable, |
blink::WebCryptoKeyUsageMask usages, |
std::vector<uint8_t>* jwk_key_data) { |
JwkWriter writer(algorithm, extractable, usages, "oct"); |
- writer.SetBase64Encoded("k", raw_key_data); |
- writer.ToBytes(jwk_key_data); |
+ writer.SetBytes("k", raw_key_data); |
+ writer.ToJson(jwk_key_data); |
} |
Status ReadSecretKeyJwk(const CryptoData& key_data, |
@@ -519,12 +491,12 @@ Status ReadSecretKeyJwk(const CryptoData& key_data, |
bool expected_extractable, |
blink::WebCryptoKeyUsageMask expected_usages, |
std::vector<uint8_t>* raw_key_data) { |
- scoped_ptr<base::DictionaryValue> dict; |
+ JwkReader jwk; |
Status status = ReadSecretKeyNoExpectedAlg( |
- key_data, expected_extractable, expected_usages, raw_key_data, &dict); |
+ key_data, expected_extractable, expected_usages, raw_key_data, &jwk); |
if (status.IsError()) |
return status; |
- return VerifyAlg(dict.get(), expected_algorithm); |
+ return jwk.VerifyAlg(expected_algorithm); |
} |
std::string MakeJwkAesAlgorithmName(const std::string& suffix, |
@@ -543,15 +515,15 @@ Status ReadAesSecretKeyJwk(const CryptoData& key_data, |
bool expected_extractable, |
blink::WebCryptoKeyUsageMask expected_usages, |
std::vector<uint8_t>* raw_key_data) { |
- scoped_ptr<base::DictionaryValue> dict; |
+ JwkReader jwk; |
Status status = ReadSecretKeyNoExpectedAlg( |
- key_data, expected_extractable, expected_usages, raw_key_data, &dict); |
+ key_data, expected_extractable, expected_usages, raw_key_data, &jwk); |
if (status.IsError()) |
return status; |
bool has_jwk_alg; |
std::string jwk_alg; |
- status = GetOptionalJwkString(dict.get(), "alg", &jwk_alg, &has_jwk_alg); |
+ status = jwk.GetOptionalString("alg", &jwk_alg, &has_jwk_alg); |
if (status.IsError()) |
return status; |
@@ -581,9 +553,9 @@ void WriteRsaPublicKeyJwk(const CryptoData& n, |
blink::WebCryptoKeyUsageMask usages, |
std::vector<uint8_t>* jwk_key_data) { |
JwkWriter writer(algorithm, extractable, usages, "RSA"); |
- writer.SetBase64Encoded("n", n); |
- writer.SetBase64Encoded("e", e); |
- writer.ToBytes(jwk_key_data); |
+ writer.SetBytes("n", n); |
+ writer.SetBytes("e", e); |
+ writer.ToJson(jwk_key_data); |
} |
// Writes an RSA private key to a JWK dictionary |
@@ -601,17 +573,17 @@ void WriteRsaPrivateKeyJwk(const CryptoData& n, |
std::vector<uint8_t>* jwk_key_data) { |
JwkWriter writer(algorithm, extractable, usages, "RSA"); |
- writer.SetBase64Encoded("n", n); |
- writer.SetBase64Encoded("e", e); |
- writer.SetBase64Encoded("d", d); |
+ 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.SetBase64Encoded("p", p); |
- writer.SetBase64Encoded("q", q); |
- writer.SetBase64Encoded("dp", dp); |
- writer.SetBase64Encoded("dq", dq); |
- writer.SetBase64Encoded("qi", qi); |
- writer.ToBytes(jwk_key_data); |
+ 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) { |
@@ -625,63 +597,56 @@ Status ReadRsaKeyJwk(const CryptoData& key_data, |
bool expected_extractable, |
blink::WebCryptoKeyUsageMask expected_usages, |
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_usages, &kty, &dict); |
+ JwkReader jwk; |
+ Status status = |
+ jwk.Init(key_data, expected_extractable, expected_usages, "RSA"); |
if (status.IsError()) |
return status; |
- status = VerifyAlg(dict.get(), expected_algorithm); |
+ status = jwk.VerifyAlg(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); |
+ status = jwk.GetBigInteger("n", &result->n); |
if (status.IsError()) |
return status; |
- status = GetJwkBigInteger(dict.get(), "e", &result->e); |
+ status = jwk.GetBigInteger("e", &result->e); |
if (status.IsError()) |
return status; |
- result->is_private_key = dict->HasKey("d"); |
+ result->is_private_key = jwk.HasKey("d"); |
if (!result->is_private_key) |
return Status::Success(); |
- status = GetJwkBigInteger(dict.get(), "d", &result->d); |
+ 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 = GetJwkBigInteger(dict.get(), "p", &result->p); |
+ status = jwk.GetBigInteger("p", &result->p); |
if (status.IsError()) |
return status; |
- status = GetJwkBigInteger(dict.get(), "q", &result->q); |
+ status = jwk.GetBigInteger("q", &result->q); |
if (status.IsError()) |
return status; |
- status = GetJwkBigInteger(dict.get(), "dp", &result->dp); |
+ status = jwk.GetBigInteger("dp", &result->dp); |
if (status.IsError()) |
return status; |
- status = GetJwkBigInteger(dict.get(), "dq", &result->dq); |
+ status = jwk.GetBigInteger("dq", &result->dq); |
if (status.IsError()) |
return status; |
- status = GetJwkBigInteger(dict.get(), "qi", &result->qi); |
+ status = jwk.GetBigInteger("qi", &result->qi); |
if (status.IsError()) |
return status; |