1// Copyright 2013 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 "content/renderer/webcrypto/webcrypto_impl.h" 6 7#include <vector> 8#include <openssl/aes.h> 9#include <openssl/evp.h> 10#include <openssl/hmac.h> 11#include <openssl/sha.h> 12#include <openssl/evp.h> 13#include <openssl/rand.h> 14 15#include "base/logging.h" 16#include "content/renderer/webcrypto/webcrypto_util.h" 17#include "crypto/openssl_util.h" 18#include "crypto/secure_util.h" 19#include "third_party/WebKit/public/platform/WebArrayBuffer.h" 20#include "third_party/WebKit/public/platform/WebCryptoAlgorithm.h" 21#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 22 23namespace content { 24 25namespace { 26 27class SymKeyHandle : public blink::WebCryptoKeyHandle { 28 public: 29 SymKeyHandle(const unsigned char* key_data, unsigned key_data_size) 30 : key_(key_data, key_data + key_data_size) {} 31 32 const std::vector<unsigned char>& key() const { return key_; } 33 34 private: 35 const std::vector<unsigned char> key_; 36 37 DISALLOW_COPY_AND_ASSIGN(SymKeyHandle); 38}; 39 40const EVP_CIPHER* GetAESCipherByKeyLength(unsigned key_length_bytes) { 41 // OpenSSL supports AES CBC ciphers for only 3 key lengths: 128, 192, 256 bits 42 switch (key_length_bytes) { 43 case 16: 44 return EVP_aes_128_cbc(); 45 case 24: 46 return EVP_aes_192_cbc(); 47 case 32: 48 return EVP_aes_256_cbc(); 49 default: 50 return NULL; 51 } 52} 53 54unsigned WebCryptoHmacParamsToBlockSize( 55 const blink::WebCryptoHmacKeyParams* params) { 56 DCHECK(params); 57 switch (params->hash().id()) { 58 case blink::WebCryptoAlgorithmIdSha1: 59 return SHA_DIGEST_LENGTH / 8; 60 case blink::WebCryptoAlgorithmIdSha224: 61 return SHA224_DIGEST_LENGTH / 8; 62 case blink::WebCryptoAlgorithmIdSha256: 63 return SHA256_DIGEST_LENGTH / 8; 64 case blink::WebCryptoAlgorithmIdSha384: 65 return SHA384_DIGEST_LENGTH / 8; 66 case blink::WebCryptoAlgorithmIdSha512: 67 return SHA512_DIGEST_LENGTH / 8; 68 default: 69 return 0; 70 } 71} 72 73// OpenSSL constants for EVP_CipherInit_ex(), do not change 74enum CipherOperation { 75 kDoDecrypt = 0, 76 kDoEncrypt = 1 77}; 78 79bool AesCbcEncryptDecrypt(CipherOperation cipher_operation, 80 const blink::WebCryptoAlgorithm& algorithm, 81 const blink::WebCryptoKey& key, 82 const unsigned char* data, 83 unsigned data_size, 84 blink::WebArrayBuffer* buffer) { 85 86 // TODO(padolph): Handle other encrypt operations and then remove this gate 87 if (algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) 88 return false; 89 90 DCHECK_EQ(algorithm.id(), key.algorithm().id()); 91 DCHECK_EQ(blink::WebCryptoKeyTypeSecret, key.type()); 92 93 if (data_size >= INT_MAX - AES_BLOCK_SIZE) { 94 // TODO(padolph): Handle this by chunking the input fed into OpenSSL. Right 95 // now it doesn't make much difference since the one-shot API would end up 96 // blowing out the memory and crashing anyway. However a newer version of 97 // the spec allows for a sequence<CryptoData> so this will be relevant. 98 return false; 99 } 100 101 // Note: PKCS padding is enabled by default 102 crypto::ScopedOpenSSL<EVP_CIPHER_CTX, EVP_CIPHER_CTX_free> context( 103 EVP_CIPHER_CTX_new()); 104 105 if (!context.get()) 106 return false; 107 108 SymKeyHandle* const sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); 109 110 const EVP_CIPHER* const cipher = 111 GetAESCipherByKeyLength(sym_key->key().size()); 112 DCHECK(cipher); 113 114 const blink::WebCryptoAesCbcParams* const params = algorithm.aesCbcParams(); 115 if (params->iv().size() != AES_BLOCK_SIZE) 116 return false; 117 118 if (!EVP_CipherInit_ex(context.get(), 119 cipher, 120 NULL, 121 &sym_key->key()[0], 122 params->iv().data(), 123 cipher_operation)) { 124 return false; 125 } 126 127 // According to the openssl docs, the amount of data written may be as large 128 // as (data_size + cipher_block_size - 1), constrained to a multiple of 129 // cipher_block_size. 130 unsigned output_max_len = data_size + AES_BLOCK_SIZE - 1; 131 const unsigned remainder = output_max_len % AES_BLOCK_SIZE; 132 if (remainder != 0) 133 output_max_len += AES_BLOCK_SIZE - remainder; 134 DCHECK_GT(output_max_len, data_size); 135 136 *buffer = blink::WebArrayBuffer::create(output_max_len, 1); 137 138 unsigned char* const buffer_data = 139 reinterpret_cast<unsigned char*>(buffer->data()); 140 141 int output_len = 0; 142 if (!EVP_CipherUpdate( 143 context.get(), buffer_data, &output_len, data, data_size)) 144 return false; 145 int final_output_chunk_len = 0; 146 if (!EVP_CipherFinal_ex( 147 context.get(), buffer_data + output_len, &final_output_chunk_len)) 148 return false; 149 150 const unsigned final_output_len = 151 static_cast<unsigned>(output_len) + 152 static_cast<unsigned>(final_output_chunk_len); 153 DCHECK_LE(final_output_len, output_max_len); 154 155 webcrypto::ShrinkBuffer(buffer, final_output_len); 156 157 return true; 158} 159 160bool ExportKeyInternalRaw( 161 const blink::WebCryptoKey& key, 162 blink::WebArrayBuffer* buffer) { 163 164 DCHECK(key.handle()); 165 DCHECK(buffer); 166 167 if (key.type() != blink::WebCryptoKeyTypeSecret || !key.extractable()) 168 return false; 169 170 const SymKeyHandle* sym_key = reinterpret_cast<SymKeyHandle*>(key.handle()); 171 172 *buffer = webcrypto::CreateArrayBuffer( 173 webcrypto::Uint8VectorStart(sym_key->key()), sym_key->key().size()); 174 175 return true; 176} 177 178} // namespace 179 180void WebCryptoImpl::Init() { crypto::EnsureOpenSSLInit(); } 181 182bool WebCryptoImpl::EncryptInternal(const blink::WebCryptoAlgorithm& algorithm, 183 const blink::WebCryptoKey& key, 184 const unsigned char* data, 185 unsigned data_size, 186 blink::WebArrayBuffer* buffer) { 187 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { 188 return AesCbcEncryptDecrypt( 189 kDoEncrypt, algorithm, key, data, data_size, buffer); 190 } 191 192 return false; 193} 194 195bool WebCryptoImpl::DecryptInternal(const blink::WebCryptoAlgorithm& algorithm, 196 const blink::WebCryptoKey& key, 197 const unsigned char* data, 198 unsigned data_size, 199 blink::WebArrayBuffer* buffer) { 200 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc) { 201 return AesCbcEncryptDecrypt( 202 kDoDecrypt, algorithm, key, data, data_size, buffer); 203 } 204 205 return false; 206} 207 208bool WebCryptoImpl::DigestInternal(const blink::WebCryptoAlgorithm& algorithm, 209 const unsigned char* data, 210 unsigned data_size, 211 blink::WebArrayBuffer* buffer) { 212 213 crypto::OpenSSLErrStackTracer(FROM_HERE); 214 215 const EVP_MD* digest_algorithm; 216 switch (algorithm.id()) { 217 case blink::WebCryptoAlgorithmIdSha1: 218 digest_algorithm = EVP_sha1(); 219 break; 220 case blink::WebCryptoAlgorithmIdSha224: 221 digest_algorithm = EVP_sha224(); 222 break; 223 case blink::WebCryptoAlgorithmIdSha256: 224 digest_algorithm = EVP_sha256(); 225 break; 226 case blink::WebCryptoAlgorithmIdSha384: 227 digest_algorithm = EVP_sha384(); 228 break; 229 case blink::WebCryptoAlgorithmIdSha512: 230 digest_algorithm = EVP_sha512(); 231 break; 232 default: 233 // Not a digest algorithm. 234 return false; 235 } 236 237 crypto::ScopedOpenSSL<EVP_MD_CTX, EVP_MD_CTX_destroy> digest_context( 238 EVP_MD_CTX_create()); 239 if (!digest_context.get()) { 240 return false; 241 } 242 243 if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, NULL) || 244 !EVP_DigestUpdate(digest_context.get(), data, data_size)) { 245 return false; 246 } 247 248 const int hash_expected_size = EVP_MD_CTX_size(digest_context.get()); 249 if (hash_expected_size <= 0) { 250 return false; 251 } 252 DCHECK_LE(hash_expected_size, EVP_MAX_MD_SIZE); 253 254 *buffer = blink::WebArrayBuffer::create(hash_expected_size, 1); 255 unsigned char* const hash_buffer = 256 reinterpret_cast<unsigned char* const>(buffer->data()); 257 258 unsigned hash_size = 0; 259 if (!EVP_DigestFinal_ex(digest_context.get(), hash_buffer, &hash_size) || 260 static_cast<int>(hash_size) != hash_expected_size) { 261 buffer->reset(); 262 return false; 263 } 264 265 return true; 266} 267 268bool WebCryptoImpl::GenerateKeyInternal( 269 const blink::WebCryptoAlgorithm& algorithm, 270 bool extractable, 271 blink::WebCryptoKeyUsageMask usage_mask, 272 blink::WebCryptoKey* key) { 273 274 unsigned keylen_bytes = 0; 275 blink::WebCryptoKeyType key_type; 276 switch (algorithm.id()) { 277 case blink::WebCryptoAlgorithmIdAesCbc: { 278 const blink::WebCryptoAesKeyGenParams* params = 279 algorithm.aesKeyGenParams(); 280 DCHECK(params); 281 if (params->length() % 8) 282 return false; 283 keylen_bytes = params->length() / 8; 284 if (!GetAESCipherByKeyLength(keylen_bytes)) { 285 return false; 286 } 287 key_type = blink::WebCryptoKeyTypeSecret; 288 break; 289 } 290 case blink::WebCryptoAlgorithmIdHmac: { 291 const blink::WebCryptoHmacKeyParams* params = algorithm.hmacKeyParams(); 292 DCHECK(params); 293 if (!params->getLength(keylen_bytes)) { 294 keylen_bytes = WebCryptoHmacParamsToBlockSize(params); 295 } 296 key_type = blink::WebCryptoKeyTypeSecret; 297 break; 298 } 299 300 default: { return false; } 301 } 302 303 if (keylen_bytes == 0) { 304 return false; 305 } 306 307 crypto::OpenSSLErrStackTracer(FROM_HERE); 308 309 std::vector<unsigned char> random_bytes(keylen_bytes, 0); 310 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) { 311 return false; 312 } 313 314 *key = blink::WebCryptoKey::create( 315 new SymKeyHandle(&random_bytes[0], random_bytes.size()), 316 key_type, extractable, algorithm, usage_mask); 317 318 return true; 319} 320 321bool WebCryptoImpl::GenerateKeyPairInternal( 322 const blink::WebCryptoAlgorithm& algorithm, 323 bool extractable, 324 blink::WebCryptoKeyUsageMask usage_mask, 325 blink::WebCryptoKey* public_key, 326 blink::WebCryptoKey* private_key) { 327 // TODO(padolph): Placeholder for OpenSSL implementation. 328 // Issue http://crbug.com/267888. 329 return false; 330} 331 332bool WebCryptoImpl::ImportKeyInternal( 333 blink::WebCryptoKeyFormat format, 334 const unsigned char* key_data, 335 unsigned key_data_size, 336 const blink::WebCryptoAlgorithm& algorithm_or_null, 337 bool extractable, 338 blink::WebCryptoKeyUsageMask usage_mask, 339 blink::WebCryptoKey* key) { 340 // TODO(eroman): Currently expects algorithm to always be specified, as it is 341 // required for raw format. 342 if (algorithm_or_null.isNull()) 343 return false; 344 const blink::WebCryptoAlgorithm& algorithm = algorithm_or_null; 345 346 // TODO(padolph): Support all relevant alg types and then remove this gate. 347 if (algorithm.id() != blink::WebCryptoAlgorithmIdHmac && 348 algorithm.id() != blink::WebCryptoAlgorithmIdAesCbc) { 349 return false; 350 } 351 352 // TODO(padolph): Need to split handling for symmetric (raw format) and 353 // asymmetric (spki or pkcs8 format) keys. 354 // Currently only supporting symmetric. 355 356 // Symmetric keys are always type secret 357 blink::WebCryptoKeyType type = blink::WebCryptoKeyTypeSecret; 358 359 const unsigned char* raw_key_data; 360 unsigned raw_key_data_size; 361 switch (format) { 362 case blink::WebCryptoKeyFormatRaw: 363 raw_key_data = key_data; 364 raw_key_data_size = key_data_size; 365 // The NSS implementation fails when importing a raw AES key with a length 366 // incompatible with AES. The line below is to match this behavior. 367 if (algorithm.id() == blink::WebCryptoAlgorithmIdAesCbc && 368 !GetAESCipherByKeyLength(raw_key_data_size)) { 369 return false; 370 } 371 break; 372 case blink::WebCryptoKeyFormatJwk: 373 // TODO(padolph): Handle jwk format; need simple JSON parser. 374 // break; 375 return false; 376 default: 377 return false; 378 } 379 380 *key = blink::WebCryptoKey::create( 381 new SymKeyHandle(raw_key_data, raw_key_data_size), 382 type, extractable, algorithm, usage_mask); 383 384 return true; 385} 386 387bool WebCryptoImpl::ExportKeyInternal( 388 blink::WebCryptoKeyFormat format, 389 const blink::WebCryptoKey& key, 390 blink::WebArrayBuffer* buffer) { 391 switch (format) { 392 case blink::WebCryptoKeyFormatRaw: 393 return ExportKeyInternalRaw(key, buffer); 394 case blink::WebCryptoKeyFormatSpki: 395 // TODO(padolph): Implement spki export 396 return false; 397 case blink::WebCryptoKeyFormatPkcs8: 398 // TODO(padolph): Implement pkcs8 export 399 return false; 400 default: 401 return false; 402 } 403 return false; 404} 405 406bool WebCryptoImpl::SignInternal( 407 const blink::WebCryptoAlgorithm& algorithm, 408 const blink::WebCryptoKey& key, 409 const unsigned char* data, 410 unsigned data_size, 411 blink::WebArrayBuffer* buffer) { 412 413 blink::WebArrayBuffer result; 414 415 switch (algorithm.id()) { 416 case blink::WebCryptoAlgorithmIdHmac: { 417 418 DCHECK_EQ(key.algorithm().id(), blink::WebCryptoAlgorithmIdHmac); 419 DCHECK_NE(0, key.usages() & blink::WebCryptoKeyUsageSign); 420 421 const blink::WebCryptoHmacParams* const params = algorithm.hmacParams(); 422 if (!params) 423 return false; 424 425 const EVP_MD* evp_sha = 0; 426 unsigned int hmac_expected_length = 0; 427 // Note that HMAC length is determined by the hash used. 428 switch (params->hash().id()) { 429 case blink::WebCryptoAlgorithmIdSha1: 430 evp_sha = EVP_sha1(); 431 hmac_expected_length = SHA_DIGEST_LENGTH; 432 break; 433 case blink::WebCryptoAlgorithmIdSha224: 434 evp_sha = EVP_sha224(); 435 hmac_expected_length = SHA224_DIGEST_LENGTH; 436 break; 437 case blink::WebCryptoAlgorithmIdSha256: 438 evp_sha = EVP_sha256(); 439 hmac_expected_length = SHA256_DIGEST_LENGTH; 440 break; 441 case blink::WebCryptoAlgorithmIdSha384: 442 evp_sha = EVP_sha384(); 443 hmac_expected_length = SHA384_DIGEST_LENGTH; 444 break; 445 case blink::WebCryptoAlgorithmIdSha512: 446 evp_sha = EVP_sha512(); 447 hmac_expected_length = SHA512_DIGEST_LENGTH; 448 break; 449 default: 450 // Not a digest algorithm. 451 return false; 452 } 453 454 SymKeyHandle* const sym_key = 455 reinterpret_cast<SymKeyHandle*>(key.handle()); 456 const std::vector<unsigned char>& raw_key = sym_key->key(); 457 458 // OpenSSL wierdness here. 459 // First, HMAC() needs a void* for the key data, so make one up front as a 460 // cosmetic to avoid a cast. Second, OpenSSL does not like a NULL key, 461 // which will result if the raw_key vector is empty; an entirely valid 462 // case. Handle this specific case by pointing to an empty array. 463 const unsigned char null_key[] = {}; 464 const void* const raw_key_voidp = raw_key.size() ? &raw_key[0] : null_key; 465 466 result = blink::WebArrayBuffer::create(hmac_expected_length, 1); 467 crypto::ScopedOpenSSLSafeSizeBuffer<EVP_MAX_MD_SIZE> hmac_result( 468 reinterpret_cast<unsigned char*>(result.data()), 469 hmac_expected_length); 470 471 crypto::OpenSSLErrStackTracer(FROM_HERE); 472 473 unsigned int hmac_actual_length; 474 unsigned char* const success = HMAC(evp_sha, 475 raw_key_voidp, 476 raw_key.size(), 477 data, 478 data_size, 479 hmac_result.safe_buffer(), 480 &hmac_actual_length); 481 if (!success || hmac_actual_length != hmac_expected_length) 482 return false; 483 484 break; 485 } 486 default: 487 return false; 488 } 489 490 *buffer = result; 491 return true; 492} 493 494bool WebCryptoImpl::VerifySignatureInternal( 495 const blink::WebCryptoAlgorithm& algorithm, 496 const blink::WebCryptoKey& key, 497 const unsigned char* signature, 498 unsigned signature_size, 499 const unsigned char* data, 500 unsigned data_size, 501 bool* signature_match) { 502 switch (algorithm.id()) { 503 case blink::WebCryptoAlgorithmIdHmac: { 504 blink::WebArrayBuffer result; 505 if (!SignInternal(algorithm, key, data, data_size, &result)) { 506 return false; 507 } 508 509 // Handling of truncated signatures is underspecified in the WebCrypto 510 // spec, so here we fail verification if a truncated signature is being 511 // verified. 512 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=23097 513 *signature_match = 514 result.byteLength() == signature_size && 515 crypto::SecureMemEqual(result.data(), signature, signature_size); 516 517 break; 518 } 519 default: 520 return false; 521 } 522 return true; 523} 524 525bool WebCryptoImpl::ImportRsaPublicKeyInternal( 526 const unsigned char* modulus_data, 527 unsigned modulus_size, 528 const unsigned char* exponent_data, 529 unsigned exponent_size, 530 const blink::WebCryptoAlgorithm& algorithm, 531 bool extractable, 532 blink::WebCryptoKeyUsageMask usage_mask, 533 blink::WebCryptoKey* key) { 534 // TODO(padolph): Placeholder for OpenSSL implementation. 535 // Issue http://crbug.com/267888. 536 return false; 537} 538 539} // namespace content 540