Implement HMAC verification for chunk data and header.

Do not ask for decryption passwd twice.
This commit is contained in:
Moinak Ghosh 2012-10-18 22:55:41 +05:30
parent d13aad830e
commit 1eb8c94fed
4 changed files with 98 additions and 21 deletions

View file

@ -541,7 +541,7 @@ err0:
}
int
get_pw_string(char pw[MAX_PW_LEN], char *prompt)
get_pw_string(char pw[MAX_PW_LEN], char *prompt, int twice)
{
int fd, len;
FILE *input, *strm;
@ -575,6 +575,7 @@ get_pw_string(char pw[MAX_PW_LEN], char *prompt)
return (-1);
}
if (twice) {
fprintf(stderr, "%s (once more): ", prompt);
fflush(stderr);
s = fgets(pw2, MAX_PW_LEN, input);
@ -592,6 +593,11 @@ get_pw_string(char pw[MAX_PW_LEN], char *prompt)
memset(pw2, 0, MAX_PW_LEN);
return (-1);
}
} else {
tcsetattr(fd, TCSANOW, &oldt);
fflush(strm);
fputs("\n", stderr);
}
len = strlen(pw1);
pw1[len-1] = '\0';

View file

@ -85,7 +85,7 @@ int crypto_buf(crypto_ctx_t *cctx, uchar_t *from, uchar_t *to, ssize_t bytes, ui
uint64_t crypto_nonce(crypto_ctx_t *cctx);
void crypto_clean_pkey(crypto_ctx_t *cctx);
void cleanup_crypto(crypto_ctx_t *cctx);
int get_pw_string(char pw[MAX_PW_LEN], char *prompt);
int get_pw_string(char pw[MAX_PW_LEN], char *prompt, int twice);
/*
* HMAC functions.

78
main.c
View file

@ -292,6 +292,32 @@ redo:
goto cont;
}
/*
* Verify HMAC first before anything else.
*/
if (encrypt_type) {
unsigned int len;
len = mac_bytes;
deserialize_checksum(checksum, tdat->compressed_chunk + cksum_bytes, mac_bytes);
memset(tdat->compressed_chunk + cksum_bytes, 0, mac_bytes);
hmac_reinit(&tdat->chunk_hmac);
hmac_update(&tdat->chunk_hmac, (uchar_t *)&tdat->len_cmp_be, sizeof (tdat->len_cmp_be));
hmac_update(&tdat->chunk_hmac, tdat->compressed_chunk,
tdat->len_cmp + cksum_bytes + mac_bytes + CHUNK_FLAG_SZ);
hmac_final(&tdat->chunk_hmac, tdat->checksum, &len);
if (memcmp(checksum, tdat->checksum, len) != 0) {
/*
* HMAC verification failure is fatal.
*/
fprintf(stderr, "Chunk %d, HMAC verification failed\n", tdat->id);
main_cancel = 1;
tdat->len_cmp = 0;
sem_post(&tdat->cmp_done_sem);
return;
}
}
cseg = tdat->compressed_chunk + cksum_bytes + mac_bytes;
_chunksize = tdat->chunksize;
deserialize_checksum(tdat->checksum, tdat->compressed_chunk, cksum_bytes);
@ -644,7 +670,7 @@ start_decompress(const char *filename, const char *to_filename)
if (!pwd_file) {
pw_len = get_pw_string(pw,
"Please enter encryption password");
"Please enter decryption password", 0);
if (pw_len == -1) {
memset(salt2, 0, saltlen);
free(salt2);
@ -680,6 +706,7 @@ start_decompress(const char *filename, const char *to_filename)
perror(" ");
memset(salt2, 0, saltlen);
free(salt2);
close(uncompfd); unlink(to_filename);
err_exit(0, "Failed to get password.\n");
}
close(fd);
@ -690,6 +717,7 @@ start_decompress(const char *filename, const char *to_filename)
memset(salt2, 0, saltlen);
free(salt2);
memset(pw, 0, MAX_PW_LEN);
close(uncompfd); unlink(to_filename);
err_exit(0, "Failed to initialize crypto\n");
}
memset(salt2, 0, saltlen);
@ -701,6 +729,7 @@ start_decompress(const char *filename, const char *to_filename)
* Verify header HMAC.
*/
if (hmac_init(&hdr_mac, cksum, &crypto_ctx) == -1) {
close(uncompfd); unlink(to_filename);
err_exit(0, "Cannot initialize header hmac.\n");
}
hmac_update(&hdr_mac, (uchar_t *)algo, ALGO_SZ);
@ -715,7 +744,8 @@ start_decompress(const char *filename, const char *to_filename)
hmac_final(&hdr_mac, hdr_hash1, &hlen);
hmac_cleanup(&hdr_mac);
if (memcmp(hdr_hash2, hdr_hash1, mac_bytes) != 0) {
err_exit(0, "Header verification failed! File tampered.\n");
close(uncompfd); unlink(to_filename);
err_exit(0, "Header verification failed! File tampered or wrong password.\n");
}
} else {
mac_bytes = 0;
@ -768,6 +798,13 @@ start_decompress(const char *filename, const char *to_filename)
} else {
tdat->rctx = NULL;
}
if (encrypt_type) {
if (hmac_init(&tdat->chunk_hmac, cksum, &crypto_ctx) == -1) {
fprintf(stderr, "Cannot initialize chunk hmac.\n");
UNCOMP_BAIL;
}
}
if (pthread_create(&(tdat->thr), NULL, perform_decompress,
(void *)tdat) != 0) {
perror("Error in thread creation: ");
@ -776,6 +813,11 @@ start_decompress(const char *filename, const char *to_filename)
}
thread = 1;
if (encrypt_type) {
/* Erase encryption key bytes stored as a plain array. No longer reqd. */
crypto_clean_pkey(&crypto_ctx);
}
w.dary = dary;
w.wfd = uncompfd;
w.nprocs = nprocs;
@ -816,6 +858,7 @@ start_decompress(const char *filename, const char *to_filename)
"file corrupt\n", chunk_num);
UNCOMP_BAIL;
}
tdat->len_cmp_be = tdat->len_cmp; // Needed for HMAC
tdat->len_cmp = htonll(tdat->len_cmp);
/*
@ -1069,7 +1112,7 @@ plain_compress:
*/
if (encrypt_type) {
/*
* Encryption algorithm should not change the size and
* Encryption algorithm must not change the size and
* encryption is in-place.
*/
rv = crypto_buf(&crypto_ctx, compressed_chunk, compressed_chunk,
@ -1120,6 +1163,22 @@ plain_compress:
*/
*(tdat->compressed_chunk) = type;
/*
* If encrypting, compute HMAC for entire chunk (hdr + data)
*/
if (encrypt_type) {
uchar_t *mac_ptr;
unsigned int hlen;
uchar_t chash[mac_bytes];
/* Clean out mac_bytes to 0 for stable hash. */
mac_ptr = tdat->cmp_seg + sizeof (tdat->len_cmp) + cksum_bytes;
memset(mac_ptr, 0, mac_bytes);
hmac_reinit(&tdat->chunk_hmac);
hmac_update(&tdat->chunk_hmac, tdat->cmp_seg, tdat->len_cmp);
hmac_final(&tdat->chunk_hmac, chash, &hlen);
serialize_checksum(chash, mac_ptr, hlen);
}
cont:
sem_post(&tdat->cmp_done_sem);
goto redo;
@ -1232,7 +1291,7 @@ start_compress(const char *filename, uint64_t chunksize, int level)
compressed_chunksize += mac_bytes;
if (!pwd_file) {
pw_len = get_pw_string(pw,
"Please enter encryption password");
"Please enter encryption password", 1);
if (pw_len == -1) {
err_exit(1, "Failed to get password.\n");
}
@ -1408,6 +1467,12 @@ start_compress(const char *filename, uint64_t chunksize, int level)
tdat->rctx = NULL;
}
if (encrypt_type) {
if (hmac_init(&tdat->chunk_hmac, cksum, &crypto_ctx) == -1) {
fprintf(stderr, "Cannot initialize chunk hmac.\n");
COMP_BAIL;
}
}
if (pthread_create(&(tdat->thr), NULL, perform_compress,
(void *)tdat) != 0) {
perror("Error in thread creation: ");
@ -1466,6 +1531,9 @@ start_compress(const char *filename, uint64_t chunksize, int level)
hmac_final(&hdr_mac, hdr_hash, &hlen);
hmac_cleanup(&hdr_mac);
/* Erase encryption key bytes stored as a plain array. No longer reqd. */
crypto_clean_pkey(&crypto_ctx);
pos = cread_buf;
*((int *)pos) = htonl(crypto_ctx.saltlen);
pos += sizeof (int);
@ -1631,6 +1699,8 @@ comp_done:
sem_post(&tdat->start_sem);
sem_post(&tdat->cmp_done_sem);
pthread_join(tdat->thr, NULL);
if (encrypt_type)
hmac_cleanup(&tdat->chunk_hmac);
}
pthread_join(writer_thr, NULL);
}

View file

@ -163,7 +163,7 @@ struct cmp_data {
dedupe_context_t *rctx;
ssize_t rbytes;
ssize_t chunksize;
ssize_t len_cmp;
ssize_t len_cmp, len_cmp_be;
uchar_t checksum[CKSUM_MAX_BYTES];
int level;
unsigned int id;
@ -175,6 +175,7 @@ struct cmp_data {
sem_t write_done_sem;
void *data;
pthread_t thr;
mac_ctx_t chunk_hmac;
};
#ifdef __cplusplus