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 <cryptohi.h> 6 #include <pk11pub.h> 7 #include <secerr.h> 8 #include <sechash.h> 9 10 #include "base/logging.h" 11 #include "base/numerics/safe_math.h" 12 #include "base/stl_util.h" 13 #include "content/child/webcrypto/algorithm_implementation.h" 14 #include "content/child/webcrypto/crypto_data.h" 15 #include "content/child/webcrypto/jwk.h" 16 #include "content/child/webcrypto/nss/key_nss.h" 17 #include "content/child/webcrypto/nss/sym_key_nss.h" 18 #include "content/child/webcrypto/nss/util_nss.h" 19 #include "content/child/webcrypto/status.h" 20 #include "content/child/webcrypto/webcrypto_util.h" 21 #include "crypto/secure_util.h" 22 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 23 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" 24 25 namespace content { 26 27 namespace webcrypto { 28 29 namespace { 30 31 const blink::WebCryptoKeyUsageMask kAllKeyUsages = 32 blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify; 33 34 bool WebCryptoHashToHMACMechanism(const blink::WebCryptoAlgorithm& algorithm, 35 CK_MECHANISM_TYPE* mechanism) { 36 switch (algorithm.id()) { 37 case blink::WebCryptoAlgorithmIdSha1: 38 *mechanism = CKM_SHA_1_HMAC; 39 return true; 40 case blink::WebCryptoAlgorithmIdSha256: 41 *mechanism = CKM_SHA256_HMAC; 42 return true; 43 case blink::WebCryptoAlgorithmIdSha384: 44 *mechanism = CKM_SHA384_HMAC; 45 return true; 46 case blink::WebCryptoAlgorithmIdSha512: 47 *mechanism = CKM_SHA512_HMAC; 48 return true; 49 default: 50 return false; 51 } 52 } 53 54 class HmacImplementation : public AlgorithmImplementation { 55 public: 56 HmacImplementation() {} 57 58 virtual Status GenerateSecretKey(const blink::WebCryptoAlgorithm& algorithm, 59 bool extractable, 60 blink::WebCryptoKeyUsageMask usage_mask, 61 blink::WebCryptoKey* key) const OVERRIDE { 62 const blink::WebCryptoHmacKeyGenParams* params = 63 algorithm.hmacKeyGenParams(); 64 65 const blink::WebCryptoAlgorithm& hash = params->hash(); 66 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; 67 if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) 68 return Status::ErrorUnsupported(); 69 70 unsigned int keylen_bits = 0; 71 Status status = GetHmacKeyGenLengthInBits(params, &keylen_bits); 72 if (status.IsError()) 73 return status; 74 75 return GenerateSecretKeyNss( 76 blink::WebCryptoKeyAlgorithm::createHmac(hash.id(), keylen_bits), 77 extractable, 78 usage_mask, 79 keylen_bits / 8, 80 mechanism, 81 key); 82 } 83 84 virtual Status VerifyKeyUsagesBeforeImportKey( 85 blink::WebCryptoKeyFormat format, 86 blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { 87 switch (format) { 88 case blink::WebCryptoKeyFormatRaw: 89 case blink::WebCryptoKeyFormatJwk: 90 return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); 91 default: 92 return Status::ErrorUnsupportedImportKeyFormat(); 93 } 94 } 95 96 virtual Status VerifyKeyUsagesBeforeGenerateKey( 97 blink::WebCryptoKeyUsageMask usage_mask) const OVERRIDE { 98 return CheckKeyCreationUsages(kAllKeyUsages, usage_mask); 99 } 100 101 virtual Status ImportKeyRaw(const CryptoData& key_data, 102 const blink::WebCryptoAlgorithm& algorithm, 103 bool extractable, 104 blink::WebCryptoKeyUsageMask usage_mask, 105 blink::WebCryptoKey* key) const OVERRIDE { 106 const blink::WebCryptoAlgorithm& hash = 107 algorithm.hmacImportParams()->hash(); 108 109 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; 110 if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) 111 return Status::ErrorUnsupported(); 112 113 base::CheckedNumeric<unsigned int> keylen_bits(key_data.byte_length()); 114 keylen_bits *= 8; 115 116 if (!keylen_bits.IsValid()) 117 return Status::ErrorDataTooLarge(); 118 119 return ImportKeyRawNss(key_data, 120 blink::WebCryptoKeyAlgorithm::createHmac( 121 hash.id(), keylen_bits.ValueOrDie()), 122 extractable, 123 usage_mask, 124 mechanism, 125 CKF_SIGN | CKF_VERIFY, 126 key); 127 } 128 129 virtual Status ImportKeyJwk(const CryptoData& key_data, 130 const blink::WebCryptoAlgorithm& algorithm, 131 bool extractable, 132 blink::WebCryptoKeyUsageMask usage_mask, 133 blink::WebCryptoKey* key) const OVERRIDE { 134 const char* algorithm_name = 135 GetJwkHmacAlgorithmName(algorithm.hmacImportParams()->hash().id()); 136 if (!algorithm_name) 137 return Status::ErrorUnexpected(); 138 139 std::vector<uint8_t> raw_data; 140 Status status = ReadSecretKeyJwk( 141 key_data, algorithm_name, extractable, usage_mask, &raw_data); 142 if (status.IsError()) 143 return status; 144 145 return ImportKeyRaw( 146 CryptoData(raw_data), algorithm, extractable, usage_mask, key); 147 } 148 149 virtual Status ExportKeyRaw(const blink::WebCryptoKey& key, 150 std::vector<uint8_t>* buffer) const OVERRIDE { 151 *buffer = SymKeyNss::Cast(key)->raw_key_data(); 152 return Status::Success(); 153 } 154 155 virtual Status ExportKeyJwk(const blink::WebCryptoKey& key, 156 std::vector<uint8_t>* buffer) const OVERRIDE { 157 SymKeyNss* sym_key = SymKeyNss::Cast(key); 158 const std::vector<uint8_t>& raw_data = sym_key->raw_key_data(); 159 160 const char* algorithm_name = 161 GetJwkHmacAlgorithmName(key.algorithm().hmacParams()->hash().id()); 162 if (!algorithm_name) 163 return Status::ErrorUnexpected(); 164 165 WriteSecretKeyJwk(CryptoData(raw_data), 166 algorithm_name, 167 key.extractable(), 168 key.usages(), 169 buffer); 170 171 return Status::Success(); 172 } 173 174 virtual Status Sign(const blink::WebCryptoAlgorithm& algorithm, 175 const blink::WebCryptoKey& key, 176 const CryptoData& data, 177 std::vector<uint8_t>* buffer) const OVERRIDE { 178 const blink::WebCryptoAlgorithm& hash = 179 key.algorithm().hmacParams()->hash(); 180 PK11SymKey* sym_key = SymKeyNss::Cast(key)->key(); 181 182 CK_MECHANISM_TYPE mechanism = CKM_INVALID_MECHANISM; 183 if (!WebCryptoHashToHMACMechanism(hash, &mechanism)) 184 return Status::ErrorUnexpected(); 185 186 SECItem param_item = {siBuffer, NULL, 0}; 187 SECItem data_item = MakeSECItemForBuffer(data); 188 // First call is to figure out the length. 189 SECItem signature_item = {siBuffer, NULL, 0}; 190 191 if (PK11_SignWithSymKey( 192 sym_key, mechanism, ¶m_item, &signature_item, &data_item) != 193 SECSuccess) { 194 return Status::OperationError(); 195 } 196 197 DCHECK_NE(0u, signature_item.len); 198 199 buffer->resize(signature_item.len); 200 signature_item.data = vector_as_array(buffer); 201 202 if (PK11_SignWithSymKey( 203 sym_key, mechanism, ¶m_item, &signature_item, &data_item) != 204 SECSuccess) { 205 return Status::OperationError(); 206 } 207 208 CHECK_EQ(buffer->size(), signature_item.len); 209 return Status::Success(); 210 } 211 212 virtual Status Verify(const blink::WebCryptoAlgorithm& algorithm, 213 const blink::WebCryptoKey& key, 214 const CryptoData& signature, 215 const CryptoData& data, 216 bool* signature_match) const OVERRIDE { 217 std::vector<uint8_t> result; 218 Status status = Sign(algorithm, key, data, &result); 219 220 if (status.IsError()) 221 return status; 222 223 // Do not allow verification of truncated MACs. 224 *signature_match = result.size() == signature.byte_length() && 225 crypto::SecureMemEqual(vector_as_array(&result), 226 signature.bytes(), 227 signature.byte_length()); 228 229 return Status::Success(); 230 } 231 }; 232 233 } // namespace 234 235 AlgorithmImplementation* CreatePlatformHmacImplementation() { 236 return new HmacImplementation; 237 } 238 239 } // namespace webcrypto 240 241 } // namespace content 242