1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <openssl/hmac.h> 6 7#include "base/logging.h" 8#include "base/numerics/safe_math.h" 9#include "base/stl_util.h" 10#include "content/child/webcrypto/algorithm_implementation.h" 11#include "content/child/webcrypto/crypto_data.h" 12#include "content/child/webcrypto/jwk.h" 13#include "content/child/webcrypto/openssl/key_openssl.h" 14#include "content/child/webcrypto/openssl/sym_key_openssl.h" 15#include "content/child/webcrypto/openssl/util_openssl.h" 16#include "content/child/webcrypto/status.h" 17#include "content/child/webcrypto/webcrypto_util.h" 18#include "crypto/openssl_util.h" 19#include "crypto/secure_util.h" 20#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 21#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" 22 23namespace content { 24 25namespace webcrypto { 26 27namespace { 28 29const blink::WebCryptoKeyUsageMask kAllKeyUsages = 30 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; 31 32Status SignHmac(const std::vector<uint8_t>& raw_key, 33 const blink::WebCryptoAlgorithm& hash, 34 const CryptoData& data, 35 std::vector<uint8_t>* buffer) { 36 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); 37 38 const EVP_MD* digest_algorithm = GetDigest(hash.id()); 39 if (!digest_algorithm) 40 return Status::ErrorUnsupported(); 41 unsigned int hmac_expected_length = EVP_MD_size(digest_algorithm); 42 43 // OpenSSL wierdness here. 44 // First, HMAC() needs a void* for the key data, so make one up front as a 45 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, 46 // which will result if the raw_key vector is empty; an entirely valid 47 // case. Handle this specific case by pointing to an empty array. 48 const unsigned char null_key[] = {}; 49 const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key; 50 51 buffer->resize(hmac_expected_length); 52 crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result( 53 vector_as_array(buffer), hmac_expected_length); 54 55 unsigned int hmac_actual_length; 56 unsigned char* const success = HMAC(digest_algorithm, 57 raw_key_voidp, 58 raw_key.size(), 59 data.bytes(), 60 data.byte_length(), 61 hmac_result.safe_buffer(), 62 &hmac_actual_length); 63 if (!success || hmac_actual_length != hmac_expected_length) 64 return Status::OperationError(); 65 66 return Status::Success(); 67} 68 69class HmacImplementation : public AlgorithmImplementation { 70 public: 71 HmacImplementation() {} 72 73 virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, 74 bool extractable, 75 blink::WebCryptoKeyUsageMask usage_mask, 76 blink::WebCryptoKey* key) const OVERRIDE { 77 const blink::WebCryptoHmacKeyGenParams* params = 78 algorithm.hmacKeyGenParams(); 79 80 unsigned int keylen_bits = 0; 81 Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits); 82 if (status.IsError()) 83 return status; 84 85 return GenerateSecretKeyOpenSsl(blink::WebCryptoKeyAlgorithm::createHmac( 86 params->hash().id(), keylen_bits), 87 extractable, 88 usage_mask, 89 keylen_bits / 8, 90 key); 91 } 92 93 virtual Status VerifyKeyUsagesBeforeImportKey( 94 blink::WebCryptoKeyFormat format, 95 blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { 96 switch (format) { 97 case blink::WebCryptoKeyFormatRaw: 98 case blink::WebCryptoKeyFormatJwk: 99 return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); 100 default: 101 return Status::ErrorUnsupportedImportKeyFormat(); 102 } 103 } 104 105 virtual Status VerifyKeyUsagesBeforeGenerateKey( 106 blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { 107 return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); 108 } 109 110 virtual Status ImportKeyRaw(const CryptoData& key_data, 111 const blink::WebCryptoAlgorithm& algorithm, 112 bool extractable, 113 blink::WebCryptoKeyUsageMask usage_mask, 114 blink::WebCryptoKey* key) const OVERRIDE { 115 const blink::WebCryptoAlgorithm& hash = 116 algorithm.hmacImportParams()->hash(); 117 118 base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length()); 119 keylen_bits *= 8; 120 121 if (!keylen_bits.IsValid()) 122 return Status::ErrorDataTooLarge(); 123 124 return ImportKeyRawOpenSsl(key_data, 125 blink::WebCryptoKeyAlgorithm::createHmac( 126 hash.id(), keylen_bits.ValueOrDie()), 127 extractable, 128 usage_mask, 129 key); 130 } 131 132 virtual Status ImportKeyJwk(const CryptoData& key_data, 133 const blink::WebCryptoAlgorithm& algorithm, 134 bool extractable, 135 blink::WebCryptoKeyUsageMask usage_mask, 136 blink::WebCryptoKey* key) const OVERRIDE { 137 const char* algorithm_name = 138 GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); 139 if (!algorithm_name) 140 return Status::ErrorUnexpected(); 141 142 std::vector<uint8_t> raw_data; 143 Status status = ReadSecretKeyJwk( 144 key_data, algorithm_name, extractable, usage_mask, &raw_data); 145 if (status.IsError()) 146 return status; 147 148 return ImportKeyRaw( 149 CryptoData(raw_data), algorithm, extractable, usage_mask, key); 150 } 151 152 virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, 153 std::vector<uint8_t>* buffer) const OVERRIDE { 154 *buffer = SymKeyOpenSsl::Cast(key)->raw_key_data(); 155 return Status::Success(); 156 } 157 158 virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, 159 std::vector<uint8_t>* buffer) const OVERRIDE { 160 SymKeyOpenSsl* sym_key = SymKeyOpenSsl::Cast(key); 161 const std::vector<uint8_t>& raw_data = sym_key->raw_key_data(); 162 163 const char* algorithm_name = 164 GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); 165 if (!algorithm_name) 166 return Status::ErrorUnexpected(); 167 168 WriteSecretKeyJwk(CryptoData(raw_data), 169 algorithm_name, 170 key.extractable(), 171 key.usages(), 172 buffer); 173 174 return Status::Success(); 175 } 176 177 virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, 178 const blink::WebCryptoKey& key, 179 const CryptoData& data, 180 std::vector<uint8_t>* buffer) const OVERRIDE { 181 const blink::WebCryptoAlgorithm& hash = 182 key.algorithm().hmacParams()->hash(); 183 184 return SignHmac( 185 SymKeyOpenSsl::Cast(key)->raw_key_data(), hash, data, buffer); 186 } 187 188 virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, 189 const blink::WebCryptoKey& key, 190 const CryptoData& signature, 191 const CryptoData& data, 192 bool* signature_match) const OVERRIDE { 193 std::vector<uint8_t> result; 194 Status status = Sign(algorithm, key, data, &result); 195 196 if (status.IsError()) 197 return status; 198 199 // Do not allow verification of truncated MACs. 200 *signature_match = result.size() == signature.byte_length() && 201 crypto::SecureMemEqual(vector_as_array(&result), 202 signature.bytes(), 203 signature.byte_length()); 204 205 return Status::Success(); 206 } 207}; 208 209} // namespace 210 211AlgorithmImplementation* CreatePlatformHmacImplementation() { 212 return new HmacImplementation; 213} 214 215} // namespace webcrypto 216 217} // namespace content 218