/* IF YOU CALL THIS MULTIPLE TIMES WITH THE SAME KEY YOU MUST PROVIDE AN IV POINTER! */ int crypt_data(const unsigned char *data_in, unsigned char *data_out, size_t data_size, const unsigned char *data_mkey, size_t data_mkey_size, unsigned char *data_new_hmac, const unsigned char *data_chk_hmac, size_t data_hmac_size, unsigned char **IV_start, int mode) { if (mode != MODE_ENCRYPT && mode != MODE_DECRYPT) { fprintf(stderr, "crypt_data called with invalid mode %d\n", mode); return -1; } symmetric_CTR ctr; #ifdef _POSIX_MEMLOCK_RANGE if (mlock(&ctr, sizeof(ctr)) != 0) { fprintf(stderr, "WARNING: mlock failed at %s:%d - ", __FILE__, __LINE__); perror(""); } #endif int err; int ret = 0; /* return code */ unsigned char *IV; unsigned long IV_size = 16; int hash_idx = find_hash("sha256"); size_t data_ckey_size, data_hkey_size; data_ckey_size = data_hkey_size = data_mkey_size; unsigned char *subkeys = safe_malloc(data_ckey_size + data_hkey_size); #ifdef _POSIX_MEMLOCK_RANGE if (mlock(subkeys, data_ckey_size + data_hkey_size) != 0) { fprintf(stderr, "WARNING: mlock failed at %s:%d - ", __FILE__, __LINE__); perror(""); } #endif unsigned char *data_ckey = subkeys + 0; unsigned char *data_hkey = subkeys + data_ckey_size; pbkdf2(data_mkey, data_mkey_size, "H", 1, SUBKEY_ITER, hash_idx, data_hkey, &data_hkey_size); pbkdf2(data_mkey, data_mkey_size, "C", 1, SUBKEY_ITER, hash_idx, data_ckey, &data_ckey_size); if (IV_start == NULL || *IV_start == NULL) { IV = safe_malloc(IV_size); /* fprintf(stderr, "Initializing key-based IV\n"); */ /* This is at least as secure as starting with a zeroed IV */ pbkdf2(data_mkey, data_mkey_size, "I", 1, SUBKEY_ITER, hash_idx, IV, &IV_size); } if (IV_start != NULL) { if (*IV_start != NULL) { /* fprintf(stderr, "IV = *IV_start\n"); */ IV = *IV_start; } else { /* fprintf(stderr, "*IV_start = IV\n"); */ *IV_start = IV; } } if (mode == MODE_DECRYPT && data_chk_hmac != NULL) { if ((err = hmac_vrfymem(hash_idx, data_hkey, data_hkey_size, data_in, data_size, data_chk_hmac, (long unsigned int *)&data_hmac_size)) != CRYPT_OK) { crypt_data_return(THRCR_BADMAC); } } /* LTC_CTR_RFC3686 is needed to avoid reusing a counter value. */ if ((err = ctr_start(find_cipher("aes"), IV, data_ckey, data_ckey_size, 0, CTR_COUNTER_BIG_ENDIAN | LTC_CTR_RFC3686, &ctr)) != CRYPT_OK) { fprintf(stderr, "Error initializing cipher: %d\n", err); crypt_data_return(-1); } /* ctr_encrypt is used for both encryption and decryption */ if ((err = ctr_encrypt(data_in, data_out, data_size, &ctr)) != CRYPT_OK) { fprintf(stderr, "ctr_encrypt error: %s\n", error_to_string(err)); ctr_done(&ctr); /* done with cipher, clean up keys */ crypt_data_return(-1); } ctr_done(&ctr); /* done with cipher, clean up keys */ if (mode == MODE_ENCRYPT && data_new_hmac != NULL) { if ((err = hmac_memory(hash_idx, data_hkey, data_hkey_size, data_out, data_size, data_new_hmac, (long unsigned int *)&data_hmac_size)) != CRYPT_OK) { fprintf(stderr, "hmac error: %s\n", error_to_string(err)); crypt_data_return(-1); } } crypt_data_return: /* before actually returning, make sure key material isn't in memory */ MEMWIPE(&ctr, sizeof(ctr)); MEMWIPE(subkeys, data_ckey_size + data_hkey_size); #ifdef _POSIX_MEMLOCK_RANGE munlock(subkeys, data_ckey_size + data_hkey_size); #endif safe_free(subkeys); /* save the IV */ if (IV_start != NULL && *IV_start != NULL) { /* fprintf(stderr, "*IV_start = ctr.ctr\n"); */ ctr_getiv(*IV_start, &IV_size, &ctr); } else { safe_free(IV); } return ret; }
int modes_test(void) { unsigned char pt[64], ct[64], tmp[64], key[16], iv[16], iv2[16]; int x, cipher_idx; symmetric_CBC cbc; symmetric_CFB cfb; symmetric_OFB ofb; symmetric_CTR ctr; unsigned long l; /* make a random pt, key and iv */ yarrow_read(pt, 64, &test_yarrow); yarrow_read(key, 16, &test_yarrow); yarrow_read(iv, 16, &test_yarrow); /* get idx of AES handy */ cipher_idx = find_cipher("aes"); if (cipher_idx == -1) { printf("test requires AES"); return 1; } /* test CBC mode */ /* encode the block */ DO(cbc_start(cipher_idx, iv, key, 16, 0, &cbc)); l = sizeof(iv2); DO(cbc_getiv(iv2, &l, &cbc)); if (l != 16 || memcmp(iv2, iv, 16)) { printf("cbc_getiv failed"); return 1; } for (x = 0; x < 4; x++) { DO(cbc_encrypt(pt+x*16, ct+x*16, &cbc)); } /* decode the block */ DO(cbc_setiv(iv2, l, &cbc)); zeromem(tmp, sizeof(tmp)); for (x = 0; x < 4; x++) { DO(cbc_decrypt(ct+x*16, tmp+x*16, &cbc)); } if (memcmp(tmp, pt, 64) != 0) { printf("CBC failed"); return 1; } /* test CFB mode */ /* encode the block */ DO(cfb_start(cipher_idx, iv, key, 16, 0, &cfb)); l = sizeof(iv2); DO(cfb_getiv(iv2, &l, &cfb)); /* note we don't memcmp iv2/iv since cfb_start processes the IV for the first block */ if (l != 16) { printf("cfb_getiv failed"); return 1; } DO(cfb_encrypt(pt, ct, 64, &cfb)); /* decode the block */ DO(cfb_setiv(iv, l, &cfb)); zeromem(tmp, sizeof(tmp)); DO(cfb_decrypt(ct, tmp, 64, &cfb)); if (memcmp(tmp, pt, 64) != 0) { printf("CFB failed"); return 1; } /* test OFB mode */ /* encode the block */ DO(ofb_start(cipher_idx, iv, key, 16, 0, &ofb)); l = sizeof(iv2); DO(ofb_getiv(iv2, &l, &ofb)); if (l != 16 || memcmp(iv2, iv, 16)) { printf("ofb_getiv failed"); return 1; } DO(ofb_encrypt(pt, ct, 64, &ofb)); /* decode the block */ DO(ofb_setiv(iv2, l, &ofb)); zeromem(tmp, sizeof(tmp)); DO(ofb_decrypt(ct, tmp, 64, &ofb)); if (memcmp(tmp, pt, 64) != 0) { printf("OFB failed"); return 1; } /* test CTR mode */ /* encode the block */ DO(ctr_start(cipher_idx, iv, key, 16, 0, &ctr)); l = sizeof(iv2); DO(ctr_getiv(iv2, &l, &ctr)); if (l != 16 || memcmp(iv2, iv, 16)) { printf("ctr_getiv failed"); return 1; } DO(ctr_encrypt(pt, ct, 64, &ctr)); /* decode the block */ DO(ctr_setiv(iv2, l, &ctr)); zeromem(tmp, sizeof(tmp)); DO(ctr_decrypt(ct, tmp, 64, &ctr)); if (memcmp(tmp, pt, 64) != 0) { printf("CTR failed"); return 1; } return 0; }