Fawkes API  Fawkes Development Version
crypto.cpp
1 
2 /***************************************************************************
3  * crypto.cpp - Protobuf stream protocol - crypto utils
4  *
5  * Created: Tue Mar 11 21:14:58 2014
6  * Copyright 2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * - Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  * - Neither the name of the authors nor the names of its contributors
20  * may be used to endorse or promote products derived from this
21  * software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34  * OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 #include <protobuf_comm/crypto.h>
38 #include <protobuf_comm/frame_header.h>
39 
40 #include <stdexcept>
41 #ifdef HAVE_LIBCRYPTO
42 # include <openssl/evp.h>
43 # include <openssl/rand.h>
44 # include <openssl/sha.h>
45 
46 # include <cstring>
47 #endif
48 
49 namespace protobuf_comm {
50 
51 /** @class BufferEncryptor <protobuf_comm/crypto.h>
52  * Encrypt buffers using AES128 in ECB mode.
53  * @author Tim Niemueller
54  */
55 
56 /** Constructor.
57  * @param key encryption key, can be any string, will be processed to meet
58  * the cipher's requirements.
59  * @param cipher_name Cipher combination to use, currently supported are
60  * aes-128-ecb, aes-128-cbc, aes-256-ecb, and aes-256-cbc
61  */
62 BufferEncryptor::BufferEncryptor(const std::string &key, std::string cipher_name)
63 {
64 #ifdef HAVE_LIBCRYPTO
65  cipher_ = cipher_by_name(cipher_name.c_str());
66  cipher_id_ = cipher_name_to_id(cipher_name.c_str());
67 
68  const size_t key_size = EVP_CIPHER_key_length(cipher_);
69  const size_t iv_size = EVP_CIPHER_iv_length(cipher_);
70  key_ = (unsigned char *)malloc(key_size);
71  unsigned char iv[iv_size];
72  if (!EVP_BytesToKey(
73  cipher_, EVP_sha256(), NULL, (const unsigned char *)key.c_str(), key.size(), 8, key_, iv)) {
74  throw std::runtime_error("Failed to generate key");
75  }
76 
77  if (!RAND_bytes((unsigned char *)&iv_, sizeof(iv_))) {
78  throw std::runtime_error("Failed to generate IV");
79  }
80 #else
81  throw std::runtime_error("Encryption support not available");
82 #endif
83 }
84 
85 /** Destructor. */
87 {
88  free(key_);
89 }
90 
91 /** Encrypt a buffer.
92  * Uses the cipher set in the constructor.
93  * @param plain plain text data
94  * @param enc upon return contains encrypted buffer
95  */
96 void
97 BufferEncryptor::encrypt(const std::string &plain, std::string &enc)
98 {
99 #ifdef HAVE_LIBCRYPTO
100  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher_id_);
101 
102  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
103  unsigned char iv_hash[SHA256_DIGEST_LENGTH];
104 
105  unsigned char *enc_m = (unsigned char *)enc.c_str();
106 
107  if (iv_size > 0) {
108  iv_ += 1;
109 
110  if (!SHA256((unsigned char *)&iv_, sizeof(iv_), iv_hash)) {
111  throw std::runtime_error("Failed to generate IV");
112  }
113  enc.replace(0, iv_size, (char *)iv_hash, iv_size);
114  enc_m += iv_size;
115  }
116 
117  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
118  if (!EVP_EncryptInit(ctx, evp_cipher, key_, iv_hash)) {
119  EVP_CIPHER_CTX_free(ctx);
120  throw std::runtime_error("Could not initialize cipher context");
121  }
122 
123  int outl = enc.size() - iv_size;
124  if (!EVP_EncryptUpdate(ctx, enc_m, &outl, (unsigned char *)plain.c_str(), plain.size())) {
125  EVP_CIPHER_CTX_free(ctx);
126  throw std::runtime_error("EncryptUpdate failed");
127  }
128 
129  int plen = 0;
130  if (!EVP_EncryptFinal_ex(ctx, enc_m + outl, &plen)) {
131  EVP_CIPHER_CTX_free(ctx);
132  throw std::runtime_error("EncryptFinal failed");
133  }
134  outl += plen;
135 
136  EVP_CIPHER_CTX_free(ctx);
137  enc.resize(outl + iv_size);
138 #else
139  throw std::runtime_error("Encryption support not available");
140 #endif
141 }
142 
143 /** Get required size for an encrypted buffer of the given plain text length.
144  * @param plain_length length of the plain text buffer to encrypt
145  * @return length of encrypted buffer required
146  */
147 size_t
149 {
150 #ifdef HAVE_LIBCRYPTO
151  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher_id_);
152 
153  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
154  size_t block_size = EVP_CIPHER_block_size(evp_cipher);
155 
156  return (((plain_length / block_size) + 1) * block_size) + iv_size;
157 #else
158  throw std::runtime_error("Encryption not supported");
159 #endif
160 }
161 
162 /** @class BufferDecryptor <protobuf_comm/crypto.h>
163  * Decrypt buffers encrypted with BufferEncryptor.
164  * @author Tim Niemueller
165  */
166 
167 /** Constructor.
168  * @param key encryption key, can be any string, will be processed to meet
169  * AES128 requirements.
170  */
171 BufferDecryptor::BufferDecryptor(const std::string &key) : key_(key)
172 {
173 }
174 
175 /** Destructor. */
177 {
178 }
179 
180 void
181 BufferDecryptor::generate_key(int cipher)
182 {
183 #ifdef HAVE_LIBCRYPTO
184  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher);
185 
186  const size_t key_size = EVP_CIPHER_key_length(evp_cipher);
187  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
188  unsigned char *key = (unsigned char *)malloc(key_size);
189  unsigned char iv[iv_size];
190  if (!EVP_BytesToKey(evp_cipher,
191  EVP_sha256(),
192  NULL,
193  (const unsigned char *)key_.c_str(),
194  key_.size(),
195  8,
196  key,
197  iv)) {
198  free(key);
199 #endif
200  throw std::runtime_error("Failed to generate key");
201 #ifdef HAVE_LIBCRYPTO
202  }
203 
204  std::string ks((const char *)key, key_size);
205  free(key);
206 
207  keys_[cipher] = ks;
208 #endif
209 }
210 
211 /** Decrypt a buffer.
212  * @param cipher cipher ID
213  * @param enc encrypted buffer
214  * @param enc_size number of bytes in @p enc
215  * @param plain on return contains plain text data
216  * @param plain_size size in bytes of @p plain
217  * @return number of bytes that were in the encrypted buffer (this can be shorter if the data
218  * did not exactly fit the AES block size.
219  */
220 size_t
222  const void *enc,
223  size_t enc_size,
224  void * plain,
225  size_t plain_size)
226 {
227 #ifdef HAVE_LIBCRYPTO
228  if (keys_.find(cipher) == keys_.end()) {
229  generate_key(cipher);
230  }
231 
232  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher);
233 
234  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
235  const unsigned char *iv = (const unsigned char *)enc;
236  unsigned char * enc_m = (unsigned char *)enc + iv_size;
237  enc_size -= iv_size;
238 
239  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
240  if (!EVP_DecryptInit(ctx, evp_cipher, (const unsigned char *)keys_[cipher].c_str(), iv)) {
241  EVP_CIPHER_CTX_free(ctx);
242  throw std::runtime_error("Could not initialize cipher context");
243  }
244 
245  int outl = plain_size;
246  if (!EVP_DecryptUpdate(ctx, (unsigned char *)plain, &outl, enc_m, enc_size)) {
247  EVP_CIPHER_CTX_free(ctx);
248  throw std::runtime_error("DecryptUpdate failed");
249  }
250 
251  int plen = 0;
252  if (!EVP_DecryptFinal(ctx, (unsigned char *)plain + outl, &plen)) {
253  EVP_CIPHER_CTX_free(ctx);
254  throw std::runtime_error("DecryptFinal failed");
255  }
256  outl += plen;
257 
258  EVP_CIPHER_CTX_free(ctx);
259  return outl;
260 #else
261  throw std::runtime_error("Decryption support not available");
262 #endif
263 }
264 
265 #ifdef HAVE_LIBCRYPTO
266 /** Get cipher name for PB_ENCRYPTION_* constants.
267  * @param cipher cipher ID
268  * @return string representing the cipher
269  */
270 const char *
271 cipher_name_by_id(int cipher)
272 {
273  switch (cipher) {
274  case PB_ENCRYPTION_AES_128_ECB: return SN_aes_128_ecb;
275  case PB_ENCRYPTION_AES_128_CBC: return SN_aes_128_cbc;
276 
277  case PB_ENCRYPTION_AES_256_ECB: return SN_aes_256_ecb;
278  case PB_ENCRYPTION_AES_256_CBC: return SN_aes_256_cbc;
279 
280  default: throw std::runtime_error("Unknown cipher type");
281  }
282 }
283 
284 /** Get cipher for PB_ENCRYPTION_* constants.
285  * @param cipher cipher ID
286  * @return cipher engine
287  */
288 const EVP_CIPHER *
289 cipher_by_id(int cipher)
290 {
291  switch (cipher) {
292  case PB_ENCRYPTION_AES_128_ECB: return EVP_aes_128_ecb();
293  case PB_ENCRYPTION_AES_128_CBC: return EVP_aes_128_cbc();
294 
295  case PB_ENCRYPTION_AES_256_ECB: return EVP_aes_256_ecb();
296  case PB_ENCRYPTION_AES_256_CBC: return EVP_aes_256_cbc();
297 
298  default: throw std::runtime_error("Unknown cipher type");
299  }
300 }
301 
302 /** Get cipher by name constants.
303  * @param cipher cipher name
304  * @return cipher engine
305  */
306 int
307 cipher_name_to_id(const char *cipher)
308 {
309  if (strcmp(cipher, LN_aes_128_ecb) == 0) {
310  return PB_ENCRYPTION_AES_128_ECB;
311  } else if (strcmp(cipher, LN_aes_128_cbc) == 0) {
312  return PB_ENCRYPTION_AES_128_CBC;
313  } else if (strcmp(cipher, LN_aes_256_ecb) == 0) {
314  return PB_ENCRYPTION_AES_256_ECB;
315  } else if (strcmp(cipher, LN_aes_256_cbc) == 0) {
316  return PB_ENCRYPTION_AES_256_CBC;
317  } else {
318  throw std::runtime_error("Unknown cipher type");
319  }
320 }
321 
322 /** Get cipher by name constants.
323  * @param cipher cipher name
324  * @return cipher engine
325  */
326 const EVP_CIPHER *
327 cipher_by_name(const char *cipher)
328 {
329  if (strcmp(cipher, LN_aes_128_ecb) == 0) {
330  return EVP_aes_128_ecb();
331  } else if (strcmp(cipher, LN_aes_128_cbc) == 0) {
332  return EVP_aes_128_cbc();
333  } else if (strcmp(cipher, LN_aes_256_ecb) == 0) {
334  return EVP_aes_256_ecb();
335  } else if (strcmp(cipher, LN_aes_256_cbc) == 0) {
336  return EVP_aes_256_cbc();
337  } else {
338  throw std::runtime_error("Unknown cipher type");
339  }
340 }
341 #endif
342 
343 } // namespace protobuf_comm
BufferEncryptor(const std::string &key, std::string cipher_name="AES-128-ECB")
Constructor.
Definition: crypto.cpp:62
~BufferEncryptor()
Destructor.
Definition: crypto.cpp:86
size_t encrypted_buffer_size(size_t plain_length)
Get required size for an encrypted buffer of the given plain text length.
Definition: crypto.cpp:148
size_t decrypt(int cipher, const void *enc, size_t enc_size, void *plain, size_t plain_size)
Decrypt a buffer.
Definition: crypto.cpp:221
BufferDecryptor(const std::string &key)
Constructor.
Definition: crypto.cpp:171
void encrypt(const std::string &plain, std::string &enc)
Encrypt a buffer.
Definition: crypto.cpp:97