static void test_cipher_aead_test_vectors(void) { struct dcrypt_context_symmetric *ctx; const char *error = NULL; test_begin("test_cipher_aead_test_vectors"); if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_ENCRYPT, &ctx, &error)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); return; } buffer_t *key, *iv, *aad, *pt, *ct, *tag, *tag_res, *res; key = buffer_create_dynamic(pool_datastack_create(), 16); iv = buffer_create_dynamic(pool_datastack_create(), 16); aad = buffer_create_dynamic(pool_datastack_create(), 16); pt = buffer_create_dynamic(pool_datastack_create(), 16); ct = buffer_create_dynamic(pool_datastack_create(), 16); tag = buffer_create_dynamic(pool_datastack_create(), 16); res = buffer_create_dynamic(pool_datastack_create(), 16); tag_res = buffer_create_dynamic(pool_datastack_create(), 16); hex_to_binary("feffe9928665731c6d6a8f9467308308", key); hex_to_binary("cafebabefacedbaddecaf888", iv); hex_to_binary("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", pt); hex_to_binary("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985", ct); hex_to_binary("4d5c2af327cd64a62cf35abd2ba6fab4", tag); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); test_assert(dcrypt_ctx_sym_init(ctx, &error)); test_assert(dcrypt_ctx_sym_update(ctx, pt->data, pt->used, res, &error)); test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); test_assert(dcrypt_ctx_sym_get_tag(ctx, tag_res)); test_assert(buffer_cmp(ct, res) == TRUE); test_assert(buffer_cmp(tag, tag_res) == TRUE); dcrypt_ctx_sym_destroy(&ctx); if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_DECRYPT, &ctx, &error)) { test_assert_failed("dcrypt_ctx_sym_create", __FILE__, __LINE__-1); } else { buffer_set_used_size(res, 0); dcrypt_ctx_sym_set_key(ctx, key->data, key->used); dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); dcrypt_ctx_sym_set_tag(ctx, tag->data, tag->used); test_assert(dcrypt_ctx_sym_init(ctx, &error)); test_assert(dcrypt_ctx_sym_update(ctx, ct->data, ct->used, res, &error)); test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); test_assert(buffer_cmp(pt, res) == TRUE); dcrypt_ctx_sym_destroy(&ctx); } test_end(); }
static ssize_t i_stream_decrypt_read(struct istream_private *stream) { struct decrypt_istream *dstream = (struct decrypt_istream *)stream; const unsigned char *data; size_t size, decrypt_size; const char *error = NULL; int ret; bool check_mac = FALSE; /* not if it's broken */ if (stream->istream.stream_errno != 0) return -1; for (;;) { /* remove skipped data from buffer */ if (stream->skip > 0) { i_assert(stream->skip <= dstream->buf->used); buffer_delete(dstream->buf, 0, stream->skip); stream->pos -= stream->skip; stream->skip = 0; } stream->buffer = dstream->buf->data; i_assert(stream->pos <= dstream->buf->used); if (stream->pos >= dstream->istream.max_buffer_size) { /* stream buffer still at maximum */ return -2; } /* if something is already decrypted, return as much of it as we can */ if (dstream->initialized && dstream->buf->used > 0) { size_t new_pos, bytes; /* only return up to max_buffer_size bytes, even when buffer actually has more, as not to confuse the caller */ if (dstream->buf->used <= dstream->istream.max_buffer_size) { new_pos = dstream->buf->used; if (dstream->finalized) stream->istream.eof = TRUE; } else { new_pos = dstream->istream.max_buffer_size; } bytes = new_pos - stream->pos; stream->pos = new_pos; return (ssize_t)bytes; } if (dstream->finalized) { /* all data decrypted */ stream->istream.eof = TRUE; return -1; } /* need to read more input */ ret = i_stream_read(stream->parent); if (ret == 0 || ret == -2) return ret; data = i_stream_get_data(stream->parent, &size); if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) { stream->istream.stream_errno = stream->parent->stream_errno; /* file was empty */ if (!dstream->initialized && size == 0 && stream->parent->eof) { stream->istream.eof = TRUE; return -1; } if (stream->istream.stream_errno != 0) return -1; if (!dstream->initialized) { io_stream_set_error(&stream->iostream, "Decryption error: %s", "Input truncated in decryption header"); stream->istream.stream_errno = EINVAL; return -1; } /* final block */ if (dcrypt_ctx_sym_final(dstream->ctx_sym, dstream->buf, &error)) { dstream->finalized = TRUE; continue; } io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (!dstream->initialized) { ssize_t hret; if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) { if (hret < 0) { if (stream->istream.stream_errno == 0) /* assume temporary failure */ stream->istream.stream_errno = EIO; return -1; } if (hret == 0 && stream->parent->eof) { /* not encrypted by us */ stream->istream.stream_errno = EINVAL; io_stream_set_error(&stream->iostream, "Truncated header"); return -1; } } if (hret == 0) { /* see if we can get more data */ continue; } else { /* clean up buffer */ safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used); buffer_set_used_size(dstream->buf, 0); i_stream_skip(stream->parent, hret); } data = i_stream_get_data(stream->parent, &size); } decrypt_size = size; if (dstream->use_mac) { if (stream->parent->eof) { if (decrypt_size < dstream->ftr) { io_stream_set_error(&stream->iostream, "Decryption error: footer is longer than data"); stream->istream.stream_errno = EINVAL; return -1; } check_mac = TRUE; } else { /* ignore footer's length of data until we reach EOF */ size -= dstream->ftr; } decrypt_size -= dstream->ftr; if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, data, decrypt_size, &error)) { io_stream_set_error(&stream->iostream, "MAC error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } } } if (check_mac) { if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; buffer_t db; buffer_create_from_data(&db, dgst, sizeof(dgst)); if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: %s", error); stream->istream.stream_errno = EINVAL; return -1; } if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { io_stream_set_error(&stream->iostream, "Cannot verify MAC: mismatch"); stream->istream.stream_errno = EINVAL; return -1; } } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr); } } if (!dcrypt_ctx_sym_update(dstream->ctx_sym, data, decrypt_size, dstream->buf, &error)) { io_stream_set_error(&stream->iostream, "Decryption error: %s", error); stream->istream.stream_errno = EINVAL; return -1; } i_stream_skip(stream->parent, size); } }