1 /* -*- mode: C -*-
2  *
3  *       File:         rec-crypt.c
4  *       Date:         Fri Aug 26 19:50:51 2011
5  *
6  *       GNU recutils - Encryption routines
7  *
8  */
9 
10 /* Copyright (C) 2011-2019 Jose E. Marchesi */
11 
12 /* This program is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include <config.h>
27 
28 #include <string.h>
29 #include <gcrypt.h>
30 #include <crc.h>
31 #include <base64.h>
32 
33 #include <rec.h>
34 #include <rec-utils.h>
35 
36 /* Size of a block in AES128 */
37 #define AESV2_BLKSIZE 16
38 #define AESV2_KEYSIZE 16
39 
40 #define SALT_SIZE 4
41 
42 static bool
rec_field_encrypted_p(rec_field_t field)43 rec_field_encrypted_p (rec_field_t field)
44 {
45   return ((strlen (rec_field_value (field)) > strlen (REC_ENCRYPTED_PREFIX))
46           && (strncmp (rec_field_value (field), REC_ENCRYPTED_PREFIX,
47                        strlen (REC_ENCRYPTED_PREFIX)) == 0));
48 }
49 
50 bool
rec_encrypt(char * in,size_t in_size,const char * password,char ** out,size_t * out_size)51 rec_encrypt (char   *in,
52              size_t  in_size,
53              const char   *password,
54              char  **out,
55              size_t *out_size)
56 {
57   gcry_cipher_hd_t handler;
58   size_t i;
59   size_t password_size;
60   char key[AESV2_KEYSIZE];
61   char iv[AESV2_BLKSIZE];
62   size_t padding;
63   uint32_t crc;
64   char *real_in;
65   size_t real_in_size;
66 
67   /* Append four bytes to the input buffer, containing the CRC of its
68      contents.  This will be used as a control token to determine
69      whether the correct key is used in decryption.
70 
71      We store the integer always in little-endian. */
72 
73   crc = crc32 (in, in_size);
74 
75 #if defined WORDS_BIGENDIAN
76   crc = rec_endian_swap (crc);
77 #endif
78 
79   real_in_size = in_size + 4;
80   real_in = malloc (real_in_size + 4);
81   memcpy (real_in, in, real_in_size);
82   memcpy (real_in + real_in_size - 4, &crc, 4);
83 
84   /* The size of the input buffer must be bigger than AESV2_BLKSIZE,
85      and must contain an entire number of blocks.  We assure that by
86      padding the buffer with \0 characters.  */
87 
88   if ((real_in_size % AESV2_BLKSIZE) != 0)
89     padding = AESV2_BLKSIZE - (real_in_size % AESV2_BLKSIZE);
90   else
91     padding = 0;
92 
93   if (padding != 0)
94     {
95       real_in_size = real_in_size + padding;
96       real_in = realloc (real_in, real_in_size);
97 
98       for (i = 0; i < padding; i++)
99         real_in[real_in_size - i - 1] = '\0';
100     }
101 
102   /* Create the handler.  */
103   if (gcry_cipher_open (&handler,
104                         GCRY_CIPHER_AES128,
105                         GCRY_CIPHER_MODE_CBC,
106                         0) != GPG_ERR_NO_ERROR)
107     return false;
108 
109   /* Set the key of the cypher.  */
110   password_size = strlen (password);
111   for (i = 0; i < AESV2_KEYSIZE; i++)
112     key[i] = password[i % password_size];
113 
114   /* Set both the key and the IV vector.  */
115   if (gcry_cipher_setkey (handler, key, AESV2_KEYSIZE)
116       != GPG_ERR_NO_ERROR)
117     {
118       gcry_cipher_close (handler);
119       return false;
120     }
121 
122   gcry_create_nonce (iv, SALT_SIZE);
123 
124   for (i = SALT_SIZE; i < AESV2_BLKSIZE; i++)
125       iv[i] = i;
126 
127   if (gcry_cipher_setiv (handler, iv, AESV2_BLKSIZE)
128       != GPG_ERR_NO_ERROR)
129     {
130       gcry_cipher_close (handler);
131       return false;
132     }
133 
134   *out_size = real_in_size + SALT_SIZE;
135   *out = malloc (*out_size);
136 
137   /* Append salt at the end of the output.  */
138   memcpy (*out + real_in_size, iv, SALT_SIZE);
139 
140   /* Encrypt the data.  */
141   if (gcry_cipher_encrypt (handler,
142                            *out,
143                            real_in_size,
144                            real_in,
145                            real_in_size) != 0)
146     {
147       /* Error.  */
148       gcry_cipher_close (handler);
149       return false;
150     }
151 
152   /* Close the handler.  */
153   gcry_cipher_close (handler);
154 
155   return true;
156 }
157 
158 bool
rec_decrypt(char * in,size_t in_size,const char * password,char ** out,size_t * out_size)159 rec_decrypt (char   *in,
160              size_t  in_size,
161              const char   *password,
162              char  **out,
163              size_t *out_size)
164 {
165   gcry_cipher_hd_t handler;
166   size_t i;
167   size_t password_size;
168   char key[AESV2_KEYSIZE];
169   char iv[AESV2_BLKSIZE];
170   size_t salt_size = 0;
171 
172   if (((in_size - SALT_SIZE) % AESV2_BLKSIZE) == 0)
173     salt_size = SALT_SIZE;
174   else if ((in_size % AESV2_BLKSIZE) != 0)
175     return false;
176 
177   /* Create the handler.  */
178   if (gcry_cipher_open (&handler,
179                         GCRY_CIPHER_AES128,
180                         GCRY_CIPHER_MODE_CBC,
181                         0) != GPG_ERR_NO_ERROR)
182     return false;
183 
184   /* Set the key of the cypher.  */
185   password_size = strlen (password);
186   for (i = 0; i < AESV2_KEYSIZE; i++)
187     key[i] = password[i % password_size];
188 
189   /* Set both the key and the IV vector.  */
190   if (gcry_cipher_setkey (handler, key, AESV2_KEYSIZE)
191       != GPG_ERR_NO_ERROR)
192     {
193       printf ("error setting key\n");
194       gcry_cipher_close (handler);
195       return false;
196     }
197 
198   /* Extract salt at the end of the output.  */
199   memcpy (iv, in + in_size - salt_size, salt_size);
200 
201   for (i = salt_size; i < AESV2_BLKSIZE; i++)
202     iv[i] = i;
203 
204   if (gcry_cipher_setiv (handler, iv, AESV2_BLKSIZE)
205       != GPG_ERR_NO_ERROR)
206     {
207       gcry_cipher_close (handler);
208       return false;
209     }
210 
211   /* Decrypt the data.  */
212   *out_size = in_size - salt_size;
213   *out = malloc (*out_size);
214   if (gcry_cipher_decrypt (handler,
215                            *out,
216                            *out_size,
217                            in,
218                            in_size - salt_size) != 0)
219     {
220       /* Error.  */
221       gcry_cipher_close (handler);
222       return false;
223     }
224 
225   /* Make sure the decrypted data is ok by checking the CRC at the end
226      of the sequence.  */
227 
228   if (strlen(*out) > 4)
229     {
230       uint32_t crc = 0;
231 
232       memcpy (&crc, *out + strlen(*out) - 4, 4);
233 #if defined WORDS_BIGENDIAN
234       crc = rec_endian_swap (crc);
235 #endif
236 
237       if (crc32 (*out, strlen(*out) - 4) != crc)
238         {
239           gcry_cipher_close (handler);
240           return false;
241         }
242 
243       (*out)[strlen(*out) - 4] = '\0';
244     }
245   else
246     {
247       gcry_cipher_close (handler);
248       return false;
249     }
250 
251   /* Close the handler.  */
252   gcry_cipher_close (handler);
253 
254   return true;
255 }
256 
257 bool
rec_encrypt_record(rec_rset_t rset,rec_record_t record,const char * password)258 rec_encrypt_record (rec_rset_t rset,
259                     rec_record_t record,
260                     const char *password)
261 {
262   rec_field_t field;
263   bool res;
264   const char *field_name;
265   rec_fex_t confidential_fields;
266   size_t i, k, num_fields;
267 
268   res = true;
269 
270   if (rset)
271     {
272       confidential_fields = rec_rset_confidential (rset);
273       for (i = 0; i < rec_fex_size (confidential_fields); i++)
274         {
275           field_name = rec_fex_elem_field_name (rec_fex_get (confidential_fields, i));
276 
277           num_fields = rec_record_get_num_fields_by_name (record, field_name);
278           for (k = 0; k < num_fields; k++)
279             {
280               field = rec_record_get_field_by_name (record, field_name, k);
281               if (field)
282                 {
283                   res = rec_encrypt_field (field, password);
284                   if (!res)
285                     break;
286                 }
287             }
288         }
289     }
290 
291   return res;
292 }
293 
294 bool
rec_encrypt_field(rec_field_t field,const char * password)295 rec_encrypt_field (rec_field_t field,
296                    const char *password)
297 {
298   char *field_value;
299   char *field_value_encrypted;
300   char *field_value_base64;
301   size_t out_size, base64_size;
302   char *aux;
303 
304   field_value = strdup (rec_field_value (field));
305   if (!field_value)
306     return false;
307 
308   /* Make sure the field is not already encrypted.  */
309   if ((strlen (rec_field_value (field)) >= strlen (REC_ENCRYPTED_PREFIX))
310       && (strncmp (rec_field_value (field), REC_ENCRYPTED_PREFIX,
311                    strlen (REC_ENCRYPTED_PREFIX)) == 0))
312     return true;
313 
314   if (!rec_encrypt (field_value,
315                     strlen (field_value),
316                     password,
317                     &field_value_encrypted,
318                     &out_size))
319     return false;
320 
321   /* Encode the encrypted value into base64.  */
322 
323   base64_size = base64_encode_alloc (field_value_encrypted,
324                                      out_size,
325                                      &field_value_base64);
326   base64_encode (field_value_encrypted,
327                  out_size,
328                  field_value_base64,
329                  base64_size);
330 
331   /* Prepennd "encrypted-".  */
332   aux = malloc (strlen (field_value_base64)
333                 + strlen (REC_ENCRYPTED_PREFIX) + 1);
334   memcpy (aux,
335           REC_ENCRYPTED_PREFIX,
336           strlen (REC_ENCRYPTED_PREFIX));
337   memcpy (aux + strlen (REC_ENCRYPTED_PREFIX),
338           field_value_base64,
339           strlen (field_value_base64));
340   aux[strlen (field_value_base64)
341       + strlen (REC_ENCRYPTED_PREFIX)] = '\0';
342   free (field_value_base64);
343   field_value_base64 = aux;
344 
345   /* Replace the value of the field.  */
346   rec_field_set_value (field, field_value_base64);
347 
348   /* Free resources.  */
349   free (field_value);
350   free (field_value_encrypted);
351   free (field_value_base64);
352 
353   return true;
354 }
355 
356 bool
rec_decrypt_field(rec_field_t field,const char * password)357 rec_decrypt_field (rec_field_t field,
358                    const char *password)
359 {
360   const char *field_value;
361   char *base64_decoded;
362   size_t base64_decoded_size;
363   char *decrypted_value;
364   size_t decrypted_value_size;
365 
366   /* Make sure the field is encrypted.  */
367   if ((strlen (rec_field_value (field)) < strlen (REC_ENCRYPTED_PREFIX))
368       || (strncmp (rec_field_value (field), REC_ENCRYPTED_PREFIX,
369                    strlen (REC_ENCRYPTED_PREFIX)) != 0))
370     return true;
371 
372   /* Skip the "encrypted-" prefix.  */
373   field_value = rec_field_value (field) + strlen (REC_ENCRYPTED_PREFIX);
374 
375   /* Decode the Base64.  */
376 
377   if (base64_decode_alloc (field_value,
378                            strlen(field_value),
379                            &base64_decoded,
380                            &base64_decoded_size))
381     {
382       base64_decode (field_value,
383                      strlen(field_value),
384                      base64_decoded,
385                      &base64_decoded_size);
386 
387       /* Decrypt.  */
388 
389       if (rec_decrypt (base64_decoded,
390                        base64_decoded_size,
391                        password,
392                        &decrypted_value,
393                        &decrypted_value_size))
394         rec_field_set_value (field, decrypted_value);
395 
396       /* Free resources.  */
397       free (base64_decoded);
398     }
399 
400   return true;
401 }
402 
403 bool
rec_decrypt_record(rec_rset_t rset,rec_record_t record,const char * password)404 rec_decrypt_record (rec_rset_t rset,
405                     rec_record_t record,
406                     const char *password)
407 {
408   bool res = true;
409   size_t i, num_fields, k;
410   rec_field_t field;
411   const char *field_name;
412   rec_fex_t confidential_fields;
413 
414   if (rset)
415     {
416       confidential_fields = rec_rset_confidential (rset);
417       for (i = 0; i < rec_fex_size (confidential_fields); i++)
418         {
419           field_name = rec_fex_elem_field_name (rec_fex_get (confidential_fields, i));
420 
421           num_fields = rec_record_get_num_fields_by_name (record, field_name);
422           for (k = 0; k < num_fields; k++)
423             {
424               field = rec_record_get_field_by_name (record, field_name, k);
425               if (field)
426                 {
427                   res = rec_decrypt_field (field, password);
428                   if (!res)
429                     break;
430                 }
431             }
432         }
433     }
434 
435   return res;
436 }
437 
438 /* End of rec-crypt.c */
439