Commit 9c2e9cf7 authored by eroman@chromium.org's avatar eroman@chromium.org

[webcryto] Validate key usages during key creation.

 (1) Key creation (whether by importKey(), unwrapKey(), generateKey() now fails if the requested key usages are not applicable (for instance asking for 'sign' on an AES-CBC key).
 (2) When generating a key pair, the public/private key get the intersection of supported usages and requested ones.
 (3) The exceptions thrown during the import phase of unwrapKey() are now surfaced to the caller (bug 372944)

BUG=372040,372944,245025
R=rsleevi@chromium.org

Review URL: https://codereview.chromium.org/282133002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272630 0039d316-1c4b-4281-b951-d872f2087c98
parent a95539cf
...@@ -509,12 +509,6 @@ Status GetOptionalJwkBool(base::DictionaryValue* dict, ...@@ -509,12 +509,6 @@ Status GetOptionalJwkBool(base::DictionaryValue* dict,
return Status::Success(); return Status::Success();
} }
// Returns true if the set bits in b make up a subset of the set bits in a.
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
blink::WebCryptoKeyUsageMask b) {
return (a & b) == b;
}
// Writes a secret/symmetric key to a JWK dictionary. // Writes a secret/symmetric key to a JWK dictionary.
void WriteSecretKey(const std::vector<uint8>& raw_key, void WriteSecretKey(const std::vector<uint8>& raw_key,
base::DictionaryValue* jwk_dict) { base::DictionaryValue* jwk_dict) {
...@@ -714,9 +708,7 @@ Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm, ...@@ -714,9 +708,7 @@ Status WriteAlg(const blink::WebCryptoKeyAlgorithm& algorithm,
} }
bool IsRsaKey(const blink::WebCryptoKey& key) { bool IsRsaKey(const blink::WebCryptoKey& key) {
const blink::WebCryptoAlgorithmId algorithm_id = key.algorithm().id(); return IsAlgorithmRsa(key.algorithm().id());
return algorithm_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5 ||
algorithm_id == blink::WebCryptoAlgorithmIdRsaOaep;
} }
Status ImportRsaKey(base::DictionaryValue* dict, Status ImportRsaKey(base::DictionaryValue* dict,
...@@ -738,7 +730,19 @@ Status ImportRsaKey(base::DictionaryValue* dict, ...@@ -738,7 +730,19 @@ Status ImportRsaKey(base::DictionaryValue* dict,
if (status.IsError()) if (status.IsError())
return status; return status;
if (!dict->HasKey("d")) { bool is_public_key = !dict->HasKey("d");
// Now that the key type is known, do an additional check on the usages to
// make sure they are all applicable for this algorithm + key type.
status = CheckKeyUsages(algorithm.id(),
is_public_key ? blink::WebCryptoKeyTypePublic
: blink::WebCryptoKeyTypePrivate,
usage_mask);
if (status.IsError())
return status;
if (is_public_key) {
return platform::ImportRsaPublicKey(algorithm, return platform::ImportRsaPublicKey(algorithm,
extractable, extractable,
usage_mask, usage_mask,
......
...@@ -158,6 +158,7 @@ Status VerifyRsaSsaPkcs1v1_5(PublicKey* key, ...@@ -158,6 +158,7 @@ Status VerifyRsaSsaPkcs1v1_5(PublicKey* key,
// * algorithm.id() is for a symmetric key algorithm. // * algorithm.id() is for a symmetric key algorithm.
// * keylen_bytes is non-zero (TODO(eroman): revisit this). // * keylen_bytes is non-zero (TODO(eroman): revisit this).
// * For AES algorithms |keylen_bytes| is either 16, 24, or 32 bytes long. // * For AES algorithms |keylen_bytes| is either 16, 24, or 32 bytes long.
// * usage_mask makes sense for the algorithm.
Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask usage_mask,
...@@ -170,9 +171,11 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -170,9 +171,11 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
// is in algorithm. They are split out for convenience. // is in algorithm. They are split out for convenience.
// * modulus_length_bits is not 0 // * modulus_length_bits is not 0
// * public_exponent is not empty. // * public_exponent is not empty.
// * {public|private}_key_usage_mask make sense for the algorithm.
Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask public_key_usage_mask,
blink::WebCryptoKeyUsageMask private_key_usage_mask,
unsigned int modulus_length_bits, unsigned int modulus_length_bits,
const CryptoData& public_exponent, const CryptoData& public_exponent,
blink::WebCryptoKey* public_key, blink::WebCryptoKey* public_key,
...@@ -182,6 +185,7 @@ Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, ...@@ -182,6 +185,7 @@ Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
// * |key| is non-null. // * |key| is non-null.
// * |algorithm.id()| is for a symmetric key algorithm. // * |algorithm.id()| is for a symmetric key algorithm.
// * For AES algorithms |key_data| is either 16, 24, or 32 bytes long. // * For AES algorithms |key_data| is either 16, 24, or 32 bytes long.
// * usage_mask makes sense for the algorithm.
// Note that this may be called from target Blink thread. // Note that this may be called from target Blink thread.
Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm,
const CryptoData& key_data, const CryptoData& key_data,
...@@ -191,6 +195,7 @@ Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm, ...@@ -191,6 +195,7 @@ Status ImportKeyRaw(const blink::WebCryptoAlgorithm& algorithm,
// Preconditions: // Preconditions:
// * algorithm.id() is for an RSA algorithm. // * algorithm.id() is for an RSA algorithm.
// * usage_mask makes sense for the algorithm.
Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask usage_mask,
...@@ -203,6 +208,7 @@ Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -203,6 +208,7 @@ Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
// * modulus, public_exponent, and private_exponent will be non-empty. The // * modulus, public_exponent, and private_exponent will be non-empty. The
// others will either all be specified (non-empty), or all be unspecified // others will either all be specified (non-empty), or all be unspecified
// (empty). // (empty).
// * usage_mask makes sense for the algorithm.
Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask usage_mask,
...@@ -217,6 +223,8 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -217,6 +223,8 @@ Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
blink::WebCryptoKey* key); blink::WebCryptoKey* key);
// Note that this may be called from target Blink thread. // Note that this may be called from target Blink thread.
// Preconditions:
// * usage_mask makes sense for the algorithm.
Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm,
const CryptoData& key_data, const CryptoData& key_data,
bool extractable, bool extractable,
...@@ -224,6 +232,8 @@ Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm, ...@@ -224,6 +232,8 @@ Status ImportKeySpki(const blink::WebCryptoAlgorithm& algorithm,
blink::WebCryptoKey* key); blink::WebCryptoKey* key);
// Note that this may be called from target Blink thread. // Note that this may be called from target Blink thread.
// Preconditions:
// * usage_mask makes sense for the algorithm.
Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm, Status ImportKeyPkcs8(const blink::WebCryptoAlgorithm& algorithm,
const CryptoData& key_data, const CryptoData& key_data,
bool extractable, bool extractable,
...@@ -278,6 +288,7 @@ Status WrapSymKeyAesKw(SymKey* key, ...@@ -278,6 +288,7 @@ Status WrapSymKeyAesKw(SymKey* key,
// * |key| is non-null // * |key| is non-null
// * |wrapped_key_data| is at least 24 bytes and a multiple of 8 bytes // * |wrapped_key_data| is at least 24 bytes and a multiple of 8 bytes
// * |algorithm.id()| is for a symmetric key algorithm. // * |algorithm.id()| is for a symmetric key algorithm.
// * usage_mask makes sense for the algorithm.
Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, Status UnwrapSymKeyAesKw(const CryptoData& wrapped_key_data,
SymKey* wrapping_key, SymKey* wrapping_key,
const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoAlgorithm& algorithm,
......
...@@ -1440,7 +1440,8 @@ Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, ...@@ -1440,7 +1440,8 @@ Status EncryptDecryptAesGcm(EncryptOrDecrypt mode,
Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask public_key_usage_mask,
blink::WebCryptoKeyUsageMask private_key_usage_mask,
unsigned int modulus_length_bits, unsigned int modulus_length_bits,
const CryptoData& public_exponent, const CryptoData& public_exponent,
blink::WebCryptoKey* public_key, blink::WebCryptoKey* public_key,
...@@ -1523,12 +1524,12 @@ Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, ...@@ -1523,12 +1524,12 @@ Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
blink::WebCryptoKeyTypePublic, blink::WebCryptoKeyTypePublic,
true, true,
key_algorithm, key_algorithm,
usage_mask); public_key_usage_mask);
*private_key = blink::WebCryptoKey::create(private_key_handle.release(), *private_key = blink::WebCryptoKey::create(private_key_handle.release(),
blink::WebCryptoKeyTypePrivate, blink::WebCryptoKeyTypePrivate,
extractable, extractable,
key_algorithm, key_algorithm,
usage_mask); private_key_usage_mask);
return Status::Success(); return Status::Success();
} }
......
...@@ -300,7 +300,8 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -300,7 +300,8 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm, Status GenerateRsaKeyPair(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask public_key_usage_mask,
blink::WebCryptoKeyUsageMask private_key_usage_mask,
unsigned int modulus_length_bits, unsigned int modulus_length_bits,
const CryptoData& public_exponent, const CryptoData& public_exponent,
blink::WebCryptoKey* public_key, blink::WebCryptoKey* public_key,
......
...@@ -438,12 +438,13 @@ Status UnwrapKeyDecryptAndImport( ...@@ -438,12 +438,13 @@ Status UnwrapKeyDecryptAndImport(
wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer);
if (status.IsError()) if (status.IsError())
return status; return status;
status = ImportKey( // NOTE that returning the details of ImportKey() failures may leak
// information about the plaintext of the encrypted key (for instance the JWK
// key_ops). As long as the ImportKey error messages don't describe actual
// key bytes however this should be OK. For more discussion see
// http://crubg.com/372040
return ImportKey(
format, CryptoData(buffer), algorithm, extractable, usage_mask, key); format, CryptoData(buffer), algorithm, extractable, usage_mask, key);
// NOTE! Returning the details of any ImportKey() failure here would leak
// information about the plaintext internals of the encrypted key. Instead,
// collapse any error into the generic Status::OperationError().
return status.IsError() ? Status::OperationError() : Status::Success();
} }
Status WrapKeyExportAndEncrypt( Status WrapKeyExportAndEncrypt(
...@@ -475,6 +476,131 @@ unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) { ...@@ -475,6 +476,131 @@ unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) {
} }
} }
// Returns the mask of all key usages that are possible for |algorithm| and
// |key_type|. If the combination of |algorithm| and |key_type| doesn't make
// sense, then returns 0 (no usages).
blink::WebCryptoKeyUsageMask GetValidKeyUsagesForKeyType(
blink::WebCryptoAlgorithmId algorithm,
blink::WebCryptoKeyType key_type) {
if (IsAlgorithmAsymmetric(algorithm) ==
(key_type == blink::WebCryptoKeyTypeSecret))
return 0;
switch (algorithm) {
case blink::WebCryptoAlgorithmIdAesCbc:
case blink::WebCryptoAlgorithmIdAesGcm:
case blink::WebCryptoAlgorithmIdAesCtr:
return blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey;
case blink::WebCryptoAlgorithmIdAesKw:
return blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey;
case blink::WebCryptoAlgorithmIdHmac:
return blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5:
switch (key_type) {
case blink::WebCryptoKeyTypePublic:
return blink::WebCryptoKeyUsageVerify;
case blink::WebCryptoKeyTypePrivate:
return blink::WebCryptoKeyUsageSign;
default:
return 0;
}
case blink::WebCryptoAlgorithmIdRsaOaep:
switch (key_type) {
case blink::WebCryptoKeyTypePublic:
return blink::WebCryptoKeyUsageEncrypt |
blink::WebCryptoKeyUsageWrapKey;
case blink::WebCryptoKeyTypePrivate:
return blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageUnwrapKey;
default:
return 0;
}
default:
return 0;
}
}
// Returns Status::Success() if |usages| is a valid set of key usages for
// |algorithm| and |key_type|. Otherwise returns an error.
// In the case of JWK format the check is incomplete for asymmetric algorithms.
Status BestEffortCheckKeyUsagesForImport(blink::WebCryptoAlgorithmId algorithm,
blink::WebCryptoKeyFormat format,
blink::WebCryptoKeyUsageMask usages) {
if (!IsAlgorithmAsymmetric(algorithm))
return CheckKeyUsages(algorithm, blink::WebCryptoKeyTypeSecret, usages);
// Try to infer the key type given the import format.
blink::WebCryptoKeyType key_type;
bool key_type_known = false;
switch (format) {
case blink::WebCryptoKeyFormatRaw:
// TODO(eroman): The spec defines Diffie-Hellman raw import for public
// keys, so this will need to be updated in the future when DH is
// implemented.
return Status::ErrorUnexpected();
case blink::WebCryptoKeyFormatSpki:
key_type = blink::WebCryptoKeyTypePublic;
key_type_known = true;
break;
case blink::WebCryptoKeyFormatPkcs8:
key_type = blink::WebCryptoKeyTypePrivate;
key_type_known = true;
break;
case blink::WebCryptoKeyFormatJwk:
key_type_known = false;
break;
default:
return Status::ErrorUnexpected();
}
if (key_type_known)
return CheckKeyUsages(algorithm, key_type, usages);
// If the key type is not known, then the algorithm is asymmetric. Whether the
// key data describes a public or private key isn't known yet. But it must at
// least be ONE of those two.
DCHECK(IsAlgorithmAsymmetric(algorithm));
if (CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePublic, usages)
.IsError() &&
CheckKeyUsages(algorithm, blink::WebCryptoKeyTypePrivate, usages)
.IsError()) {
return Status::ErrorCreateKeyBadUsages();
}
return Status::Success();
}
// Returns an error if |combined_usage_mask| is invalid for generating a key
// pair for |algorithm|. Otherwise returns Status::Success(), and fills
// |public_key_usages| with the usages for the public key, and
// |private_key_usages| with those for the private key.
Status CheckKeyUsagesForGenerateKeyPair(
blink::WebCryptoAlgorithmId algorithm,
blink::WebCryptoKeyUsageMask combined_usage_mask,
blink::WebCryptoKeyUsageMask* public_key_usages,
blink::WebCryptoKeyUsageMask* private_key_usages) {
DCHECK(IsAlgorithmAsymmetric(algorithm));
blink::WebCryptoKeyUsageMask all_public_key_usages =
GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePublic);
blink::WebCryptoKeyUsageMask all_private_key_usages =
GetValidKeyUsagesForKeyType(algorithm, blink::WebCryptoKeyTypePrivate);
if (!ContainsKeyUsages(all_public_key_usages | all_private_key_usages,
combined_usage_mask))
return Status::ErrorCreateKeyBadUsages();
*public_key_usages = combined_usage_mask & all_public_key_usages;
*private_key_usages = combined_usage_mask & all_private_key_usages;
return Status::Success();
}
} // namespace } // namespace
void Init() { platform::Init(); } void Init() { platform::Init(); }
...@@ -520,6 +646,11 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -520,6 +646,11 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) { blink::WebCryptoKey* key) {
Status status =
CheckKeyUsages(algorithm.id(), blink::WebCryptoKeyTypeSecret, usage_mask);
if (status.IsError())
return status;
unsigned int keylen_bytes = 0; unsigned int keylen_bytes = 0;
// Get the secret key length in bytes from generation parameters. // Get the secret key length in bytes from generation parameters.
...@@ -564,9 +695,19 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, ...@@ -564,9 +695,19 @@ Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm,
Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask combined_usage_mask,
blink::WebCryptoKey* public_key, blink::WebCryptoKey* public_key,
blink::WebCryptoKey* private_key) { blink::WebCryptoKey* private_key) {
blink::WebCryptoKeyUsageMask public_key_usage_mask = 0;
blink::WebCryptoKeyUsageMask private_key_usage_mask = 0;
Status status = CheckKeyUsagesForGenerateKeyPair(algorithm.id(),
combined_usage_mask,
&public_key_usage_mask,
&private_key_usage_mask);
if (status.IsError())
return status;
// TODO(padolph): Handle other asymmetric algorithm key generation. // TODO(padolph): Handle other asymmetric algorithm key generation.
switch (algorithm.paramsType()) { switch (algorithm.paramsType()) {
case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: { case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: {
...@@ -582,7 +723,8 @@ Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, ...@@ -582,7 +723,8 @@ Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm,
return platform::GenerateRsaKeyPair(algorithm, return platform::GenerateRsaKeyPair(algorithm,
extractable, extractable,
usage_mask, public_key_usage_mask,
private_key_usage_mask,
params->modulusLengthBits(), params->modulusLengthBits(),
publicExponent, publicExponent,
public_key, public_key,
...@@ -600,6 +742,14 @@ Status ImportKey(blink::WebCryptoKeyFormat format, ...@@ -600,6 +742,14 @@ Status ImportKey(blink::WebCryptoKeyFormat format,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask usage_mask,
blink::WebCryptoKey* key) { blink::WebCryptoKey* key) {
// This is "best effort" because it is incomplete for JWK (for which the key
// type is not yet known). ImportKeyJwk() does extra checks on key usage once
// the key type has been determined.
Status status =
BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask);
if (status.IsError())
return status;
switch (format) { switch (format) {
case blink::WebCryptoKeyFormatRaw: case blink::WebCryptoKeyFormatRaw:
return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key);
...@@ -750,6 +900,14 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format, ...@@ -750,6 +900,14 @@ Status UnwrapKey(blink::WebCryptoKeyFormat format,
if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) if (wrapping_algorithm.id() != wrapping_key.algorithm().id())
return Status::ErrorUnexpected(); return Status::ErrorUnexpected();
// Fail-fast if the key usages don't make sense. This avoids decrypting the
// key only to then have import fail. It is "best effort" because when
// unwrapping JWK for asymmetric algorithms the key type isn't known yet.
Status status =
BestEffortCheckKeyUsagesForImport(algorithm.id(), format, usage_mask);
if (status.IsError())
return status;
switch (format) { switch (format) {
case blink::WebCryptoKeyFormatRaw: case blink::WebCryptoKeyFormatRaw:
if (wrapping_algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) { if (wrapping_algorithm.id() == blink::WebCryptoAlgorithmIdAesKw) {
...@@ -846,6 +1004,18 @@ Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, ...@@ -846,6 +1004,18 @@ Status ToPlatformPrivateKey(const blink::WebCryptoKey& key,
return Status::Success(); return Status::Success();
} }
// Returns Status::Success() if |usages| is a valid set of key usages for
// |algorithm| and |key_type|. Otherwise returns an error.
Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm,
blink::WebCryptoKeyType key_type,
blink::WebCryptoKeyUsageMask usages) {
if (!ContainsKeyUsages(GetValidKeyUsagesForKeyType(algorithm, key_type),
usages))
return Status::ErrorCreateKeyBadUsages();
return Status::Success();
}
} // namespace webcrypto } // namespace webcrypto
} // namespace content } // namespace content
...@@ -174,6 +174,12 @@ Status ToPlatformPublicKey(const blink::WebCryptoKey& key, ...@@ -174,6 +174,12 @@ Status ToPlatformPublicKey(const blink::WebCryptoKey& key,
Status ToPlatformPrivateKey(const blink::WebCryptoKey& key, Status ToPlatformPrivateKey(const blink::WebCryptoKey& key,
platform::PrivateKey** out); platform::PrivateKey** out);
// Returns Staus::Success() if |usages| is valid for |key_type| and |algorithm|.
// Otherwise returns a failure
Status CheckKeyUsages(blink::WebCryptoAlgorithmId algorithm,
blink::WebCryptoKeyType key_type,
blink::WebCryptoKeyUsageMask usages);
} // namespace webcrypto } // namespace webcrypto
} // namespace content } // namespace content
......
...@@ -457,7 +457,8 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der, ...@@ -457,7 +457,8 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der,
const std::vector<uint8>& pkcs8_der, const std::vector<uint8>& pkcs8_der,
const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoAlgorithm& algorithm,
bool extractable, bool extractable,
blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKeyUsageMask public_key_usage_mask,
blink::WebCryptoKeyUsageMask private_key_usage_mask,
blink::WebCryptoKey* public_key, blink::WebCryptoKey* public_key,
blink::WebCryptoKey* private_key) { blink::WebCryptoKey* private_key) {
ASSERT_EQ(Status::Success(), ASSERT_EQ(Status::Success(),
...@@ -465,28 +466,28 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der, ...@@ -465,28 +466,28 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der,
CryptoData(spki_der), CryptoData(spki_der),
algorithm, algorithm,
true, true,
usage_mask, public_key_usage_mask,
public_key)); public_key));
EXPECT_FALSE(public_key->isNull()); EXPECT_FALSE(public_key->isNull());
EXPECT_TRUE(public_key->handle()); EXPECT_TRUE(public_key->handle());
EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key->type()); EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key->type());
EXPECT_EQ(algorithm.id(), public_key->algorithm().id()); EXPECT_EQ(algorithm.id(), public_key->algorithm().id());
EXPECT_EQ(extractable, extractable); EXPECT_TRUE(public_key->extractable());
EXPECT_EQ(usage_mask, public_key->usages()); EXPECT_EQ(public_key_usage_mask, public_key->usages());
ASSERT_EQ(Status::Success(), ASSERT_EQ(Status::Success(),
ImportKey(blink::WebCryptoKeyFormatPkcs8, ImportKey(blink::WebCryptoKeyFormatPkcs8,
CryptoData(pkcs8_der), CryptoData(pkcs8_der),
algorithm, algorithm,
extractable, extractable,
usage_mask, private_key_usage_mask,
private_key)); private_key));
EXPECT_FALSE(private_key->isNull()); EXPECT_FALSE(private_key->isNull());
EXPECT_TRUE(private_key->handle()); EXPECT_TRUE(private_key->handle());
EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type()); EXPECT_EQ(blink::WebCryptoKeyTypePrivate, private_key->type());
EXPECT_EQ(algorithm.id(), private_key->algorithm().id()); EXPECT_EQ(algorithm.id(), private_key->algorithm().id());
EXPECT_EQ(extractable, extractable); EXPECT_EQ(extractable, private_key->extractable());
EXPECT_EQ(usage_mask, private_key->usages()); EXPECT_EQ(private_key_usage_mask, private_key->usages());
} }
Status AesGcmEncrypt(const blink::WebCryptoKey& key, Status AesGcmEncrypt(const blink::WebCryptoKey& key,
...@@ -1216,9 +1217,7 @@ TEST_F(SharedCryptoTest, ImportJwkKeyUsage) { ...@@ -1216,9 +1217,7 @@ TEST_F(SharedCryptoTest, ImportJwkKeyUsage) {
{"verify", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageVerify}, {"verify", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageVerify},
{"wrapKey", "A128KW", aes_kw_algorithm, blink::WebCryptoKeyUsageWrapKey}, {"wrapKey", "A128KW", aes_kw_algorithm, blink::WebCryptoKeyUsageWrapKey},
{"unwrapKey", "A128KW", aes_kw_algorithm, {"unwrapKey", "A128KW", aes_kw_algorithm,
blink::WebCryptoKeyUsageUnwrapKey}, blink::WebCryptoKeyUsageUnwrapKey}};
{"deriveKey", "HS256", hmac_algorithm,
blink::WebCryptoKeyUsageDeriveKey}};
for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(test_case); for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(test_case);
++test_index) { ++test_index) {
SCOPED_TRACE(test_index); SCOPED_TRACE(test_index);
...@@ -1309,13 +1308,11 @@ TEST_F(SharedCryptoTest, ImportJwkKeyUsage) { ...@@ -1309,13 +1308,11 @@ TEST_F(SharedCryptoTest, ImportJwkKeyUsage) {
blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageEncrypt |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageUnwrapKey,
blink::WebCryptoKeyUsageDeriveKey,
&key)); &key));
EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageUnwrapKey,
blink::WebCryptoKeyUsageDeriveKey,
key.usages()); key.usages());
} }
...@@ -1476,22 +1473,22 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { ...@@ -1476,22 +1473,22 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) {
{CreateRsaHashedImportAlgorithm( {CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha1), blink::WebCryptoAlgorithmIdSha1),
blink::WebCryptoKeyUsageSign, "RS1"}, blink::WebCryptoKeyUsageVerify, "RS1"},
// RSASSA-PKCS1-v1_5 SHA-256 // RSASSA-PKCS1-v1_5 SHA-256
{CreateRsaHashedImportAlgorithm( {CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256), blink::WebCryptoAlgorithmIdSha256),
blink::WebCryptoKeyUsageSign, "RS256"}, blink::WebCryptoKeyUsageVerify, "RS256"},
// RSASSA-PKCS1-v1_5 SHA-384 // RSASSA-PKCS1-v1_5 SHA-384
{CreateRsaHashedImportAlgorithm( {CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha384), blink::WebCryptoAlgorithmIdSha384),
blink::WebCryptoKeyUsageSign, "RS384"}, blink::WebCryptoKeyUsageVerify, "RS384"},
// RSASSA-PKCS1-v1_5 SHA-512 // RSASSA-PKCS1-v1_5 SHA-512
{CreateRsaHashedImportAlgorithm( {CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha512), blink::WebCryptoAlgorithmIdSha512),
blink::WebCryptoKeyUsageSign, "RS512"}, blink::WebCryptoKeyUsageVerify, "RS512"},
// RSA-OAEP with SHA-1 and MGF-1 / SHA-1 // RSA-OAEP with SHA-1 and MGF-1 / SHA-1
{CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep, {CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep,
blink::WebCryptoAlgorithmIdSha1), blink::WebCryptoAlgorithmIdSha1),
...@@ -1546,7 +1543,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { ...@@ -1546,7 +1543,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) {
CryptoData(jwk), test.algorithm, true, test.usage, &public_key2)); CryptoData(jwk), test.algorithm, true, test.usage, &public_key2));
ASSERT_TRUE(public_key2.handle()); ASSERT_TRUE(public_key2.handle());
EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type());
EXPECT_EQ(true, public_key2.extractable()); EXPECT_TRUE(public_key2.extractable());
EXPECT_EQ(test.algorithm.id(), public_key2.algorithm().id()); EXPECT_EQ(test.algorithm.id(), public_key2.algorithm().id());
// Only perform SPKI consistency test for RSA-SSA as its // Only perform SPKI consistency test for RSA-SSA as its
...@@ -1679,7 +1676,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) { ...@@ -1679,7 +1676,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
ImportKeyJwk(CryptoData(json_vec), ImportKeyJwk(CryptoData(json_vec),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
extractable, extractable,
usage_mask, blink::WebCryptoKeyUsageEncrypt,
&key)); &key));
// Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value // Fail: Input algorithm (HMAC SHA1) is inconsistent with JWK value
...@@ -1705,8 +1702,8 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) { ...@@ -1705,8 +1702,8 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
dict.SetString("alg", "HS256"); dict.SetString("alg", "HS256");
// Fail: Input usage_mask (encrypt) is not a subset of the JWK value // Fail: Input usage_mask (encrypt) is not a subset of the JWK value
// (sign|verify) // (sign|verify). Moreover "encrypt" is not a valid usage for HMAC.
EXPECT_EQ(Status::ErrorJwkUseInconsistent(), EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKeyJwk(CryptoData(json_vec), ImportKeyJwk(CryptoData(json_vec),
algorithm, algorithm,
extractable, extractable,
...@@ -1714,11 +1711,11 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) { ...@@ -1714,11 +1711,11 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
&key)); &key));
// Fail: Input usage_mask (encrypt|sign|verify) is not a subset of the JWK // Fail: Input usage_mask (encrypt|sign|verify) is not a subset of the JWK
// value (sign|verify) // value (sign|verify). Moreover "encrypt" is not a valid usage for HMAC.
usage_mask = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign | usage_mask = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign |
blink::WebCryptoKeyUsageVerify; blink::WebCryptoKeyUsageVerify;
EXPECT_EQ( EXPECT_EQ(
Status::ErrorJwkUseInconsistent(), Status::ErrorCreateKeyBadUsages(),
ImportKeyJwk( ImportKeyJwk(
CryptoData(json_vec), algorithm, extractable, usage_mask, &key)); CryptoData(json_vec), algorithm, extractable, usage_mask, &key));
...@@ -2078,8 +2075,11 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportPkcs8)) { ...@@ -2078,8 +2075,11 @@ TEST_F(SharedCryptoTest, MAYBE(ImportExportPkcs8)) {
blink::WebCryptoKeyUsageSign, blink::WebCryptoKeyUsageSign,
&key)); &key));
// Failing case: Import RSA key but provide an inconsistent input algorithm. // Failing case: Import RSA key but provide an inconsistent input algorithm
EXPECT_EQ(Status::DataError(), // and usage. Several issues here:
// * AES-CBC doesn't support PKCS8 key format
// * AES-CBC doesn't support "sign" usage
EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::WebCryptoKeyFormatPkcs8, ImportKey(blink::WebCryptoKeyFormatPkcs8,
CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)), CryptoData(HexStringToBytes(kPrivateKeyPkcs8DerHex)),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc), CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
...@@ -2414,8 +2414,6 @@ TEST_F(SharedCryptoTest, MAYBE(GenerateKeyPairRsa)) { ...@@ -2414,8 +2414,6 @@ TEST_F(SharedCryptoTest, MAYBE(GenerateKeyPairRsa)) {
TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) {
// Import a key pair. // Import a key pair.
blink::WebCryptoKeyUsageMask usage_mask =
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify;
blink::WebCryptoAlgorithm importAlgorithm = blink::WebCryptoAlgorithm importAlgorithm =
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha1); blink::WebCryptoAlgorithmIdSha1);
...@@ -2426,7 +2424,8 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { ...@@ -2426,7 +2424,8 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) {
HexStringToBytes(kPrivateKeyPkcs8DerHex), HexStringToBytes(kPrivateKeyPkcs8DerHex),
importAlgorithm, importAlgorithm,
false, false,
usage_mask, blink::WebCryptoKeyUsageVerify,
blink::WebCryptoKeyUsageSign,
&public_key, &public_key,
&private_key)); &private_key));
...@@ -2483,18 +2482,6 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { ...@@ -2483,18 +2482,6 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) {
&signature_match)); &signature_match));
EXPECT_FALSE(signature_match); EXPECT_FALSE(signature_match);
// Ensure that verifying using a private key, rather than a public key, fails.
EXPECT_EQ(Status::ErrorUnexpectedKeyType(),
VerifySignature(algorithm,
private_key,
CryptoData(signature),
CryptoData(data),
&signature_match));
// Ensure that signing using a public key, rather than a private key, fails.
EXPECT_EQ(Status::ErrorUnexpectedKeyType(),
Sign(algorithm, public_key, CryptoData(data), &signature));
// Ensure that signing and verifying with an incompatible algorithm fails. // Ensure that signing and verifying with an incompatible algorithm fails.
algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep); algorithm = CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaOaep);
...@@ -2531,7 +2518,7 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) { ...@@ -2531,7 +2518,7 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSsaSignVerifyFailures)) {
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256), blink::WebCryptoAlgorithmIdSha256),
true, true,
usage_mask, blink::WebCryptoKeyUsageVerify,
&public_key_256)); &public_key_256));
// Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The // Now verify using an algorithm whose inner hash is SHA-256, not SHA-1. The
...@@ -2565,14 +2552,15 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSignVerifyKnownAnswer)) { ...@@ -2565,14 +2552,15 @@ TEST_F(SharedCryptoTest, MAYBE(RsaSignVerifyKnownAnswer)) {
blink::WebCryptoAlgorithmIdSha1); blink::WebCryptoAlgorithmIdSha1);
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( ASSERT_NO_FATAL_FAILURE(
HexStringToBytes(kPublicKeySpkiDerHex), ImportRsaKeyPair(HexStringToBytes(kPublicKeySpkiDerHex),
HexStringToBytes(kPrivateKeyPkcs8DerHex), HexStringToBytes(kPrivateKeyPkcs8DerHex),
importAlgorithm, importAlgorithm,
false, false,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageVerify,
&public_key, blink::WebCryptoKeyUsageSign,
&private_key)); &public_key,
&private_key));
blink::WebCryptoAlgorithm algorithm = blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5); CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5);
...@@ -2704,22 +2692,7 @@ TEST_F(SharedCryptoTest, MAYBE(UnwrapFailures)) { ...@@ -2704,22 +2692,7 @@ TEST_F(SharedCryptoTest, MAYBE(UnwrapFailures)) {
const std::vector<uint8> test_ciphertext = const std::vector<uint8> test_ciphertext =
GetBytesFromHexString(test, "ciphertext"); GetBytesFromHexString(test, "ciphertext");
// Using a key that does not have unwrapKey usage should fail.
blink::WebCryptoKey bad_wrapping_key = ImportSecretKeyFromRaw(
test_kek,
webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw),
blink::WebCryptoKeyUsageDecrypt); // <-- should be UnwrapKey
blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull(); blink::WebCryptoKey unwrapped_key = blink::WebCryptoKey::createNull();
EXPECT_EQ(
Status::ErrorUnexpected(),
UnwrapKey(blink::WebCryptoKeyFormatRaw,
CryptoData(test_ciphertext),
bad_wrapping_key,
webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw),
webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
true,
blink::WebCryptoKeyUsageEncrypt,
&unwrapped_key));
// Using a wrapping algorithm that does not match the wrapping key algorithm // Using a wrapping algorithm that does not match the wrapping key algorithm
// should fail. // should fail.
...@@ -3315,14 +3288,14 @@ TEST_F(SharedCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) { ...@@ -3315,14 +3288,14 @@ TEST_F(SharedCryptoRsaOaepTest, EncryptDecryptKnownAnswerTest) {
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull(); blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair( ASSERT_NO_FATAL_FAILURE(ImportRsaKeyPair(public_key_der,
public_key_der, private_key_der,
private_key_der, import_algorithm,
import_algorithm, false,
false, blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt, blink::WebCryptoKeyUsageDecrypt,
&public_key, &public_key,
&private_key)); &private_key));
blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label); blink::WebCryptoAlgorithm op_algorithm = CreateRsaOaepAlgorithm(label);
std::vector<uint8> decrypted_data; std::vector<uint8> decrypted_data;
...@@ -3497,8 +3470,8 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapRawKey) { ...@@ -3497,8 +3470,8 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapRawKey) {
HexStringToBytes(kPrivateKeyPkcs8DerHex), HexStringToBytes(kPrivateKeyPkcs8DerHex),
import_algorithm, import_algorithm,
false, false,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
&public_key, &public_key,
&private_key)); &private_key));
...@@ -3615,8 +3588,8 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { ...@@ -3615,8 +3588,8 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) {
HexStringToBytes(kPrivateKey2048Pkcs8DerHex), HexStringToBytes(kPrivateKey2048Pkcs8DerHex),
import_algorithm, import_algorithm,
false, false,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
&public_key, &public_key,
&private_key)); &private_key));
...@@ -3671,6 +3644,322 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) { ...@@ -3671,6 +3644,322 @@ TEST_F(SharedCryptoRsaOaepTest, WrapUnwrapJwkSymKey) {
EXPECT_BYTES_EQ_HEX(key_hex, raw_key); EXPECT_BYTES_EQ_HEX(key_hex, raw_key);
} }
// Try importing an RSA-SSA public key with unsupported key usages using SPKI
// format. RSA-SSA public keys only support the 'verify' usage.
TEST_F(SharedCryptoTest, MAYBE(ImportRsaSsaPublicKeyBadUsage_SPKI)) {
const blink::WebCryptoAlgorithm algorithm =
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify,
blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
};
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::WebCryptoKeyFormatSpki,
CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
algorithm,
false,
bad_usages[i],
&public_key));
}
}
// Try importing an RSA-SSA public key with unsupported key usages using JWK
// format. RSA-SSA public keys only support the 'verify' usage.
TEST_F(SharedCryptoTest, MAYBE(ImportRsaSsaPublicKeyBadUsage_JWK)) {
const blink::WebCryptoAlgorithm algorithm =
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify,
blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
};
base::DictionaryValue dict;
RestoreJwkRsaDictionary(&dict);
dict.Remove("use", NULL);
dict.SetString("alg", "RS256");
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKeyJwkFromDict(
dict, algorithm, false, bad_usages[i], &public_key));
}
}
// Try importing an AES-CBC key with unsupported key usages using raw
// format. AES-CBC keys support the following usages:
// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST_F(SharedCryptoTest, MAYBE(ImportAesCbcKeyBadUsage_Raw)) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageDeriveBits,
blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify,
};
std::vector<uint8> key_bytes(16);
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(key_bytes),
algorithm,
true,
bad_usages[i],
&key));
}
}
// Try importing an AES-KW key with unsupported key usages using raw
// format. AES-KW keys support the following usages:
// 'wrapKey', 'unwrapKey'
TEST_F(SharedCryptoTest, MAYBE(ImportAesKwKeyBadUsage_Raw)) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageUnwrapKey,
blink::WebCryptoKeyUsageDeriveBits,
blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify,
};
std::vector<uint8> key_bytes(16);
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(key_bytes),
algorithm,
true,
bad_usages[i],
&key));
}
}
// Try unwrapping an HMAC key with unsupported usages using JWK format and
// AES-KW. HMAC keys support the following usages:
// 'sign', 'verify'
TEST_F(SharedCryptoTest, MAYBE(UnwrapHmacKeyBadUsage_JWK)) {
const blink::WebCryptoAlgorithm unwrap_algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageWrapKey,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey,
blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDeriveKey,
};
// Import the wrapping key.
blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::Success(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(std::vector<uint8>(16)),
unwrap_algorithm,
true,
blink::WebCryptoKeyUsageUnwrapKey,
&wrapping_key));
// The JWK plain text is:
// { "kty": "oct","alg": "HS256","k": "GADWrMRHwQfoNaXU5fZvTg=="}
const char* kWrappedJwk =
"0AA245F17064FFB2A7A094436A39BEBFC962C627303D1327EA750CE9F917688C2782A943"
"7AE7586547AC490E8AE7D5B02D63868D5C3BB57D36C4C8C5BF3962ACEC6F42E767E5706"
"4";
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
UnwrapKey(blink::WebCryptoKeyFormatJwk,
CryptoData(HexStringToBytes(kWrappedJwk)),
wrapping_key,
unwrap_algorithm,
webcrypto::CreateHmacImportAlgorithm(
blink::WebCryptoAlgorithmIdSha256),
true,
bad_usages[i],
&key));
}
}
// Try unwrapping an RSA-SSA public key with unsupported usages using JWK format
// and AES-KW. RSA-SSA public keys support the following usages:
// 'verify'
TEST_F(SharedCryptoTest, MAYBE(UnwrapRsaSsaPublicKeyBadUsage_JWK)) {
const blink::WebCryptoAlgorithm unwrap_algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageEncrypt,
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageWrapKey,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageWrapKey,
};
// Import the wrapping key.
blink::WebCryptoKey wrapping_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::Success(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(std::vector<uint8>(16)),
unwrap_algorithm,
true,
blink::WebCryptoKeyUsageUnwrapKey,
&wrapping_key));
// The JWK plaintext is:
// { "kty": "RSA","alg": "RS256","n": "...","e": "AQAB"}
const char* kWrappedJwk =
"CE8DAEF99E977EE58958B8C4494755C846E883B2ECA575C5366622839AF71AB30875F152"
"E8E33E15A7817A3A2874EB53EFE05C774D98BC936BA9BA29BEB8BB3F3C3CE2323CB3359D"
"E3F426605CF95CCF0E01E870ABD7E35F62E030B5FB6E520A5885514D1D850FB64B57806D"
"1ADA57C6E27DF345D8292D80F6B074F1BE51C4CF3D76ECC8886218551308681B44FAC60B"
"8CF6EA439BC63239103D0AE81ADB96F908680586C6169284E32EB7DD09D31103EBDAC0C2"
"40C72DCF0AEA454113CC47457B13305B25507CBEAB9BDC8D8E0F867F9167F9DCEF0D9F9B"
"30F2EE83CEDFD51136852C8A5939B768";
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
UnwrapKey(blink::WebCryptoKeyFormatJwk,
CryptoData(HexStringToBytes(kWrappedJwk)),
wrapping_key,
unwrap_algorithm,
webcrypto::CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256),
true,
bad_usages[i],
&key));
}
}
// Generate an AES-CBC key with invalid usages. AES-CBC supports:
// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST_F(SharedCryptoTest, MAYBE(GenerateAesKeyBadUsages)) {
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign, blink::WebCryptoKeyUsageVerify,
blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageVerify,
};
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
GenerateSecretKey(
CreateAesCbcKeyGenAlgorithm(128), true, bad_usages[i], &key));
}
}
// Generate an RSA-SSA key pair with invalid usages. RSA-SSA supports:
// 'sign', 'verify'
TEST_F(SharedCryptoTest, MAYBE(GenerateRsaSsaBadUsages)) {
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageVerify | blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageWrapKey,
};
const unsigned int modulus_length = 256;
const std::vector<uint8> public_exponent = HexStringToBytes("010001");
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256,
modulus_length,
public_exponent),
true,
bad_usages[i],
&public_key,
&private_key));
}
}
// Generate an RSA-SSA key pair. The public and private keys should select the
// key usages which are applicable, and not have the exact same usages as was
// specified to GenerateKey
TEST_F(SharedCryptoTest, MAYBE(GenerateRsaSsaKeyPairIntersectUsages)) {
const unsigned int modulus_length = 256;
const std::vector<uint8> public_exponent = HexStringToBytes("010001");
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ASSERT_EQ(Status::Success(),
GenerateKeyPair(
CreateRsaHashedKeyGenAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256,
modulus_length,
public_exponent),
true,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify,
&public_key,
&private_key));
EXPECT_EQ(blink::WebCryptoKeyUsageVerify, public_key.usages());
EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages());
// Try again but this time without the Verify usages.
ASSERT_EQ(Status::Success(),
GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256,
modulus_length,
public_exponent),
true,
blink::WebCryptoKeyUsageSign,
&public_key,
&private_key));
EXPECT_EQ(0, public_key.usages());
EXPECT_EQ(blink::WebCryptoKeyUsageSign, private_key.usages());
}
} // namespace webcrypto } // namespace webcrypto
} // namespace content } // namespace content
...@@ -198,6 +198,11 @@ Status Status::ErrorGenerateKeyLength() { ...@@ -198,6 +198,11 @@ Status Status::ErrorGenerateKeyLength() {
"bits"); "bits");
} }
Status Status::ErrorCreateKeyBadUsages() {
return Status(blink::WebCryptoErrorTypeData,
"Cannot create a key using the specified key usages.");
}
Status::Status(blink::WebCryptoErrorType error_type, Status::Status(blink::WebCryptoErrorType error_type,
const std::string& error_details_utf8) const std::string& error_details_utf8)
: type_(TYPE_ERROR), : type_(TYPE_ERROR),
......
...@@ -19,11 +19,6 @@ namespace webcrypto { ...@@ -19,11 +19,6 @@ namespace webcrypto {
// //
// As such, it is important that errors DO NOT reveal any sensitive material // As such, it is important that errors DO NOT reveal any sensitive material
// (like key bytes). // (like key bytes).
//
// Care must be taken with what errors are reported back to Blink when doing
// compound operations like unwrapping a JWK key. In this case, errors
// generated by the JWK import are not appropriate to report since the wrapped
// JWK is not visible to the caller.
class CONTENT_EXPORT Status { class CONTENT_EXPORT Status {
public: public:
Status() : type_(TYPE_ERROR) {} Status() : type_(TYPE_ERROR) {}
...@@ -187,6 +182,11 @@ class CONTENT_EXPORT Status { ...@@ -187,6 +182,11 @@ class CONTENT_EXPORT Status {
// zero, or it was not a multiple of 8 bits. // zero, or it was not a multiple of 8 bits.
static Status ErrorGenerateKeyLength(); static Status ErrorGenerateKeyLength();
// Attempted to create a key (either by importKey(), generateKey(), or
// unwrapKey()) however the key usages were not applicable for the key type
// and algorithm.
static Status ErrorCreateKeyBadUsages();
private: private:
enum Type { TYPE_ERROR, TYPE_SUCCESS }; enum Type { TYPE_ERROR, TYPE_SUCCESS };
......
...@@ -134,12 +134,6 @@ void CompleteWithKeyOrError(const Status& status, ...@@ -134,12 +134,6 @@ void CompleteWithKeyOrError(const Status& status,
} }
} }
bool IsAlgorithmAsymmetric(const blink::WebCryptoAlgorithm& algorithm) {
// TODO(padolph): include all other asymmetric algorithms once they are
// defined, e.g. EC and DH.
return webcrypto::IsAlgorithmRsa(algorithm.id());
}
// Gets a task runner for the current thread. The current thread is either: // Gets a task runner for the current thread. The current thread is either:
// //
// * The main Blink thread // * The main Blink thread
...@@ -405,7 +399,8 @@ void DoGenerateKeyReply(scoped_ptr<GenerateKeyState> state) { ...@@ -405,7 +399,8 @@ void DoGenerateKeyReply(scoped_ptr<GenerateKeyState> state) {
void DoGenerateKey(scoped_ptr<GenerateKeyState> passed_state) { void DoGenerateKey(scoped_ptr<GenerateKeyState> passed_state) {
GenerateKeyState* state = passed_state.get(); GenerateKeyState* state = passed_state.get();
state->is_asymmetric = IsAlgorithmAsymmetric(state->algorithm); state->is_asymmetric =
webcrypto::IsAlgorithmAsymmetric(state->algorithm.id());
if (state->is_asymmetric) { if (state->is_asymmetric) {
state->status = webcrypto::GenerateKeyPair(state->algorithm, state->status = webcrypto::GenerateKeyPair(state->algorithm,
state->extractable, state->extractable,
...@@ -420,8 +415,6 @@ void DoGenerateKey(scoped_ptr<GenerateKeyState> passed_state) { ...@@ -420,8 +415,6 @@ void DoGenerateKey(scoped_ptr<GenerateKeyState> passed_state) {
DCHECK_EQ(state->algorithm.id(), state->private_key.algorithm().id()); DCHECK_EQ(state->algorithm.id(), state->private_key.algorithm().id());
DCHECK_EQ(true, state->public_key.extractable()); DCHECK_EQ(true, state->public_key.extractable());
DCHECK_EQ(state->extractable, state->private_key.extractable()); DCHECK_EQ(state->extractable, state->private_key.extractable());
DCHECK_EQ(state->usage_mask, state->public_key.usages());
DCHECK_EQ(state->usage_mask, state->private_key.usages());
} }
} else { } else {
blink::WebCryptoKey* key = &state->public_key; blink::WebCryptoKey* key = &state->public_key;
......
...@@ -179,11 +179,22 @@ bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, ...@@ -179,11 +179,22 @@ bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm,
} }
} }
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
blink::WebCryptoKeyUsageMask b) {
return (a & b) == b;
}
bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) { bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id) {
return alg_id == blink::WebCryptoAlgorithmIdRsaOaep || return alg_id == blink::WebCryptoAlgorithmIdRsaOaep ||
alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5; alg_id == blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5;
} }
bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id) {
// TODO(padolph): include all other asymmetric algorithms once they are
// defined, e.g. EC and DH.
return IsAlgorithmRsa(alg_id);
}
} // namespace webcrypto } // namespace webcrypto
} // namespace content } // namespace content
...@@ -71,7 +71,12 @@ bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm, ...@@ -71,7 +71,12 @@ bool CreateSecretKeyAlgorithm(const blink::WebCryptoAlgorithm& algorithm,
unsigned int keylen_bytes, unsigned int keylen_bytes,
blink::WebCryptoKeyAlgorithm* key_algorithm); blink::WebCryptoKeyAlgorithm* key_algorithm);
// Returns true if the set bits in b make up a subset of the set bits in a.
bool ContainsKeyUsages(blink::WebCryptoKeyUsageMask a,
blink::WebCryptoKeyUsageMask b);
bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id); bool IsAlgorithmRsa(blink::WebCryptoAlgorithmId alg_id);
bool IsAlgorithmAsymmetric(blink::WebCryptoAlgorithmId alg_id);
} // namespace webcrypto } // namespace webcrypto
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment