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 <openssl/aes.h>
8#include <openssl/evp.h>
9
10#include "base/logging.h"
11#include "base/string_util.h"
12#include "crypto/openssl_util.h"
13#include "crypto/symmetric_key.h"
14
15namespace crypto {
16
17namespace {
18
19const EVP_CIPHER* GetCipherForKey(SymmetricKey* key) {
20  switch (key->key().length()) {
21    case 16: return EVP_aes_128_cbc();
22    case 24: return EVP_aes_192_cbc();
23    case 32: return EVP_aes_256_cbc();
24    default: return NULL;
25  }
26}
27
28// On destruction this class will cleanup the ctx, and also clear the OpenSSL
29// ERR stack as a convenience.
30class ScopedCipherCTX {
31 public:
32  explicit ScopedCipherCTX() {
33    EVP_CIPHER_CTX_init(&ctx_);
34  }
35  ~ScopedCipherCTX() {
36    EVP_CIPHER_CTX_cleanup(&ctx_);
37    ClearOpenSSLERRStack(FROM_HERE);
38  }
39  EVP_CIPHER_CTX* get() { return &ctx_; }
40
41 private:
42  EVP_CIPHER_CTX ctx_;
43};
44
45}  // namespace
46
47Encryptor::Encryptor()
48    : key_(NULL),
49      mode_(CBC) {
50}
51
52Encryptor::~Encryptor() {
53}
54
55bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
56  DCHECK(key);
57  DCHECK_EQ(CBC, mode);
58
59  EnsureOpenSSLInit();
60  if (iv.size() != AES_BLOCK_SIZE)
61    return false;
62
63  if (GetCipherForKey(key) == NULL)
64    return false;
65
66  key_ = key;
67  mode_ = mode;
68  iv_ = iv;
69  return true;
70}
71
72bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
73  return Crypt(true, plaintext, ciphertext);
74}
75
76bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
77  return Crypt(false, ciphertext, plaintext);
78}
79
80bool Encryptor::Crypt(bool do_encrypt,
81                      const std::string& input,
82                      std::string* output) {
83  DCHECK(key_);  // Must call Init() before En/De-crypt.
84  // Work on the result in a local variable, and then only transfer it to
85  // |output| on success to ensure no partial data is returned.
86  std::string result;
87  output->swap(result);
88
89  const EVP_CIPHER* cipher = GetCipherForKey(key_);
90  DCHECK(cipher);  // Already handled in Init();
91
92  const std::string& key = key_->key();
93  DCHECK_EQ(EVP_CIPHER_iv_length(cipher), static_cast<int>(iv_.length()));
94  DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast<int>(key.length()));
95
96  ScopedCipherCTX ctx;
97  if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL,
98                         reinterpret_cast<const uint8*>(key.data()),
99                         reinterpret_cast<const uint8*>(iv_.data()),
100                         do_encrypt))
101    return false;
102
103  // When encrypting, add another block size of space to allow for any padding.
104  const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0);
105  uint8* out_ptr = reinterpret_cast<uint8*>(WriteInto(&result,
106                                                      output_size + 1));
107  int out_len;
108  if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len,
109                        reinterpret_cast<const uint8*>(input.data()),
110                        input.length()))
111    return false;
112
113  // Write out the final block plus padding (if any) to the end of the data
114  // just written.
115  int tail_len;
116  if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len))
117    return false;
118
119  out_len += tail_len;
120  DCHECK_LE(out_len, static_cast<int>(output_size));
121  result.resize(out_len);
122
123  output->swap(result);
124  return true;
125}
126
127}  // namespace crypto
128