OLD | NEW |
| (Empty) |
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 #include <stddef.h> | |
10 #include <stdint.h> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/strings/string_util.h" | |
14 #include "crypto/openssl_util.h" | |
15 #include "crypto/symmetric_key.h" | |
16 | |
17 namespace crypto { | |
18 | |
19 namespace { | |
20 | |
21 const EVP_CIPHER* GetCipherForKey(SymmetricKey* key) { | |
22 switch (key->key().length()) { | |
23 case 16: return EVP_aes_128_cbc(); | |
24 case 32: return EVP_aes_256_cbc(); | |
25 default: return NULL; | |
26 } | |
27 } | |
28 | |
29 // On destruction this class will cleanup the ctx, and also clear the OpenSSL | |
30 // ERR stack as a convenience. | |
31 class ScopedCipherCTX { | |
32 public: | |
33 explicit ScopedCipherCTX() { | |
34 EVP_CIPHER_CTX_init(&ctx_); | |
35 } | |
36 ~ScopedCipherCTX() { | |
37 EVP_CIPHER_CTX_cleanup(&ctx_); | |
38 ClearOpenSSLERRStack(FROM_HERE); | |
39 } | |
40 EVP_CIPHER_CTX* get() { return &ctx_; } | |
41 | |
42 private: | |
43 EVP_CIPHER_CTX ctx_; | |
44 }; | |
45 | |
46 } // namespace | |
47 | |
48 Encryptor::Encryptor() | |
49 : key_(NULL), | |
50 mode_(CBC) { | |
51 } | |
52 | |
53 Encryptor::~Encryptor() { | |
54 } | |
55 | |
56 bool Encryptor::Init(SymmetricKey* key, | |
57 Mode mode, | |
58 const base::StringPiece& iv) { | |
59 DCHECK(key); | |
60 DCHECK(mode == CBC || mode == CTR); | |
61 | |
62 EnsureOpenSSLInit(); | |
63 if (mode == CBC && iv.size() != AES_BLOCK_SIZE) | |
64 return false; | |
65 | |
66 if (GetCipherForKey(key) == NULL) | |
67 return false; | |
68 | |
69 key_ = key; | |
70 mode_ = mode; | |
71 iv.CopyToString(&iv_); | |
72 return true; | |
73 } | |
74 | |
75 bool Encryptor::Encrypt(const base::StringPiece& plaintext, | |
76 std::string* ciphertext) { | |
77 CHECK(!plaintext.empty() || (mode_ == CBC)); | |
78 return (mode_ == CTR) ? | |
79 CryptCTR(true, plaintext, ciphertext) : | |
80 Crypt(true, plaintext, ciphertext); | |
81 } | |
82 | |
83 bool Encryptor::Decrypt(const base::StringPiece& ciphertext, | |
84 std::string* plaintext) { | |
85 CHECK(!ciphertext.empty()); | |
86 return (mode_ == CTR) ? | |
87 CryptCTR(false, ciphertext, plaintext) : | |
88 Crypt(false, ciphertext, plaintext); | |
89 } | |
90 | |
91 bool Encryptor::Crypt(bool do_encrypt, | |
92 const base::StringPiece& input, | |
93 std::string* output) { | |
94 DCHECK(key_); // Must call Init() before En/De-crypt. | |
95 // Work on the result in a local variable, and then only transfer it to | |
96 // |output| on success to ensure no partial data is returned. | |
97 std::string result; | |
98 output->clear(); | |
99 | |
100 const EVP_CIPHER* cipher = GetCipherForKey(key_); | |
101 DCHECK(cipher); // Already handled in Init(); | |
102 | |
103 const std::string& key = key_->key(); | |
104 DCHECK_EQ(EVP_CIPHER_iv_length(cipher), iv_.length()); | |
105 DCHECK_EQ(EVP_CIPHER_key_length(cipher), key.length()); | |
106 | |
107 ScopedCipherCTX ctx; | |
108 if (!EVP_CipherInit_ex( | |
109 ctx.get(), cipher, NULL, reinterpret_cast<const uint8_t*>(key.data()), | |
110 reinterpret_cast<const uint8_t*>(iv_.data()), do_encrypt)) | |
111 return false; | |
112 | |
113 // When encrypting, add another block size of space to allow for any padding. | |
114 const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0); | |
115 CHECK_GT(output_size, 0u); | |
116 CHECK_GT(output_size + 1, input.size()); | |
117 uint8_t* out_ptr = | |
118 reinterpret_cast<uint8_t*>(base::WriteInto(&result, output_size + 1)); | |
119 int out_len; | |
120 if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len, | |
121 reinterpret_cast<const uint8_t*>(input.data()), | |
122 input.length())) | |
123 return false; | |
124 | |
125 // Write out the final block plus padding (if any) to the end of the data | |
126 // just written. | |
127 int tail_len; | |
128 if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len)) | |
129 return false; | |
130 | |
131 out_len += tail_len; | |
132 DCHECK_LE(out_len, static_cast<int>(output_size)); | |
133 result.resize(out_len); | |
134 | |
135 output->swap(result); | |
136 return true; | |
137 } | |
138 | |
139 bool Encryptor::CryptCTR(bool do_encrypt, | |
140 const base::StringPiece& input, | |
141 std::string* output) { | |
142 if (!counter_.get()) { | |
143 LOG(ERROR) << "Counter value not set in CTR mode."; | |
144 return false; | |
145 } | |
146 | |
147 AES_KEY aes_key; | |
148 if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()), | |
149 key_->key().size() * 8, &aes_key) != 0) { | |
150 return false; | |
151 } | |
152 | |
153 const size_t out_size = input.size(); | |
154 CHECK_GT(out_size, 0u); | |
155 CHECK_GT(out_size + 1, input.size()); | |
156 | |
157 std::string result; | |
158 uint8_t* out_ptr = | |
159 reinterpret_cast<uint8_t*>(base::WriteInto(&result, out_size + 1)); | |
160 | |
161 uint8_t ivec[AES_BLOCK_SIZE] = { 0 }; | |
162 uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 }; | |
163 unsigned int block_offset = 0; | |
164 | |
165 counter_->Write(ivec); | |
166 | |
167 AES_ctr128_encrypt(reinterpret_cast<const uint8_t*>(input.data()), out_ptr, | |
168 input.size(), &aes_key, ivec, ecount_buf, &block_offset); | |
169 | |
170 // AES_ctr128_encrypt() updates |ivec|. Update the |counter_| here. | |
171 SetCounter(base::StringPiece(reinterpret_cast<const char*>(ivec), | |
172 AES_BLOCK_SIZE)); | |
173 | |
174 output->swap(result); | |
175 return true; | |
176 } | |
177 | |
178 } // namespace crypto | |
OLD | NEW |