// 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/shared_crypto.h" #include "base/logging.h" #include "content/child/webcrypto/crypto_data.h" #include "content/child/webcrypto/jwk.h" #include "content/child/webcrypto/platform_crypto.h" #include "content/child/webcrypto/status.h" #include "content/child/webcrypto/webcrypto_util.h" #include "crypto/secure_util.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" #include "third_party/WebKit/public/platform/WebCryptoKey.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" namespace content { namespace webcrypto { // ------------ // Threading: // ------------ // // All functions in this file are called from the webcrypto worker pool except // for: // // * SerializeKeyForClone() // * DeserializeKeyForClone() // * ImportKey() // TODO(eroman): Change this. namespace { // TODO(eroman): Move this helper to WebCryptoKey. bool KeyUsageAllows(const blink::WebCryptoKey& key, const blink::WebCryptoKeyUsage usage) { return ((key.usages() & usage) != 0); } bool IsValidAesKeyLengthBits(unsigned int length_bits) { return length_bits == 128 || length_bits == 192 || length_bits == 256; } bool IsValidAesKeyLengthBytes(unsigned int length_bytes) { return length_bytes == 16 || length_bytes == 24 || length_bytes == 32; } Status ToPlatformSymKey(const blink::WebCryptoKey& key, platform::SymKey** out) { *out = static_cast<platform::Key*>(key.handle())->AsSymKey(); if (!*out) return Status::ErrorUnexpectedKeyType(); return Status::Success(); } 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 ToPlatformPrivateKey(const blink::WebCryptoKey& key, platform::PrivateKey** out) { *out = static_cast<platform::Key*>(key.handle())->AsPrivateKey(); if (!*out) return Status::ErrorUnexpectedKeyType(); return Status::Success(); } const size_t kAesBlockSizeBytes = 16; Status EncryptDecryptAesCbc(EncryptOrDecrypt mode, const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::SymKey* sym_key; Status status = ToPlatformSymKey(key, &sym_key); if (status.IsError()) return status; const blink::WebCryptoAesCbcParams* params = algorithm.aesCbcParams(); if (!params) return Status::ErrorUnexpected(); CryptoData iv(params->iv().data(), params->iv().size()); if (iv.byte_length() != kAesBlockSizeBytes) return Status::ErrorIncorrectSizeAesCbcIv(); return platform::EncryptDecryptAesCbc(mode, sym_key, data, iv, buffer); } Status EncryptDecryptAesGcm(EncryptOrDecrypt mode, const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::SymKey* sym_key; Status status = ToPlatformSymKey(key, &sym_key); if (status.IsError()) return status; const blink::WebCryptoAesGcmParams* params = algorithm.aesGcmParams(); if (!params) return Status::ErrorUnexpected(); unsigned int tag_length_bits = 128; if (params->hasTagLengthBits()) tag_length_bits = params->optionalTagLengthBits(); if (tag_length_bits != 32 && tag_length_bits != 64 && tag_length_bits != 96 && tag_length_bits != 104 && tag_length_bits != 112 && tag_length_bits != 120 && tag_length_bits != 128) return Status::ErrorInvalidAesGcmTagLength(); return platform::EncryptDecryptAesGcm( mode, sym_key, data, CryptoData(params->iv()), CryptoData(params->optionalAdditionalData()), tag_length_bits, buffer); } Status EncryptRsaEsPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::PublicKey* public_key; Status status = ToPlatformPublicKey(key, &public_key); if (status.IsError()) return status; // RSAES encryption does not support empty input if (!data.byte_length()) return Status::ErrorDataTooSmall(); return platform::EncryptRsaEsPkcs1v1_5(public_key, data, buffer); } Status DecryptRsaEsPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::PrivateKey* private_key; Status status = ToPlatformPrivateKey(key, &private_key); if (status.IsError()) return status; // RSAES decryption does not support empty input if (!data.byte_length()) return Status::ErrorDataTooSmall(); return platform::DecryptRsaEsPkcs1v1_5(private_key, data, buffer); } Status SignHmac(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::SymKey* sym_key; Status status = ToPlatformSymKey(key, &sym_key); if (status.IsError()) return status; return platform::SignHmac( sym_key, key.algorithm().hmacParams()->hash(), data, buffer); } Status VerifyHmac(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& signature, const CryptoData& data, bool* signature_match) { std::vector<uint8> result; Status status = SignHmac(algorithm, key, data, &result); if (status.IsError()) return status; // Do not allow verification of truncated MACs. *signature_match = result.size() == signature.byte_length() && crypto::SecureMemEqual( Uint8VectorStart(result), signature.bytes(), signature.byte_length()); return Status::Success(); } Status SignRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::PrivateKey* private_key; Status status = ToPlatformPrivateKey(key, &private_key); if (status.IsError()) return status; return platform::SignRsaSsaPkcs1v1_5( private_key, key.algorithm().rsaHashedParams()->hash(), data, buffer); } Status VerifyRsaSsaPkcs1v1_5(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& signature, const CryptoData& data, bool* signature_match) { platform::PublicKey* public_key; Status status = ToPlatformPublicKey(key, &public_key); if (status.IsError()) return status; return platform::VerifyRsaSsaPkcs1v1_5( public_key, key.algorithm().rsaHashedParams()->hash(), signature, data, signature_match); } // Note that this function may be called from the target Blink thread. Status ImportKeyRaw(const CryptoData& key_data, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdAesCtr: case blink::WebCryptoAlgorithmIdAesCbc: case blink::WebCryptoAlgorithmIdAesGcm: case blink::WebCryptoAlgorithmIdAesKw: if (!IsValidAesKeyLengthBytes(key_data.byte_length())) return Status::ErrorImportAesKeyLength(); // Fallthrough intentional! case blink::WebCryptoAlgorithmIdHmac: return platform::ImportKeyRaw( algorithm, key_data, extractable, usage_mask, key); default: return Status::ErrorUnsupported(); } } // Returns the key format to use for structured cloning. blink::WebCryptoKeyFormat GetCloneFormatForKeyType( blink::WebCryptoKeyType type) { switch (type) { case blink::WebCryptoKeyTypeSecret: return blink::WebCryptoKeyFormatRaw; case blink::WebCryptoKeyTypePublic: return blink::WebCryptoKeyFormatSpki; case blink::WebCryptoKeyTypePrivate: return blink::WebCryptoKeyFormatPkcs8; } NOTREACHED(); return blink::WebCryptoKeyFormatRaw; } // Converts a KeyAlgorithm into an equivalent Algorithm for import. blink::WebCryptoAlgorithm KeyAlgorithmToImportAlgorithm( const blink::WebCryptoKeyAlgorithm& algorithm) { switch (algorithm.paramsType()) { case blink::WebCryptoKeyAlgorithmParamsTypeAes: case blink::WebCryptoKeyAlgorithmParamsTypeRsa: return CreateAlgorithm(algorithm.id()); case blink::WebCryptoKeyAlgorithmParamsTypeHmac: return CreateHmacImportAlgorithm(algorithm.hmacParams()->hash().id()); case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: return CreateRsaHashedImportAlgorithm( algorithm.id(), algorithm.rsaHashedParams()->hash().id()); case blink::WebCryptoKeyAlgorithmParamsTypeNone: break; } return blink::WebCryptoAlgorithm::createNull(); } // There is some duplicated information in the serialized format used by // structured clone (since the KeyAlgorithm is serialized separately from the // key data). Use this extra information to further validate what was // deserialized from the key data. // // A failure here implies either a bug in the code, or that the serialized data // was corrupted. bool ValidateDeserializedKey(const blink::WebCryptoKey& key, const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type) { if (algorithm.id() != key.algorithm().id()) return false; if (key.type() != type) return false; switch (algorithm.paramsType()) { case blink::WebCryptoKeyAlgorithmParamsTypeAes: if (algorithm.aesParams()->lengthBits() != key.algorithm().aesParams()->lengthBits()) return false; break; case blink::WebCryptoKeyAlgorithmParamsTypeRsa: case blink::WebCryptoKeyAlgorithmParamsTypeRsaHashed: if (algorithm.rsaParams()->modulusLengthBits() != key.algorithm().rsaParams()->modulusLengthBits()) return false; if (algorithm.rsaParams()->publicExponent().size() != key.algorithm().rsaParams()->publicExponent().size()) return false; if (memcmp(algorithm.rsaParams()->publicExponent().data(), key.algorithm().rsaParams()->publicExponent().data(), key.algorithm().rsaParams()->publicExponent().size()) != 0) return false; break; case blink::WebCryptoKeyAlgorithmParamsTypeNone: case blink::WebCryptoKeyAlgorithmParamsTypeHmac: break; } return true; } // Validates the size of data input to AES-KW. AES-KW requires the input data // size to be at least 24 bytes and a multiple of 8 bytes. Status CheckAesKwInputSize(const CryptoData& aeskw_input_data) { if (aeskw_input_data.byte_length() < 24) return Status::ErrorDataTooSmall(); if (aeskw_input_data.byte_length() % 8) return Status::ErrorInvalidAesKwDataLength(); return Status::Success(); } Status UnwrapKeyRaw(const CryptoData& wrapped_key_data, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoAlgorithm& wrapping_algorithm, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { // TODO(padolph): Handle other wrapping algorithms switch (wrapping_algorithm.id()) { case blink::WebCryptoAlgorithmIdAesKw: { platform::SymKey* platform_wrapping_key; Status status = ToPlatformSymKey(wrapping_key, &platform_wrapping_key); if (status.IsError()) return status; status = CheckAesKwInputSize(wrapped_key_data); if (status.IsError()) return status; return platform::UnwrapSymKeyAesKw(wrapped_key_data, platform_wrapping_key, algorithm, extractable, usage_mask, key); } case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: { platform::PrivateKey* platform_wrapping_key; Status status = ToPlatformPrivateKey(wrapping_key, &platform_wrapping_key); if (status.IsError()) return status; if (!wrapped_key_data.byte_length()) return Status::ErrorDataTooSmall(); return platform::UnwrapSymKeyRsaEs(wrapped_key_data, platform_wrapping_key, algorithm, extractable, usage_mask, key); } default: return Status::ErrorUnsupported(); } } Status WrapKeyRaw(const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoKey& key_to_wrap, const blink::WebCryptoAlgorithm& wrapping_algorithm, std::vector<uint8>* buffer) { // A raw key is always a symmetric key. platform::SymKey* platform_key; Status status = ToPlatformSymKey(key_to_wrap, &platform_key); if (status.IsError()) return status; // TODO(padolph): Handle other wrapping algorithms switch (wrapping_algorithm.id()) { case blink::WebCryptoAlgorithmIdAesKw: { platform::SymKey* platform_wrapping_key; status = ToPlatformSymKey(wrapping_key, &platform_wrapping_key); if (status.IsError()) return status; return platform::WrapSymKeyAesKw( platform_wrapping_key, platform_key, buffer); } case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: { platform::PublicKey* platform_wrapping_key; status = ToPlatformPublicKey(wrapping_key, &platform_wrapping_key); if (status.IsError()) return status; return platform::WrapSymKeyRsaEs( platform_wrapping_key, platform_key, buffer); } default: return Status::ErrorUnsupported(); } } Status DecryptAesKw(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { platform::SymKey* sym_key; Status status = ToPlatformSymKey(key, &sym_key); if (status.IsError()) return status; status = CheckAesKwInputSize(data); if (status.IsError()) return status; return platform::DecryptAesKw(sym_key, data, buffer); } Status DecryptDontCheckKeyUsage(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdAesCbc: return EncryptDecryptAesCbc(DECRYPT, algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdAesGcm: return EncryptDecryptAesGcm(DECRYPT, algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: return DecryptRsaEsPkcs1v1_5(algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdAesKw: return DecryptAesKw(algorithm, key, data, buffer); default: return Status::ErrorUnsupported(); } } Status EncryptDontCheckUsage(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdAesCbc: return EncryptDecryptAesCbc(ENCRYPT, algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdAesGcm: return EncryptDecryptAesGcm(ENCRYPT, algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5: return EncryptRsaEsPkcs1v1_5(algorithm, key, data, buffer); default: return Status::ErrorUnsupported(); } } Status UnwrapKeyDecryptAndImport( blink::WebCryptoKeyFormat format, const CryptoData& wrapped_key_data, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoAlgorithm& wrapping_algorithm, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { std::vector<uint8> buffer; Status status = DecryptDontCheckKeyUsage( wrapping_algorithm, wrapping_key, wrapped_key_data, &buffer); if (status.IsError()) return status; status = ImportKey( 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( blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoKey& key_to_wrap, const blink::WebCryptoAlgorithm& wrapping_algorithm, std::vector<uint8>* buffer) { std::vector<uint8> exported_data; Status status = ExportKey(format, key_to_wrap, &exported_data); if (status.IsError()) return status; return EncryptDontCheckUsage( wrapping_algorithm, wrapping_key, CryptoData(exported_data), buffer); } // Returns the internal block size for SHA-* unsigned int ShaBlockSizeBytes(blink::WebCryptoAlgorithmId hash_id) { switch (hash_id) { case blink::WebCryptoAlgorithmIdSha1: case blink::WebCryptoAlgorithmIdSha256: return 64; case blink::WebCryptoAlgorithmIdSha384: case blink::WebCryptoAlgorithmIdSha512: return 128; default: NOTREACHED(); return 0; } } } // namespace void Init() { platform::Init(); } Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageEncrypt)) return Status::ErrorUnexpected(); return EncryptDontCheckUsage(algorithm, key, data, buffer); } Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageDecrypt)) return Status::ErrorUnexpected(); return DecryptDontCheckKeyUsage(algorithm, key, data, buffer); } Status Digest(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& data, std::vector<uint8>* buffer) { switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdSha1: case blink::WebCryptoAlgorithmIdSha256: case blink::WebCryptoAlgorithmIdSha384: case blink::WebCryptoAlgorithmIdSha512: return platform::DigestSha(algorithm.id(), data, buffer); default: return Status::ErrorUnsupported(); } } scoped_ptr<blink::WebCryptoDigestor> CreateDigestor( blink::WebCryptoAlgorithmId algorithm) { return platform::CreateDigestor(algorithm); } Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { unsigned int keylen_bytes = 0; // Get the secret key length in bytes from generation parameters. // This resolves any defaults. switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdAesCbc: case blink::WebCryptoAlgorithmIdAesGcm: case blink::WebCryptoAlgorithmIdAesKw: { if (!IsValidAesKeyLengthBits(algorithm.aesKeyGenParams()->lengthBits())) return Status::ErrorGenerateKeyLength(); keylen_bytes = algorithm.aesKeyGenParams()->lengthBits() / 8; break; } case blink::WebCryptoAlgorithmIdHmac: { const blink::WebCryptoHmacKeyGenParams* params = algorithm.hmacKeyGenParams(); DCHECK(params); if (params->hasLengthBits()) { if (params->optionalLengthBits() % 8) return Status::ErrorGenerateKeyLength(); keylen_bytes = params->optionalLengthBits() / 8; } else { keylen_bytes = ShaBlockSizeBytes(params->hash().id()); if (keylen_bytes == 0) return Status::ErrorUnsupported(); } break; } default: return Status::ErrorUnsupported(); } // TODO(eroman): Is this correct? HMAC can import zero-length keys, so should // probably be able to allowed to generate them too. if (keylen_bytes == 0) return Status::ErrorGenerateKeyLength(); return platform::GenerateSecretKey( algorithm, extractable, usage_mask, keylen_bytes, key); } Status GenerateKeyPair(const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* public_key, blink::WebCryptoKey* private_key) { // TODO(padolph): Handle other asymmetric algorithm key generation. switch (algorithm.paramsType()) { case blink::WebCryptoAlgorithmParamsTypeRsaHashedKeyGenParams: case blink::WebCryptoAlgorithmParamsTypeRsaKeyGenParams: { const blink::WebCryptoRsaKeyGenParams* params = NULL; blink::WebCryptoAlgorithm hash_or_null = blink::WebCryptoAlgorithm::createNull(); if (algorithm.rsaHashedKeyGenParams()) { params = algorithm.rsaHashedKeyGenParams(); hash_or_null = algorithm.rsaHashedKeyGenParams()->hash(); } else { params = algorithm.rsaKeyGenParams(); } if (!params->modulusLengthBits()) return Status::ErrorGenerateRsaZeroModulus(); CryptoData publicExponent(params->publicExponent()); if (!publicExponent.byte_length()) return Status::ErrorGenerateKeyPublicExponent(); return platform::GenerateRsaKeyPair(algorithm, extractable, usage_mask, params->modulusLengthBits(), publicExponent, hash_or_null, public_key, private_key); } default: return Status::ErrorUnsupported(); } } // Note that this function may be called from the target Blink thread. Status ImportKey(blink::WebCryptoKeyFormat format, const CryptoData& key_data, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { switch (format) { case blink::WebCryptoKeyFormatRaw: return ImportKeyRaw(key_data, algorithm, extractable, usage_mask, key); case blink::WebCryptoKeyFormatSpki: return platform::ImportKeySpki( algorithm, key_data, extractable, usage_mask, key); case blink::WebCryptoKeyFormatPkcs8: return platform::ImportKeyPkcs8( algorithm, key_data, extractable, usage_mask, key); case blink::WebCryptoKeyFormatJwk: return ImportKeyJwk(key_data, algorithm, extractable, usage_mask, key); default: return Status::ErrorUnsupported(); } } // TODO(eroman): Move this to anonymous namespace. Status ExportKeyDontCheckExtractability(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& key, std::vector<uint8>* buffer) { switch (format) { case blink::WebCryptoKeyFormatRaw: { platform::SymKey* sym_key; Status status = ToPlatformSymKey(key, &sym_key); if (status.IsError()) return status; return platform::ExportKeyRaw(sym_key, buffer); } case blink::WebCryptoKeyFormatSpki: { platform::PublicKey* public_key; Status status = ToPlatformPublicKey(key, &public_key); if (status.IsError()) return status; return platform::ExportKeySpki(public_key, buffer); } case blink::WebCryptoKeyFormatPkcs8: { platform::PrivateKey* private_key; Status status = ToPlatformPrivateKey(key, &private_key); if (status.IsError()) return status; return platform::ExportKeyPkcs8(private_key, key.algorithm(), buffer); } case blink::WebCryptoKeyFormatJwk: return ExportKeyJwk(key, buffer); default: return Status::ErrorUnsupported(); } } Status ExportKey(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& key, std::vector<uint8>* buffer) { if (!key.extractable()) return Status::ErrorKeyNotExtractable(); return ExportKeyDontCheckExtractability(format, key, buffer); } Status Sign(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& data, std::vector<uint8>* buffer) { if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageSign)) return Status::ErrorUnexpected(); if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdHmac: return SignHmac(algorithm, key, data, buffer); case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: return SignRsaSsaPkcs1v1_5(algorithm, key, data, buffer); default: return Status::ErrorUnsupported(); } } Status VerifySignature(const blink::WebCryptoAlgorithm& algorithm, const blink::WebCryptoKey& key, const CryptoData& signature, const CryptoData& data, bool* signature_match) { if (!KeyUsageAllows(key, blink::WebCryptoKeyUsageVerify)) return Status::ErrorUnexpected(); if (algorithm.id() != key.algorithm().id()) return Status::ErrorUnexpected(); if (!signature.byte_length()) { // None of the algorithms generate valid zero-length signatures so this // will necessarily fail verification. Early return to protect // implementations from dealing with a NULL signature pointer. *signature_match = false; return Status::Success(); } switch (algorithm.id()) { case blink::WebCryptoAlgorithmIdHmac: return VerifyHmac(algorithm, key, signature, data, signature_match); case blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5: return VerifyRsaSsaPkcs1v1_5( algorithm, key, signature, data, signature_match); default: return Status::ErrorUnsupported(); } } Status WrapKey(blink::WebCryptoKeyFormat format, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoKey& key_to_wrap, const blink::WebCryptoAlgorithm& wrapping_algorithm, std::vector<uint8>* buffer) { if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageWrapKey)) return Status::ErrorUnexpected(); if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) return Status::ErrorUnexpected(); switch (format) { case blink::WebCryptoKeyFormatRaw: return WrapKeyRaw(wrapping_key, key_to_wrap, wrapping_algorithm, buffer); case blink::WebCryptoKeyFormatJwk: return WrapKeyExportAndEncrypt( format, wrapping_key, key_to_wrap, wrapping_algorithm, buffer); case blink::WebCryptoKeyFormatSpki: case blink::WebCryptoKeyFormatPkcs8: return Status::ErrorUnsupported(); // TODO(padolph) default: NOTREACHED(); return Status::ErrorUnsupported(); } } Status UnwrapKey(blink::WebCryptoKeyFormat format, const CryptoData& wrapped_key_data, const blink::WebCryptoKey& wrapping_key, const blink::WebCryptoAlgorithm& wrapping_algorithm, const blink::WebCryptoAlgorithm& algorithm, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, blink::WebCryptoKey* key) { if (!KeyUsageAllows(wrapping_key, blink::WebCryptoKeyUsageUnwrapKey)) return Status::ErrorUnexpected(); if (wrapping_algorithm.id() != wrapping_key.algorithm().id()) return Status::ErrorUnexpected(); switch (format) { case blink::WebCryptoKeyFormatRaw: return UnwrapKeyRaw(wrapped_key_data, wrapping_key, wrapping_algorithm, algorithm, extractable, usage_mask, key); case blink::WebCryptoKeyFormatJwk: return UnwrapKeyDecryptAndImport(format, wrapped_key_data, wrapping_key, wrapping_algorithm, algorithm, extractable, usage_mask, key); case blink::WebCryptoKeyFormatSpki: case blink::WebCryptoKeyFormatPkcs8: return Status::ErrorUnsupported(); // TODO(padolph) default: NOTREACHED(); return Status::ErrorUnsupported(); } } // Note that this function is called from the target Blink thread. bool SerializeKeyForClone(const blink::WebCryptoKey& key, blink::WebVector<uint8>* key_data) { return static_cast<webcrypto::platform::Key*>(key.handle()) ->ThreadSafeSerializeForClone(key_data); } // Note that this function is called from the target Blink thread. bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable, blink::WebCryptoKeyUsageMask usage_mask, const CryptoData& key_data, blink::WebCryptoKey* key) { // TODO(eroman): This should not call into the platform crypto layer. // Otherwise it runs the risk of stalling while the NSS/OpenSSL global locks // are held. // // An alternate approach is to defer the key import until the key is used. // However this means that any deserialization errors would have to be // surfaced as WebCrypto errors, leading to slightly different behaviors. For // instance you could clone a key which fails to be deserialized. Status status = ImportKey(GetCloneFormatForKeyType(type), key_data, KeyAlgorithmToImportAlgorithm(algorithm), extractable, usage_mask, key); if (status.IsError()) return false; return ValidateDeserializedKey(*key, algorithm, type); } } // namespace webcrypto } // namespace content