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
11namespace crypto {
12
13namespace {
14
15// On success, returns the block size (in bytes) for the algorithm that |key|
16// is for. On failure, returns 0.
17DWORD 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
31Encryptor::Encryptor()
32    : key_(NULL),
33      mode_(CBC),
34      block_size_(0) {
35}
36
37Encryptor::~Encryptor() {
38}
39
40bool 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
80bool 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
97bool 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