20221212
[devspec.git] / devspec.en_US / project / recutils / src / rec-crypt.c
blob1f6455d324a1c0d7b94fe6f61623729602f43393
1 /* -*- mode: C -*-
3 * File: rec-crypt.c
4 * Date: Fri Aug 26 19:50:51 2011
6 * GNU recutils - Encryption routines
8 */
10 /* Copyright (C) 2011-2019 Jose E. Marchesi */
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.
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.
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/>.
26 #include <config.h>
28 #include <string.h>
29 #include <gcrypt.h>
30 #include <crc.h>
31 #include <base64.h>
33 #include <rec.h>
34 #include <rec-utils.h>
36 /* Size of a block in AES128 */
37 #define AESV2_BLKSIZE 16
38 #define AESV2_KEYSIZE 16
40 #define SALT_SIZE 4
42 static bool
43 rec_field_encrypted_p (rec_field_t field)
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));
50 bool
51 rec_encrypt (char *in,
52 size_t in_size,
53 const char *password,
54 char **out,
55 size_t *out_size)
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;
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.
71 We store the integer always in little-endian. */
73 crc = crc32 (in, in_size);
75 #if defined WORDS_BIGENDIAN
76 crc = rec_endian_swap (crc);
77 #endif
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);
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. */
88 if ((real_in_size % AESV2_BLKSIZE) != 0)
89 padding = AESV2_BLKSIZE - (real_in_size % AESV2_BLKSIZE);
90 else
91 padding = 0;
93 if (padding != 0)
95 real_in_size = real_in_size + padding;
96 real_in = realloc (real_in, real_in_size);
98 for (i = 0; i < padding; i++)
99 real_in[real_in_size - i - 1] = '\0';
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;
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];
114 /* Set both the key and the IV vector. */
115 if (gcry_cipher_setkey (handler, key, AESV2_KEYSIZE)
116 != GPG_ERR_NO_ERROR)
118 gcry_cipher_close (handler);
119 return false;
122 gcry_create_nonce (iv, SALT_SIZE);
124 for (i = SALT_SIZE; i < AESV2_BLKSIZE; i++)
125 iv[i] = i;
127 if (gcry_cipher_setiv (handler, iv, AESV2_BLKSIZE)
128 != GPG_ERR_NO_ERROR)
130 gcry_cipher_close (handler);
131 return false;
134 *out_size = real_in_size + SALT_SIZE;
135 *out = malloc (*out_size);
137 /* Append salt at the end of the output. */
138 memcpy (*out + real_in_size, iv, SALT_SIZE);
140 /* Encrypt the data. */
141 if (gcry_cipher_encrypt (handler,
142 *out,
143 real_in_size,
144 real_in,
145 real_in_size) != 0)
147 /* Error. */
148 gcry_cipher_close (handler);
149 return false;
152 /* Close the handler. */
153 gcry_cipher_close (handler);
155 return true;
158 bool
159 rec_decrypt (char *in,
160 size_t in_size,
161 const char *password,
162 char **out,
163 size_t *out_size)
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;
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;
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;
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];
189 /* Set both the key and the IV vector. */
190 if (gcry_cipher_setkey (handler, key, AESV2_KEYSIZE)
191 != GPG_ERR_NO_ERROR)
193 printf ("error setting key\n");
194 gcry_cipher_close (handler);
195 return false;
198 /* Extract salt at the end of the output. */
199 memcpy (iv, in + in_size - salt_size, salt_size);
201 for (i = salt_size; i < AESV2_BLKSIZE; i++)
202 iv[i] = i;
204 if (gcry_cipher_setiv (handler, iv, AESV2_BLKSIZE)
205 != GPG_ERR_NO_ERROR)
207 gcry_cipher_close (handler);
208 return false;
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,
218 in_size - salt_size) != 0)
220 /* Error. */
221 gcry_cipher_close (handler);
222 return false;
225 /* Make sure the decrypted data is ok by checking the CRC at the end
226 of the sequence. */
228 if (strlen(*out) > 4)
230 uint32_t crc = 0;
232 memcpy (&crc, *out + strlen(*out) - 4, 4);
233 #if defined WORDS_BIGENDIAN
234 crc = rec_endian_swap (crc);
235 #endif
237 if (crc32 (*out, strlen(*out) - 4) != crc)
239 gcry_cipher_close (handler);
240 return false;
243 (*out)[strlen(*out) - 4] = '\0';
245 else
247 gcry_cipher_close (handler);
248 return false;
251 /* Close the handler. */
252 gcry_cipher_close (handler);
254 return true;
257 bool
258 rec_encrypt_record (rec_rset_t rset,
259 rec_record_t record,
260 const char *password)
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;
268 res = true;
270 if (rset)
272 confidential_fields = rec_rset_confidential (rset);
273 for (i = 0; i < rec_fex_size (confidential_fields); i++)
275 field_name = rec_fex_elem_field_name (rec_fex_get (confidential_fields, i));
277 num_fields = rec_record_get_num_fields_by_name (record, field_name);
278 for (k = 0; k < num_fields; k++)
280 field = rec_record_get_field_by_name (record, field_name, k);
281 if (field)
283 res = rec_encrypt_field (field, password);
284 if (!res)
285 break;
291 return res;
294 bool
295 rec_encrypt_field (rec_field_t field,
296 const char *password)
298 char *field_value;
299 char *field_value_encrypted;
300 char *field_value_base64;
301 size_t out_size, base64_size;
302 char *aux;
304 field_value = strdup (rec_field_value (field));
305 if (!field_value)
306 return false;
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;
314 if (!rec_encrypt (field_value,
315 strlen (field_value),
316 password,
317 &field_value_encrypted,
318 &out_size))
319 return false;
321 /* Encode the encrypted value into base64. */
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);
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;
345 /* Replace the value of the field. */
346 rec_field_set_value (field, field_value_base64);
348 /* Free resources. */
349 free (field_value);
350 free (field_value_encrypted);
351 free (field_value_base64);
353 return true;
356 bool
357 rec_decrypt_field (rec_field_t field,
358 const char *password)
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;
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;
372 /* Skip the "encrypted-" prefix. */
373 field_value = rec_field_value (field) + strlen (REC_ENCRYPTED_PREFIX);
375 /* Decode the Base64. */
377 if (base64_decode_alloc (field_value,
378 strlen(field_value),
379 &base64_decoded,
380 &base64_decoded_size))
382 base64_decode (field_value,
383 strlen(field_value),
384 base64_decoded,
385 &base64_decoded_size);
387 /* Decrypt. */
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);
396 /* Free resources. */
397 free (base64_decoded);
400 return true;
403 bool
404 rec_decrypt_record (rec_rset_t rset,
405 rec_record_t record,
406 const char *password)
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;
414 if (rset)
416 confidential_fields = rec_rset_confidential (rset);
417 for (i = 0; i < rec_fex_size (confidential_fields); i++)
419 field_name = rec_fex_elem_field_name (rec_fex_get (confidential_fields, i));
421 num_fields = rec_record_get_num_fields_by_name (record, field_name);
422 for (k = 0; k < num_fields; k++)
424 field = rec_record_get_field_by_name (record, field_name, k);
425 if (field)
427 res = rec_decrypt_field (field, password);
428 if (!res)
429 break;
435 return res;
438 /* End of rec-crypt.c */