Index: content/child/webcrypto/jwk.cc |
diff --git a/content/child/webcrypto/jwk.cc b/content/child/webcrypto/jwk.cc |
deleted file mode 100644 |
index 9c29dc2d396b85e6f807a06abf0f5933a38fa880..0000000000000000000000000000000000000000 |
--- a/content/child/webcrypto/jwk.cc |
+++ /dev/null |
@@ -1,622 +0,0 @@ |
-// 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 "content/child/webcrypto/jwk.h" |
- |
-#include <set> |
- |
-#include "base/base64.h" |
-#include "base/json/json_reader.h" |
-#include "base/json/json_writer.h" |
-#include "base/stl_util.h" |
-#include "base/strings/string_piece.h" |
-#include "base/strings/stringprintf.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) is defined by: |
-// http://tools.ietf.org/html/draft-ietf-jose-json-web-key |
-// |
-// A JWK is a simple JSON dictionary with the following members: |
-// - "kty" (Key Type) Parameter, REQUIRED |
-// - <kty-specific parameters, see below>, REQUIRED |
-// - "use" (Key Use) OPTIONAL |
-// - "key_ops" (Key Operations) OPTIONAL |
-// - "alg" (Algorithm) OPTIONAL |
-// - "ext" (Key Exportability), OPTIONAL |
-// (all other entries are ignored) |
-// |
-// The <kty-specific parameters> are defined by the JWA spec: |
-// http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms |
- |
-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; |
-// Web Crypto equivalent usage mask for JWK 'use' = 'sig'. |
-const blink::WebCryptoKeyUsageMask kJwkSigUsage = |
- blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; |
- |
-// Checks that the "ext" member 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 = jwk.GetOptionalBool("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(); |
-} |
- |
-struct JwkToWebCryptoUsageMapping { |
- const char* const jwk_key_op; |
- const blink::WebCryptoKeyUsage webcrypto_usage; |
-}; |
- |
-// Keep this ordered the same as WebCrypto's "recognized key usage |
-// values". While this is not required for spec compliance, |
-// it makes the ordering of key_ops match that of WebCrypto's Key.usages. |
-const JwkToWebCryptoUsageMapping kJwkWebCryptoUsageMap[] = { |
- {"encrypt", blink::WebCryptoKeyUsageEncrypt}, |
- {"decrypt", blink::WebCryptoKeyUsageDecrypt}, |
- {"sign", blink::WebCryptoKeyUsageSign}, |
- {"verify", blink::WebCryptoKeyUsageVerify}, |
- {"deriveKey", blink::WebCryptoKeyUsageDeriveKey}, |
- {"deriveBits", blink::WebCryptoKeyUsageDeriveBits}, |
- {"wrapKey", blink::WebCryptoKeyUsageWrapKey}, |
- {"unwrapKey", blink::WebCryptoKeyUsageUnwrapKey}}; |
- |
-bool JwkKeyOpToWebCryptoUsage(const std::string& key_op, |
- blink::WebCryptoKeyUsage* usage) { |
- for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { |
- if (kJwkWebCryptoUsageMap[i].jwk_key_op == key_op) { |
- *usage = kJwkWebCryptoUsageMap[i].webcrypto_usage; |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-// Creates a JWK key_ops list from a Web Crypto usage mask. |
-scoped_ptr<base::ListValue> CreateJwkKeyOpsFromWebCryptoUsages( |
- blink::WebCryptoKeyUsageMask usages) { |
- scoped_ptr<base::ListValue> jwk_key_ops(new base::ListValue()); |
- for (size_t i = 0; i < arraysize(kJwkWebCryptoUsageMap); ++i) { |
- if (usages & kJwkWebCryptoUsageMap[i].webcrypto_usage) |
- jwk_key_ops->AppendString(kJwkWebCryptoUsageMap[i].jwk_key_op); |
- } |
- return jwk_key_ops.Pass(); |
-} |
- |
-// Composes a Web Crypto usage mask from an array of JWK key_ops values. |
-Status GetWebCryptoUsagesFromJwkKeyOps(const base::ListValue* key_ops, |
- blink::WebCryptoKeyUsageMask* usages) { |
- // This set keeps track of all unrecognized key_ops values. |
- std::set<std::string> unrecognized_usages; |
- |
- *usages = 0; |
- for (size_t i = 0; i < key_ops->GetSize(); ++i) { |
- std::string key_op; |
- if (!key_ops->GetString(i, &key_op)) { |
- return Status::ErrorJwkMemberWrongType( |
- base::StringPrintf("key_ops[%d]", static_cast<int>(i)), "string"); |
- } |
- |
- blink::WebCryptoKeyUsage usage; |
- if (JwkKeyOpToWebCryptoUsage(key_op, &usage)) { |
- // Ensure there are no duplicate usages. |
- if (*usages & usage) |
- return Status::ErrorJwkDuplicateKeyOps(); |
- *usages |= usage; |
- } |
- |
- // Reaching here means the usage was unrecognized. Such usages are skipped |
- // over, however they are kept track of in a set to ensure there were no |
- // duplicates. |
- if (!unrecognized_usages.insert(key_op).second) |
- return Status::ErrorJwkDuplicateKeyOps(); |
- } |
- return Status::Success(); |
-} |
- |
-// 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 = |
- jwk.GetOptionalList("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 usages must be a subset of jwk_key_ops_mask. |
- if (!ContainsKeyUsages(jwk_key_ops_mask, expected_usages)) |
- return Status::ErrorJwkKeyopsInconsistent(); |
- } |
- |
- // JWK "use" (optional) --> usages parameter |
- std::string jwk_use_value; |
- bool has_jwk_use; |
- status = jwk.GetOptionalString("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 usages must be a subset of jwk_use_mask. |
- if (!ContainsKeyUsages(jwk_use_mask, expected_usages)) |
- 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(); |
-} |
- |
-} // namespace |
- |
-JwkReader::JwkReader() { |
-} |
- |
-JwkReader::~JwkReader() { |
-} |
- |
-Status JwkReader::Init(const CryptoData& bytes, |
- bool expected_extractable, |
- blink::WebCryptoKeyUsageMask expected_usages, |
- const std::string& expected_kty, |
- const std::string& expected_alg) { |
- // 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. |
- std::string kty; |
- Status status = GetString("kty", &kty); |
- if (status.IsError()) |
- return status; |
- |
- if (kty != expected_kty) |
- return Status::ErrorJwkUnexpectedKty(expected_kty); |
- |
- status = VerifyExt(*this, expected_extractable); |
- if (status.IsError()) |
- return status; |
- |
- status = VerifyUsages(*this, expected_usages); |
- if (status.IsError()) |
- return status; |
- |
- // Verify the algorithm if an expectation was provided. |
- if (!expected_alg.empty()) { |
- status = VerifyAlg(expected_alg); |
- if (status.IsError()) |
- return status; |
- } |
- |
- return Status::Success(); |
-} |
- |
-bool JwkReader::HasMember(const std::string& member_name) const { |
- return dict_->HasKey(member_name); |
-} |
- |
-Status JwkReader::GetString(const std::string& member_name, |
- std::string* result) const { |
- base::Value* value = NULL; |
- if (!dict_->Get(member_name, &value)) |
- return Status::ErrorJwkMemberMissing(member_name); |
- if (!value->GetAsString(result)) |
- return Status::ErrorJwkMemberWrongType(member_name, "string"); |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetOptionalString(const std::string& member_name, |
- std::string* result, |
- bool* member_exists) const { |
- *member_exists = false; |
- base::Value* value = NULL; |
- if (!dict_->Get(member_name, &value)) |
- return Status::Success(); |
- |
- if (!value->GetAsString(result)) |
- return Status::ErrorJwkMemberWrongType(member_name, "string"); |
- |
- *member_exists = true; |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetOptionalList(const std::string& member_name, |
- base::ListValue** result, |
- bool* member_exists) const { |
- *member_exists = false; |
- base::Value* value = NULL; |
- if (!dict_->Get(member_name, &value)) |
- return Status::Success(); |
- |
- if (!value->GetAsList(result)) |
- return Status::ErrorJwkMemberWrongType(member_name, "list"); |
- |
- *member_exists = true; |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetBytes(const std::string& member_name, |
- std::string* result) const { |
- std::string base64_string; |
- Status status = GetString(member_name, &base64_string); |
- if (status.IsError()) |
- return status; |
- |
- if (!Base64DecodeUrlSafe(base64_string, result)) |
- return Status::ErrorJwkBase64Decode(member_name); |
- |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetBigInteger(const std::string& member_name, |
- std::string* result) const { |
- Status status = GetBytes(member_name, result); |
- if (status.IsError()) |
- return status; |
- |
- if (result->empty()) |
- return Status::ErrorJwkEmptyBigInteger(member_name); |
- |
- // 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(member_name); |
- |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetOptionalBool(const std::string& member_name, |
- bool* result, |
- bool* member_exists) const { |
- *member_exists = false; |
- base::Value* value = NULL; |
- if (!dict_->Get(member_name, &value)) |
- return Status::Success(); |
- |
- if (!value->GetAsBoolean(result)) |
- return Status::ErrorJwkMemberWrongType(member_name, "boolean"); |
- |
- *member_exists = true; |
- return Status::Success(); |
-} |
- |
-Status JwkReader::GetAlg(std::string* alg, bool* has_alg) const { |
- return GetOptionalString("alg", alg, has_alg); |
-} |
- |
-Status JwkReader::VerifyAlg(const std::string& expected_alg) const { |
- bool has_jwk_alg; |
- std::string jwk_alg_value; |
- Status status = GetAlg(&jwk_alg_value, &has_jwk_alg); |
- if (status.IsError()) |
- return status; |
- |
- if (has_jwk_alg && jwk_alg_value != expected_alg) |
- return Status::ErrorJwkAlgorithmInconsistent(); |
- |
- return Status::Success(); |
-} |
- |
-JwkWriter::JwkWriter(const std::string& algorithm, |
- bool extractable, |
- blink::WebCryptoKeyUsageMask usages, |
- const std::string& kty) { |
- if (!algorithm.empty()) |
- dict_.SetString("alg", algorithm); |
- dict_.Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(usages).release()); |
- dict_.SetBoolean("ext", extractable); |
- dict_.SetString("kty", kty); |
-} |
- |
-void JwkWriter::SetString(const std::string& member_name, |
- const std::string& value) { |
- dict_.SetString(member_name, value); |
-} |
- |
-void JwkWriter::SetBytes(const std::string& member_name, |
- const CryptoData& value) { |
- dict_.SetString(member_name, 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, |
- JwkReader* jwk) { |
- Status status = jwk->Init(key_data, expected_extractable, expected_usages, |
- "oct", std::string()); |
- if (status.IsError()) |
- return status; |
- |
- std::string 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()); |
- |
- return Status::Success(); |
-} |
- |
-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.SetBytes("k", 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, |
- 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_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) |
- return false; |
- |
- std::string base64_encoded_text(input); |
- std::replace(base64_encoded_text.begin(), base64_encoded_text.end(), '-', |
- '+'); |
- std::replace(base64_encoded_text.begin(), base64_encoded_text.end(), '_', |
- '/'); |
- base64_encoded_text.append((4 - base64_encoded_text.size() % 4) % 4, '='); |
- return base::Base64Decode(base64_encoded_text, output); |
-} |
- |
-std::string Base64EncodeUrlSafe(const base::StringPiece& input) { |
- std::string output; |
- base::Base64Encode(input, &output); |
- std::replace(output.begin(), output.end(), '+', '-'); |
- std::replace(output.begin(), output.end(), '/', '_'); |
- output.erase(std::remove(output.begin(), output.end(), '='), output.end()); |
- return output; |
-} |
- |
-std::string Base64EncodeUrlSafe(const std::vector<uint8_t>& input) { |
- const base::StringPiece string_piece( |
- reinterpret_cast<const char*>(vector_as_array(&input)), input.size()); |
- return Base64EncodeUrlSafe(string_piece); |
-} |
- |
-Status GetWebCryptoUsagesFromJwkKeyOpsForTest( |
- const base::ListValue* key_ops, |
- blink::WebCryptoKeyUsageMask* usages) { |
- return GetWebCryptoUsagesFromJwkKeyOps(key_ops, usages); |
-} |
- |
-} // namespace webcrypto |
- |
-} // namespace content |