Home | History | Annotate | Download | only in crypto
      1 // Copyright (c) 2011 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 "crypto/encryptor.h"
      6 
      7 #include <vector>
      8 
      9 #include "crypto/symmetric_key.h"
     10 
     11 namespace crypto {
     12 
     13 namespace {
     14 
     15 // On success, returns the block size (in bytes) for the algorithm that |key|
     16 // is for. On failure, returns 0.
     17 DWORD GetCipherBlockSize(HCRYPTKEY key) {
     18   DWORD block_size_in_bits = 0;
     19   DWORD param_size = sizeof(block_size_in_bits);
     20   BOOL ok = CryptGetKeyParam(key, KP_BLOCKLEN,
     21                              reinterpret_cast<BYTE*>(&block_size_in_bits),
     22                              &param_size, 0);
     23   if (!ok)
     24     return 0;
     25 
     26   return block_size_in_bits / 8;
     27 }
     28 
     29 }  // namespace
     30 
     31 Encryptor::Encryptor()
     32     : key_(NULL),
     33       mode_(CBC),
     34       block_size_(0) {
     35 }
     36 
     37 Encryptor::~Encryptor() {
     38 }
     39 
     40 bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
     41   DCHECK(key);
     42   DCHECK_EQ(CBC, mode) << "Unsupported mode of operation";
     43 
     44   // In CryptoAPI, the IV, padding mode, and feedback register (for a chaining
     45   // mode) are properties of a key, so we have to create a copy of the key for
     46   // the Encryptor.  See the Remarks section of the CryptEncrypt MSDN page.
     47   BOOL ok = CryptDuplicateKey(key->key(), NULL, 0, capi_key_.receive());
     48   if (!ok)
     49     return false;
     50 
     51   // CRYPT_MODE_CBC is the default for Microsoft Base Cryptographic Provider,
     52   // but we set it anyway to be safe.
     53   DWORD cipher_mode = CRYPT_MODE_CBC;
     54   ok = CryptSetKeyParam(capi_key_.get(), KP_MODE,
     55                         reinterpret_cast<BYTE*>(&cipher_mode), 0);
     56   if (!ok)
     57     return false;
     58 
     59   block_size_ = GetCipherBlockSize(capi_key_.get());
     60   if (block_size_ == 0)
     61     return false;
     62 
     63   if (iv.size() != block_size_)
     64     return false;
     65 
     66   ok = CryptSetKeyParam(capi_key_.get(), KP_IV,
     67                         reinterpret_cast<const BYTE*>(iv.data()), 0);
     68   if (!ok)
     69     return false;
     70 
     71   DWORD padding_method = PKCS5_PADDING;
     72   ok = CryptSetKeyParam(capi_key_.get(), KP_PADDING,
     73                         reinterpret_cast<BYTE*>(&padding_method), 0);
     74   if (!ok)
     75     return false;
     76 
     77   return true;
     78 }
     79 
     80 bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
     81   DWORD data_len = plaintext.size();
     82   DWORD total_len = data_len + block_size_;
     83 
     84   // CryptoAPI encrypts/decrypts in place.
     85   std::vector<BYTE> tmp(total_len);
     86   memcpy(&tmp[0], plaintext.data(), data_len);
     87 
     88   BOOL ok = CryptEncrypt(capi_key_.get(), NULL, TRUE, 0, &tmp[0],
     89                          &data_len, total_len);
     90   if (!ok)
     91     return false;
     92 
     93   ciphertext->assign(reinterpret_cast<char*>(&tmp[0]), data_len);
     94   return true;
     95 }
     96 
     97 bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
     98   DWORD data_len = ciphertext.size();
     99   if (data_len == 0)
    100     return false;
    101 
    102   std::vector<BYTE> tmp(data_len);
    103   memcpy(&tmp[0], ciphertext.data(), data_len);
    104 
    105   BOOL ok = CryptDecrypt(capi_key_.get(), NULL, TRUE, 0, &tmp[0], &data_len);
    106   if (!ok)
    107     return false;
    108 
    109   DCHECK_GT(tmp.size(), data_len);
    110 
    111   plaintext->assign(reinterpret_cast<char*>(&tmp[0]), data_len);
    112   return true;
    113 }
    114 
    115 }  // namespace crypto
    116