This source file includes following definitions.
- CreateRsaSsaImportAlgorithm
- CreateRsaOaepImportAlgorithm
- required_key_length_bytes_
- required_key_length_bytes_
- required_key_length_bytes_
- CreateImportAlgorithm
- IsInvalidKeyByteLength
- GetAlgorithmInfo
- BindAlgorithmId
- ImportAlgorithmsConsistent
- GetJwkString
- GetOptionalJwkString
- GetOptionalJwkList
- GetJwkBytes
- GetOptionalJwkBool
- ContainsKeyUsages
- WriteSecretKey
- WriteRsaPublicKey
- WriteKeyOps
- WriteExt
- WriteAlg
- IsRsaPublicKey
- ToPlatformPublicKey
- ImportKeyJwk
- ExportKeyJwk
#include <algorithm>
#include <functional>
#include <map>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/strings/stringprintf.h"
#include "content/child/webcrypto/crypto_data.h"
#include "content/child/webcrypto/platform_crypto.h"
#include "content/child/webcrypto/shared_crypto.h"
#include "content/child/webcrypto/status.h"
#include "content/child/webcrypto/webcrypto_util.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
namespace content {
namespace webcrypto {
namespace {
blink::WebCryptoAlgorithm CreateRsaSsaImportAlgorithm(
blink::WebCryptoAlgorithmId hash_id) {
return CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, hash_id);
}
blink::WebCryptoAlgorithm CreateRsaOaepImportAlgorithm(
blink::WebCryptoAlgorithmId hash_id) {
return CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep,
hash_id);
}
const blink::WebCryptoKeyUsageMask kJwkEncUsage =
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
blink::WebCryptoKeyUsageDeriveKey;
const blink::WebCryptoKeyUsageMask kJwkSigUsage =
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
typedef blink::WebCryptoAlgorithm (*AlgorithmCreationFunc)();
class JwkAlgorithmInfo {
public:
JwkAlgorithmInfo()
: creation_func_(NULL),
required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
explicit JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func)
: creation_func_(algorithm_creation_func),
required_key_length_bytes_(NO_KEY_SIZE_REQUIREMENT) {}
JwkAlgorithmInfo(AlgorithmCreationFunc algorithm_creation_func,
unsigned int required_key_length_bits)
: creation_func_(algorithm_creation_func),
required_key_length_bytes_(required_key_length_bits / 8) {
DCHECK_EQ(0u, required_key_length_bits % 8);
}
bool CreateImportAlgorithm(blink::WebCryptoAlgorithm* algorithm) const {
*algorithm = creation_func_();
return !algorithm->isNull();
}
bool IsInvalidKeyByteLength(size_t byte_length) const {
if (required_key_length_bytes_ == NO_KEY_SIZE_REQUIREMENT)
return false;
return required_key_length_bytes_ != byte_length;
}
private:
enum { NO_KEY_SIZE_REQUIREMENT = UINT_MAX };
AlgorithmCreationFunc creation_func_;
unsigned int required_key_length_bytes_;
};
typedef std::map<std::string, JwkAlgorithmInfo> JwkAlgorithmInfoMap;
class JwkAlgorithmRegistry {
public:
JwkAlgorithmRegistry() {
alg_to_info_["HS1"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
blink::WebCryptoAlgorithmIdSha1>);
alg_to_info_["HS256"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
blink::WebCryptoAlgorithmIdSha256>);
alg_to_info_["HS384"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
blink::WebCryptoAlgorithmIdSha384>);
alg_to_info_["HS512"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateHmacImportAlgorithm,
blink::WebCryptoAlgorithmIdSha512>);
alg_to_info_["RS1"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
blink::WebCryptoAlgorithmIdSha1>);
alg_to_info_["RS256"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
blink::WebCryptoAlgorithmIdSha256>);
alg_to_info_["RS384"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
blink::WebCryptoAlgorithmIdSha384>);
alg_to_info_["RS512"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaSsaImportAlgorithm,
blink::WebCryptoAlgorithmIdSha512>);
alg_to_info_["RSA1_5"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm,
blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5>);
alg_to_info_["RSA-OAEP"] =
JwkAlgorithmInfo(&BindAlgorithmId<CreateRsaOaepImportAlgorithm,
blink::WebCryptoAlgorithmIdSha1>);
alg_to_info_["A128KW"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
128);
alg_to_info_["A192KW"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
192);
alg_to_info_["A256KW"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesKw>,
256);
alg_to_info_["A128GCM"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
128);
alg_to_info_["A192GCM"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
192);
alg_to_info_["A256GCM"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesGcm>,
256);
alg_to_info_["A128CBC"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
128);
alg_to_info_["A192CBC"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
192);
alg_to_info_["A256CBC"] = JwkAlgorithmInfo(
&BindAlgorithmId<CreateAlgorithm, blink::WebCryptoAlgorithmIdAesCbc>,
256);
}
const JwkAlgorithmInfo* GetAlgorithmInfo(const std::string& jwk_alg) const {
const JwkAlgorithmInfoMap::const_iterator pos = alg_to_info_.find(jwk_alg);
if (pos == alg_to_info_.end())
return NULL;
return &pos->second;
}
private:
typedef blink::WebCryptoAlgorithm (*FuncWithWebCryptoAlgIdArg)(
blink::WebCryptoAlgorithmId);
template <FuncWithWebCryptoAlgIdArg func,
blink::WebCryptoAlgorithmId algorithm_id>
static blink::WebCryptoAlgorithm BindAlgorithmId() {
return func(algorithm_id);
}
JwkAlgorithmInfoMap alg_to_info_;
};
base::LazyInstance<JwkAlgorithmRegistry> jwk_alg_registry =
LAZY_INSTANCE_INITIALIZER;
bool ImportAlgorithmsConsistent(const blink::WebCryptoAlgorithm& alg1,
const blink::WebCryptoAlgorithm& alg2) {
DCHECK(!alg1.isNull());
DCHECK(!alg2.isNull());
if (alg1.id() != alg2.id())
return false;
if (alg1.paramsType() != alg2.paramsType())
return false;
switch (alg1.paramsType()) {
case blink::WebCryptoAlgorithmParamsTypeNone:
return true;
case blink::WebCryptoAlgorithmParamsTypeRsaHashedImportParams:
return ImportAlgorithmsConsistent(alg1.rsaHashedImportParams()->hash(),
alg2.rsaHashedImportParams()->hash());
case blink::WebCryptoAlgorithmParamsTypeHmacImportParams:
return ImportAlgorithmsConsistent(alg1.hmacImportParams()->hash(),
alg2.hmacImportParams()->hash());
default:
return false;
}
}
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();
}
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();
}
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();
}
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();
}
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();
}
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
blink::WebCryptoKeyUsageMask b) {
return (a & b) == b;
}
void WriteSecretKey(const blink::WebArrayBuffer& raw_key,
base::DictionaryValue* jwk_dict) {
DCHECK(jwk_dict);
jwk_dict->SetString("kty", "oct");
DCHECK(!raw_key.isNull());
DCHECK(raw_key.data());
DCHECK(raw_key.byteLength());
unsigned int key_length_bytes = raw_key.byteLength();
const base::StringPiece key_str(static_cast<const char*>(raw_key.data()),
key_length_bytes);
jwk_dict->SetString("k", Base64EncodeUrlSafe(key_str));
}
void WriteRsaPublicKey(const std::vector<uint8>& modulus,
const std::vector<uint8>& public_exponent,
base::DictionaryValue* jwk_dict) {
DCHECK(jwk_dict);
DCHECK(modulus.size());
DCHECK(public_exponent.size());
jwk_dict->SetString("kty", "RSA");
jwk_dict->SetString("n", Base64EncodeUrlSafe(modulus));
jwk_dict->SetString("e", Base64EncodeUrlSafe(public_exponent));
}
void WriteKeyOps(blink::WebCryptoKeyUsageMask key_usages,
base::DictionaryValue* jwk_dict) {
jwk_dict->Set("key_ops", CreateJwkKeyOpsFromWebCryptoUsages(key_usages));
}
void WriteExt(bool extractable, base::DictionaryValue* jwk_dict) {
jwk_dict->SetBoolean("ext", extractable);
}
Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm,
base::DictionaryValue* jwk_dict) {
switch (algorithm.paramsType()) {
case blink::WebCryptoKeyAlgorithmParamsTypeAes: {
DCHECK(algorithm.aesParams());
const char* aes_prefix = "";
switch (algorithm.aesParams()->lengthBits()) {
case 128:
aes_prefix = "A128";
break;
case 192:
aes_prefix = "A192";
break;
case 256:
aes_prefix = "A256";
break;
default:
NOTREACHED();
return Status::ErrorUnexpected();
}
const char* aes_suffix = "";
switch (algorithm.id()) {
case blink::WebCryptoAlgorithmIdAesCbc:
aes_suffix = "CBC";
break;
case blink::WebCryptoAlgorithmIdAesCtr:
aes_suffix = "CTR";
break;
case blink::WebCryptoAlgorithmIdAesGcm:
aes_suffix = "GCM";
break;
case blink::WebCryptoAlgorithmIdAesKw:
aes_suffix = "KW";
break;
default:
return Status::ErrorUnsupported();
}
jwk_dict->SetString("alg",
base::StringPrintf("%s%s", aes_prefix, aes_suffix));
break;
}
case blink::WebCryptoKeyAlgorithmParamsTypeHmac: {
DCHECK(algorithm.hmacParams());
switch (algorithm.hmacParams()->hash().id()) {
case blink::WebCryptoAlgorithmIdSha1:
jwk_dict->SetString("alg", "HS1");
break;
case blink::WebCryptoAlgorithmIdSha256:
jwk_dict->SetString("alg", "HS256");
break;
case blink::WebCryptoAlgorithmIdSha384:
jwk_dict->SetString("alg", "HS384");
break;
case blink::WebCryptoAlgorithmIdSha512:
jwk_dict->SetString("alg", "HS512");
break;
default:
NOTREACHED();
return Status::ErrorUnexpected();
}
break;
}
case blink::WebCryptoKeyAlgorithmParamsTypeRsa:
switch (algorithm.id()) {
case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5:
jwk_dict->SetString("alg", "RSA1_5");
break;
default:
NOTREACHED();
return Status::ErrorUnexpected();
}
break;
case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed:
switch (algorithm.rsaHashedParams()->hash().id()) {
case blink::WebCryptoAlgorithmIdRsaOaep:
jwk_dict->SetString("alg", "RSA-OAEP");
break;
case blink::WebCryptoAlgorithmIdSha1:
jwk_dict->SetString("alg", "RS1");
break;
case blink::WebCryptoAlgorithmIdSha256:
jwk_dict->SetString("alg", "RS256");
break;
case blink::WebCryptoAlgorithmIdSha384:
jwk_dict->SetString("alg", "RS384");
break;
case blink::WebCryptoAlgorithmIdSha512:
jwk_dict->SetString("alg", "RS512");
break;
default:
NOTREACHED();
return Status::ErrorUnexpected();
}
break;
default:
return Status::ErrorUnsupported();
}
return Status::Success();
}
bool IsRsaPublicKey(const blink::WebCryptoKey& key) {
if (key.type() != blink::WebCryptoKeyTypePublic)
return false;
const blink::WebCryptoAlgorithmId algorithm_id = key.algorithm().id();
return algorithm_id == blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5 ||
algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep;
}
Status ToPlatformPublicKey(const blink::WebCryptoKey& key,
platform::PublicKey** out) {
*out = static_cast<platform::Key*>(key.handle())->AsPublicKey();
if (!*out)
return Status::ErrorUnexpectedKeyType();
return Status::Success();
}
}
Status ImportKeyJwk(const CryptoData& key_data,
const blink::WebCryptoAlgorithm& algorithm,
bool extractable,
blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) {
if (!key_data.byte_length())
return Status::ErrorImportEmptyKeyData();
DCHECK(key);
base::StringPiece json_string(reinterpret_cast<const char*>(key_data.bytes()),
key_data.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();
std::string jwk_kty_value;
Status status = GetJwkString(dict_value, "kty", &jwk_kty_value);
if (status.IsError())
return status;
{
bool jwk_ext_value = false;
bool has_jwk_ext;
status =
GetOptionalJwkBool(dict_value, "ext", &jwk_ext_value, &has_jwk_ext);
if (status.IsError())
return status;
if (has_jwk_ext && !jwk_ext_value && extractable)
return Status::ErrorJwkExtInconsistent();
}
const JwkAlgorithmInfo* algorithm_info = NULL;
std::string jwk_alg_value;
bool has_jwk_alg;
status =
GetOptionalJwkString(dict_value, "alg", &jwk_alg_value, &has_jwk_alg);
if (status.IsError())
return status;
if (has_jwk_alg) {
blink::WebCryptoAlgorithm jwk_algorithm =
blink::WebCryptoAlgorithm::createNull();
algorithm_info = jwk_alg_registry.Get().GetAlgorithmInfo(jwk_alg_value);
if (!algorithm_info ||
!algorithm_info->CreateImportAlgorithm(&jwk_algorithm))
return Status::ErrorJwkUnrecognizedAlgorithm();
if (!ImportAlgorithmsConsistent(jwk_algorithm, algorithm))
return Status::ErrorJwkAlgorithmInconsistent();
}
DCHECK(!algorithm.isNull());
base::ListValue* jwk_key_ops_value = NULL;
bool has_jwk_key_ops;
status = GetOptionalJwkList(
dict_value, "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;
if (!ContainsKeyUsages(jwk_key_ops_mask, usage_mask))
return Status::ErrorJwkKeyopsInconsistent();
}
std::string jwk_use_value;
bool has_jwk_use;
status =
GetOptionalJwkString(dict_value, "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();
if (!ContainsKeyUsages(jwk_use_mask, usage_mask))
return Status::ErrorJwkUseInconsistent();
}
if (has_jwk_key_ops && has_jwk_use &&
!ContainsKeyUsages(jwk_use_mask, jwk_key_ops_mask))
return Status::ErrorJwkUseAndKeyopsInconsistent();
if (jwk_kty_value == "oct") {
std::string jwk_k_value;
status = GetJwkBytes(dict_value, "k", &jwk_k_value);
if (status.IsError())
return status;
if (algorithm_info &&
algorithm_info->IsInvalidKeyByteLength(jwk_k_value.size())) {
return Status::ErrorJwkIncorrectKeyLength();
}
return ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(jwk_k_value),
algorithm,
extractable,
usage_mask,
key);
}
if (jwk_kty_value == "RSA") {
if (dict_value->HasKey("d"))
return Status::ErrorJwkRsaPrivateKeyUnsupported();
std::string jwk_n_value;
status = GetJwkBytes(dict_value, "n", &jwk_n_value);
if (status.IsError())
return status;
std::string jwk_e_value;
status = GetJwkBytes(dict_value, "e", &jwk_e_value);
if (status.IsError())
return status;
return platform::ImportRsaPublicKey(algorithm,
extractable,
usage_mask,
CryptoData(jwk_n_value),
CryptoData(jwk_e_value),
key);
}
return Status::ErrorJwkUnrecognizedKty();
}
Status ExportKeyJwk(const blink::WebCryptoKey& key,
blink::WebArrayBuffer* buffer) {
DCHECK(key.extractable());
base::DictionaryValue jwk_dict;
Status status = Status::Error();
switch (key.type()) {
case blink::WebCryptoKeyTypeSecret: {
blink::WebArrayBuffer exported_key;
status = ExportKey(blink::WebCryptoKeyFormatRaw, key, &exported_key);
if (status.IsError())
return status;
WriteSecretKey(exported_key, &jwk_dict);
break;
}
case blink::WebCryptoKeyTypePublic: {
if (!IsRsaPublicKey(key))
return Status::ErrorUnsupported();
platform::PublicKey* public_key;
status = ToPlatformPublicKey(key, &public_key);
if (status.IsError())
return status;
std::vector<uint8> modulus;
std::vector<uint8> public_exponent;
status =
platform::ExportRsaPublicKey(public_key, &modulus, &public_exponent);
if (status.IsError())
return status;
WriteRsaPublicKey(modulus, public_exponent, &jwk_dict);
break;
}
case blink::WebCryptoKeyTypePrivate:
default:
return Status::ErrorUnsupported();
}
WriteKeyOps(key.usages(), &jwk_dict);
WriteExt(key.extractable(), &jwk_dict);
status = WriteAlg(key.algorithm(), &jwk_dict);
if (status.IsError())
return status;
std::string json;
base::JSONWriter::Write(&jwk_dict, &json);
*buffer = CreateArrayBuffer(reinterpret_cast<const uint8*>(json.data()),
json.size());
return Status::Success();
}
}
}