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 # include <cstring>
46 #endif
47 
48 namespace protobuf_comm {
49 #if 0 /* just to make Emacs auto-indent happy */
50 }
51 #endif
52 
53 /** @class BufferEncryptor <protobuf_comm/crypto.h>
54  * Encrypt buffers using AES128 in ECB mode.
55  * @author Tim Niemueller
56  */
57 
58 /** Constructor.
59  * @param key encryption key, can be any string, will be processed to meet
60  * the cipher's requirements.
61  * @param cipher_name Cipher combination to use, currently supported are
62  * aes-128-ecb, aes-128-cbc, aes-256-ecb, and aes-256-cbc
63  */
64 BufferEncryptor::BufferEncryptor(const std::string &key, std::string cipher_name)
65 {
66  cipher_ = cipher_by_name(cipher_name.c_str());
67  cipher_id_ = cipher_name_to_id(cipher_name.c_str());
68 
69  const size_t key_size = EVP_CIPHER_key_length(cipher_);
70  const size_t iv_size = EVP_CIPHER_iv_length(cipher_);
71  key_ = (unsigned char *)malloc(key_size);
72  unsigned char iv[iv_size];
73  if( ! EVP_BytesToKey(cipher_, EVP_sha256(), NULL,
74  (const unsigned char *)key.c_str(), key.size(), 8, key_, iv))
75  {
76  throw std::runtime_error("Failed to generate key");
77  }
78 
79  if (!RAND_bytes((unsigned char *)&iv_, sizeof(iv_))) {
80  throw std::runtime_error("Failed to generate IV");
81  }
82 }
83 
84 
85 /** Destructor. */
87 {
88  free(key_);
89 }
90 
91 
92 /** Encrypt a buffer.
93  * Uses the cipher set in the constructor.
94  * @param plain plain text data
95  * @param enc upon return contains encrypted buffer
96  */
97 void
98 BufferEncryptor::encrypt(const std::string &plain, std::string &enc)
99 {
100 #ifdef HAVE_LIBCRYPTO
101  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher_id_);
102 
103  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
104  unsigned char iv_hash[SHA256_DIGEST_LENGTH];
105 
106  unsigned char *enc_m = (unsigned char *)enc.c_str();
107 
108  if (iv_size > 0) {
109  iv_ += 1;
110 
111  if (! SHA256((unsigned char *)&iv_, sizeof(iv_), iv_hash)) {
112  throw std::runtime_error("Failed to generate IV");
113  }
114  enc.replace(0, iv_size, (char *)iv_hash, iv_size);
115  enc_m += iv_size;
116  }
117 
118  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
119  if ( ! EVP_EncryptInit(ctx, evp_cipher, key_, iv_hash))
120  {
121  EVP_CIPHER_CTX_free(ctx);
122  throw std::runtime_error("Could not initialize cipher context");
123  }
124 
125  int outl = enc.size() - iv_size;
126  if ( ! EVP_EncryptUpdate(ctx, enc_m, &outl,
127  (unsigned char *)plain.c_str(), plain.size()) )
128  {
129  EVP_CIPHER_CTX_free(ctx);
130  throw std::runtime_error("EncryptUpdate failed");
131  }
132 
133  int plen = 0;
134  if ( ! EVP_EncryptFinal_ex(ctx, enc_m + outl, &plen) ) {
135  EVP_CIPHER_CTX_free(ctx);
136  throw std::runtime_error("EncryptFinal failed");
137  }
138  outl += plen;
139 
140  EVP_CIPHER_CTX_free(ctx);
141  enc.resize(outl + iv_size);
142 #else
143  throw std::runtime_error("Encryption support not available");
144 #endif
145 
146 }
147 
148 
149 /** Get required size for an encrypted buffer of the given plain text length.
150  * @param plain_length length of the plain text buffer to encrypt
151  * @return length of encrypted buffer required
152  */
153 size_t
155 {
156 #ifdef HAVE_LIBCRYPTO
157  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher_id_);
158 
159  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
160  size_t block_size = EVP_CIPHER_block_size(evp_cipher);
161 
162  return (((plain_length / block_size) + 1) * block_size) + iv_size;
163 #else
164  throw std::runtime_error("Encryption not supported");
165 #endif
166 }
167 
168 
169 /** @class BufferDecryptor <protobuf_comm/crypto.h>
170  * Decrypt buffers encrypted with BufferEncryptor.
171  * @author Tim Niemueller
172  */
173 
174 
175 /** Constructor.
176  * @param key encryption key, can be any string, will be processed to meet
177  * AES128 requirements.
178  */
179 BufferDecryptor::BufferDecryptor(const std::string &key)
180  : key_(key)
181 {
182 }
183 
184 
185 /** Destructor. */
187 {
188 }
189 
190 
191 void
192 BufferDecryptor::generate_key(int cipher)
193 {
194  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher);
195 
196  const size_t key_size = EVP_CIPHER_key_length(evp_cipher);
197  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
198  unsigned char *key = (unsigned char *)malloc(key_size);
199  unsigned char iv[iv_size];
200  if( ! EVP_BytesToKey(evp_cipher, EVP_sha256(), NULL,
201  (const unsigned char *)key_.c_str(), key_.size(), 8, key, iv))
202  {
203  free(key);
204  throw std::runtime_error("Failed to generate key");
205  }
206 
207  std::string ks((const char *)key, key_size);
208  free(key);
209 
210  keys_[cipher] = ks;
211 }
212 
213 
214 /** Decrypt a buffer.
215  * @param cipher cipher ID
216  * @param enc encrypted buffer
217  * @param enc_size number of bytes in @p enc
218  * @param plain on return contains plain text data
219  * @param plain_size size in bytes of @p plain
220  * @return number of bytes that were in the encrypted buffer (this can be shorter if the data
221  * did not exactly fit the AES block size.
222  */
223 size_t
224 BufferDecryptor::decrypt(int cipher, const void *enc, size_t enc_size, void *plain, size_t plain_size)
225 {
226 #ifdef HAVE_LIBCRYPTO
227  if (keys_.find(cipher) == keys_.end()) {
228  generate_key(cipher);
229  }
230 
231  const EVP_CIPHER *evp_cipher = cipher_by_id(cipher);
232 
233  const size_t iv_size = EVP_CIPHER_iv_length(evp_cipher);
234  const unsigned char *iv = (const unsigned char *)enc;
235  unsigned char *enc_m = (unsigned char *)enc + iv_size;
236  enc_size -= iv_size;
237 
238  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
239  if ( ! EVP_DecryptInit(ctx, evp_cipher, (const unsigned char *)keys_[cipher].c_str(), iv))
240  {
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,
247  (unsigned char *)plain, &outl, enc_m, enc_size))
248  {
249  EVP_CIPHER_CTX_free(ctx);
250  throw std::runtime_error("DecryptUpdate failed");
251  }
252 
253  int plen = 0;
254  if ( ! EVP_DecryptFinal(ctx, (unsigned char *)plain + outl, &plen) ) {
255  EVP_CIPHER_CTX_free(ctx);
256  throw std::runtime_error("DecryptFinal failed");
257  }
258  outl += plen;
259 
260  EVP_CIPHER_CTX_free(ctx);
261  return outl;
262 #else
263  throw std::runtime_error("Decryption support not available");
264 #endif
265 }
266 
267 
268 /** Get cipher name for PB_ENCRYPTION_* constants.
269  * @param cipher cipher ID
270  * @return string representing the cipher
271  */
272 const char *
273 cipher_name_by_id(int cipher)
274 {
275  switch (cipher) {
276  case PB_ENCRYPTION_AES_128_ECB:
277  return SN_aes_128_ecb;
278  case PB_ENCRYPTION_AES_128_CBC:
279  return SN_aes_128_cbc;
280 
281  case PB_ENCRYPTION_AES_256_ECB:
282  return SN_aes_256_ecb;
283  case PB_ENCRYPTION_AES_256_CBC:
284  return SN_aes_256_cbc;
285 
286  default:
287  throw std::runtime_error("Unknown cipher type");
288  }
289 }
290 
291 
292 /** Get cipher for PB_ENCRYPTION_* constants.
293  * @param cipher cipher ID
294  * @return cipher engine
295  */
296 const EVP_CIPHER *
297 cipher_by_id(int cipher)
298 {
299  switch (cipher) {
300  case PB_ENCRYPTION_AES_128_ECB:
301  return EVP_aes_128_ecb();
302  case PB_ENCRYPTION_AES_128_CBC:
303  return EVP_aes_128_cbc();
304 
305  case PB_ENCRYPTION_AES_256_ECB:
306  return EVP_aes_256_ecb();
307  case PB_ENCRYPTION_AES_256_CBC:
308  return EVP_aes_256_cbc();
309 
310  default:
311  throw std::runtime_error("Unknown cipher type");
312  }
313 }
314 
315 /** Get cipher by name constants.
316  * @param cipher cipher name
317  * @return cipher engine
318  */
319 int
320 cipher_name_to_id(const char *cipher)
321 {
322  if (strcmp(cipher, LN_aes_128_ecb) == 0) {
323  return PB_ENCRYPTION_AES_128_ECB;
324  } else if (strcmp(cipher, LN_aes_128_cbc) == 0) {
325  return PB_ENCRYPTION_AES_128_CBC;
326  } else if (strcmp(cipher, LN_aes_256_ecb) == 0) {
327  return PB_ENCRYPTION_AES_256_ECB;
328  } else if (strcmp(cipher, LN_aes_256_cbc) == 0) {
329  return PB_ENCRYPTION_AES_256_CBC;
330  } else {
331  throw std::runtime_error("Unknown cipher type");
332  }
333 }
334 
335 
336 /** Get cipher by name constants.
337  * @param cipher cipher name
338  * @return cipher engine
339  */
340 const EVP_CIPHER *
341 cipher_by_name(const char *cipher)
342 {
343  if (strcmp(cipher, LN_aes_128_ecb) == 0) {
344  return EVP_aes_128_ecb();
345  } else if (strcmp(cipher, LN_aes_128_cbc) == 0) {
346  return EVP_aes_128_cbc();
347  } else if (strcmp(cipher, LN_aes_256_ecb) == 0) {
348  return EVP_aes_256_ecb();
349  } else if (strcmp(cipher, LN_aes_256_cbc) == 0) {
350  return EVP_aes_256_cbc();
351  } else {
352  throw std::runtime_error("Unknown cipher type");
353  }
354 }
355 
356 
357 } // end namespace fawkes
BufferEncryptor(const std::string &key, std::string cipher_name="AES-128-ECB")
Constructor.
Definition: crypto.cpp:64
~BufferEncryptor()
Destructor.
Definition: crypto.cpp:86
~BufferDecryptor()
Destructor.
Definition: crypto.cpp:186
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:154
size_t decrypt(int cipher, const void *enc, size_t enc_size, void *plain, size_t plain_size)
Decrypt a buffer.
Definition: crypto.cpp:224
BufferDecryptor(const std::string &key)
Constructor.
Definition: crypto.cpp:179
void encrypt(const std::string &plain, std::string &enc)
Encrypt a buffer.
Definition: crypto.cpp:98