bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
a81d5c3f5a4ad5d100b258d10d4c75f4a02ab1f6Stephan Bosch
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "lib.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "buffer.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "randgen.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "safe-memset.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "hash-method.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "sha2.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "dcrypt.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "istream.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "istream-decrypt.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "istream-private.h"
17fbd200b78112bd0d89e89598aa01cea72a74e5Martti Rannanjärvi#include "dcrypt-iostream.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include "hex-binary.h"
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#include <arpa/inet.h>
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi#define ISTREAM_DECRYPT_READ_FIRST 15
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct decrypt_istream {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct istream_private istream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_t *buf;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_stream_decrypt_get_key_callback_t *key_callback;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi void *key_context;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct dcrypt_private_key *priv_key;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool initialized;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool finalized;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool use_mac;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uoff_t ftr, pos;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi enum io_stream_encrypt_flags flags;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char *iv; /* original iv, in case seeking is done, future feature */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct dcrypt_context_symmetric *ctx_sym;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct dcrypt_context_hmac *ctx_mac;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi enum decrypt_istream_format format;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi};
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvienum decrypt_istream_format i_stream_encrypt_get_format(const struct istream *input)
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi{
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi return ((const struct decrypt_istream*)input->real_stream)->format;
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi}
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvienum io_stream_encrypt_flags i_stream_encrypt_get_flags(const struct istream *input)
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi{
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi return ((const struct decrypt_istream*)input->real_stream)->flags;
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi}
ebd1c50bc43e08ee0a05ad68c7d48497a1563fabMartti Rannanjärvi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomissize_t i_stream_decrypt_read_header_v1(struct decrypt_istream *stream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data, size_t mlen)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error = NULL;
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen size_t keydata_len = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint16_t len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ec, i = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, *key_ct_pos = NULL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen size_t pos = sizeof(IOSTREAM_CRYPT_MAGIC);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t digest_len = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t key_ct_len = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t key_digest_size = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_t ephemeral_key;
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *secret = t_buffer_create(256);
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *key = t_buffer_create(256);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen if (mlen < 2)
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen return 0;
b9e830a81455faf3c0dadfc9dbf0c7dc8aca955cJosef 'Jeff' Sipek keydata_len = be16_to_cpu_unaligned(data);
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen if (mlen-2 < keydata_len) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* try to read more */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data+=2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi mlen-=2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen while (i < 4 && mlen > 2) {
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen memcpy(&len, data, 2);
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen len = ntohs(len);
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen if (len == 0 || len > mlen-2)
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += 2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi mlen -= 2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi pos += 2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi switch(i++) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi case 0:
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_create_from_const_data(&ephemeral_key, data, len);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi case 1:
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* public key id */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi digest_pos = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi digest_len = len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi case 2:
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* encryption key digest */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi key_digest_pos = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi key_digest_size = len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi case 3:
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* encrypted key data */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi key_ct_pos = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi key_ct_len = len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi pos += len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi mlen -= len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (i < 4) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Invalid or corrupted header");
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen /* was it consumed? */
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen stream->istream.istream.stream_errno =
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen mlen > 2 ? EINVAL : EPIPE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* we don't have a private key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->priv_key == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* see if we can get one */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->key_callback != NULL) {
269a38b5e60ad8698d6ea56e4a500be2f2486795Aki Tuomi const char *key_id = binary_to_hex(digest_pos, digest_len);
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek int ret = stream->key_callback(key_id, &stream->priv_key, &error, stream->key_context);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ret < 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ret == 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Private key not available");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
55a7fe1e4637a8dbc6184b54edeb74ac8276b2c1Aki Tuomi dcrypt_key_ref_private(stream->priv_key);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Private key not available");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *check = t_buffer_create(32);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
8d0e0f098348baa17b165f694e37c518b546b857Aki Tuomi if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Cannot get public key hash: %s", error);
8d0e0f098348baa17b165f694e37c518b546b857Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(digest_pos, check->data, I_MIN(digest_len,check->used)) != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Private key not available");
8d0e0f098348baa17b165f694e37c518b546b857Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* derive shared secret */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &ephemeral_key, secret, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Cannot perform ECDH: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* run it thru SHA256 once */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const struct hash_method *hash = &hash_method_sha256;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char hctx[hash->context_size];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char hres[hash->digest_size];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->init(hctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->loop(hctx, secret->data, secret->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->result(hctx, hres);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* NB! The old code was broken and used this kind of IV - it is not correct, but
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi we need to stay compatible with old data */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* use it to decrypt the actual encryption key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct dcrypt_context_symmetric *dctx;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &dctx, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ec = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_init(dctx, &error) ||
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) ||
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi !dcrypt_ctx_sym_final(dctx, key, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ec = -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_destroy(&dctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ec != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* see if we got the correct key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->init(hctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->loop(hctx, key->data, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->result(hctx, hres);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (key_digest_size != sizeof(hres)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: invalid digest length");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: decrypted key is invalid");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* prime context with key */
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &stream->ctx_sym, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption context create error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* Again, old code used this IV, so we have to use it too */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption init error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->use_mac = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->initialized = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* now we are ready to decrypt stream */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
a84b413ef72378bbe235a13846fe6a84899eaedcTimo Sirainen return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic bool get_msb32(const unsigned char **_data, const unsigned char *end, uint32_t *num_r)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data = *_data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (end-data < 4)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return FALSE;
b9e830a81455faf3c0dadfc9dbf0c7dc8aca955cJosef 'Jeff' Sipek *num_r = be32_to_cpu_unaligned(data);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi *_data += 4;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomibool i_stream_decrypt_der(const unsigned char **_data,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *end, const char **str_r)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data = *_data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned int len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (end-data < 2)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* get us DER encoded length */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((data[1] & 0x80) != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* two byte length */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (end-data < 3)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi len = ((data[1] & 0x7f) << 8) + data[2] + 3;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi len = data[1] + 2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((size_t)(end-data) < len)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi *str_r = dcrypt_oid2name(data, len, NULL);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi *_data += len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomissize_t i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, unsigned int rounds,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data, const unsigned char *end, buffer_t *key, size_t key_len)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi enum dcrypt_key_type ktype;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int keys;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool have_key = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char dgst[32];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t val;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_t buf;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (data == end)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi keys = *data++;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* if we have a key, prefab the digest */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->key_callback == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->priv_key == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_create_from_data(&buf, dgst, sizeof(dgst));
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi if (!dcrypt_key_id_private(stream->priv_key, "sha256", &buf,
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi &error)) {
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: "
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi "dcrypt_key_id_private failed: %s",
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi error);
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi return -1;
2223b7a4487cf641876f2713a0638133270fa1d2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* for each key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi for(;keys>0;keys--) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ktype = *data++;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->key_callback != NULL) {
269a38b5e60ad8698d6ea56e4a500be2f2486795Aki Tuomi const char *hexdgst = binary_to_hex(data, sizeof(dgst)); /* digest length */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* hope you going to give us right key.. */
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek int ret = stream->key_callback(hexdgst, &stream->priv_key, &error, stream->key_context);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ret < 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ret > 0) {
55a7fe1e4637a8dbc6184b54edeb74ac8276b2c1Aki Tuomi dcrypt_key_ref_private(stream->priv_key);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi have_key = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* see if key matches to the one we have */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(dgst, data, sizeof(dgst)) == 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi have_key = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi break;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += sizeof(dgst);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* wasn't correct key, skip over some data */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &val) ||
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi !get_msb32(&data, end, &val))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* didn't find matching key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!have_key) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += sizeof(dgst);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *ephemeral_key;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t ep_key_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *encrypted_key;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t eklen;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *ekhash;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t ekhash_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read ephemeral key (can be missing for RSA) */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &ep_key_len) || (size_t)(end-data) < ep_key_len)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ephemeral_key = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += ep_key_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read encrypted key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi encrypted_key = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += eklen;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read key data hash */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &ekhash_len) || (size_t)(end-data) < ekhash_len)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ekhash = data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += ekhash_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* decrypt the seed */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ktype == DCRYPT_KEY_RSA) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, key, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if (ktype == DCRYPT_KEY_EC) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* perform ECDHE */
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *temp_key = t_buffer_create(256);
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *secret = t_buffer_create(256);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_t peer_key;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_create_from_const_data(&peer_key, ephemeral_key, ep_key_len);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &peer_key, secret, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: corrupted header");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* use shared secret and peer key to generate decryption key, AES-256-CBC has 32 byte key and 16 byte IV */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_pbkdf2(secret->data, secret->used, peer_key.data, peer_key.used,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi malg, rounds, temp_key, 32+16, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (temp_key->used != 32+16) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid temporary key");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct dcrypt_context_symmetric *dctx;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, &dctx, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *ptr = temp_key->data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* we use ephemeral_key for IV */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_key(dctx, ptr, 32);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ec = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_init(dctx, &error) ||
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, key, &error) ||
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi !dcrypt_ctx_sym_final(dctx, key, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ec = -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (key->used != key_len) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid key length");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ec = -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_destroy(&dctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ec != 0) return ec;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported key type 0x%02x", ktype);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* make sure we were able to decrypt the encrypted key correctly */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (hash == NULL) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported hash algorithm: %s", malg);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char hctx[hash->context_size];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char hres[hash->digest_size];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->init(hctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->loop(hctx, key->data, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->result(hctx, hres);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi for(int i = 1; i < 2049; i++) {
32340fe8f461f6ae56c4cb3ee8392ba14c9f539aAki Tuomi uint32_t i_msb = cpu32_to_be(i);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->init(hctx);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->loop(hctx, hres, sizeof(hres));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->loop(hctx, &i_msb, sizeof(i_msb));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi hash->result(hctx, hres);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* do the comparison */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: corrupted header ekhash");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomiint i_stream_decrypt_header_contents(struct decrypt_istream *stream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data, size_t size)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *end = data + size;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool failed = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read cipher OID */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *calg;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!i_stream_decrypt_der(&data, end, &calg))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek if (calg == NULL || !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, &stream->ctx_sym, NULL)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid cipher: %s", calg);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *malg;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!i_stream_decrypt_der(&data, end, &malg))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &stream->ctx_mac, NULL)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid MAC algorithm: %s", malg);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read rounds (for PBKDF2) */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t rounds;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &rounds))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read key data length */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t kdlen;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &kdlen))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t tagsize;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi tagsize = IOSTREAM_TAG_SIZE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi tagsize = IOSTREAM_TAG_SIZE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi tagsize = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* how much key data we should be getting */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize;
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer_t *keydata = t_buffer_create(kl);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* try to decrypt the keydata with a private key */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ret;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, end, keydata, kl)) <= 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return ret;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* oh, it worked! */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *ptr = keydata->data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (keydata->used != kl) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* but returned wrong amount of data */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Key decryption error: Key data length mismatch");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* prime contexts */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* based on the chosen MAC, initialize HMAC or AEAD */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "MAC error: %s", error);
1ec1404c0133d1d26a0477a50189de54fcf79725Timo Sirainen stream->istream.istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi failed = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->use_mac = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->ftr = tagsize;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->use_mac = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->use_mac = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* destroy private key data */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_set_used_size(keydata, 0);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return failed ? -1 : 1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomissize_t i_stream_decrypt_read_header(struct decrypt_istream *stream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data, size_t mlen)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *end = data + mlen;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* check magic */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (mlen < sizeof(IOSTREAM_CRYPT_MAGIC))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(data, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) {
0835df7f2caae9d276551eeb66eebc5f5d6cc6edTimo Sirainen io_stream_set_error(&stream->istream.iostream, "Stream is not encrypted (invalid magic)");
1ec1404c0133d1d26a0477a50189de54fcf79725Timo Sirainen stream->istream.istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data += sizeof(IOSTREAM_CRYPT_MAGIC);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (data >= end)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0; /* read more? */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* check version */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (*data == '\x01') {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->format = DECRYPT_FORMAT_V1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return i_stream_decrypt_read_header_v1(stream, data+1, end - (data+1));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if (*data != '\x02') {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Unsupported encrypted data 0x%02x", *data);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->format = DECRYPT_FORMAT_V2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data++;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* read flags */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t flags;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &flags))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->flags = flags;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* get the total length of header */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi uint32_t hdr_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!get_msb32(&data, end, &hdr_len))
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi /* do not forget stream format */
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi if ((size_t)(end-data)+1 < hdr_len)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ret;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((ret = i_stream_decrypt_header_contents(stream, data, hdr_len)) < 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi else if (ret == 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption error: truncate header length");
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen stream->istream.istream.stream_errno = EPIPE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->initialized = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* if it all went well, try to initialize decryption context */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->istream.iostream, "Decryption init error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return hdr_len;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic ssize_t
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomii_stream_decrypt_read(struct istream_private *stream)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream =
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi (struct decrypt_istream *)stream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const unsigned char *data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t size, decrypt_size;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error = NULL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ret;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool check_mac = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* not if it's broken */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->istream.stream_errno != 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi for (;;) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* remove skipped data from buffer */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->skip > 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_assert(stream->skip <= dstream->buf->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_delete(dstream->buf, 0, stream->skip);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->pos -= stream->skip;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->skip = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->buffer = dstream->buf->data;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_assert(stream->pos <= dstream->buf->used);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->pos >= dstream->istream.max_buffer_size) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* stream buffer still at maximum */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -2;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* if something is already decrypted, return as much of it as
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi we can */
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi if (dstream->initialized && dstream->buf->used > 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size_t new_pos, bytes;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* only return up to max_buffer_size bytes, even when buffer
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi actually has more, as not to confuse the caller */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->buf->used <= dstream->istream.max_buffer_size) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi new_pos = dstream->buf->used;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->finalized)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.eof = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi new_pos = dstream->istream.max_buffer_size;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bytes = new_pos - stream->pos;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->pos = new_pos;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return (ssize_t)bytes;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->finalized) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* all data decrypted */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.eof = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
4b4e8dcad11120fa006e8edf1c659271ed5bd800Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* need to read more input */
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen ret = i_stream_read_memarea(stream->parent);
4b4e8dcad11120fa006e8edf1c659271ed5bd800Aki Tuomi if (ret == 0)
b99357fa1952863bbea761ff7ff3b8ca0f07702fTimo Sirainen return ret;
4b4e8dcad11120fa006e8edf1c659271ed5bd800Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data = i_stream_get_data(stream->parent, &size);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.stream_errno = stream->parent->stream_errno;
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi /* file was empty */
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi if (!dstream->initialized && size == 0 && stream->parent->eof) {
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi stream->istream.eof = TRUE;
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi return -1;
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi }
7a71f2326280fb300f7c45d1ba5b30af3db37f2cAki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->istream.stream_errno != 0)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dstream->initialized) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Decryption error: %s",
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Input truncated in decryption header");
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen stream->istream.stream_errno = EPIPE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* final block */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dcrypt_ctx_sym_final(dstream->ctx_sym,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->buf, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->finalized = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi continue;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "MAC error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dstream->initialized) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi ssize_t hret;
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi
b99357fa1952863bbea761ff7ff3b8ca0f07702fTimo Sirainen if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) {
43ca8bce8a4c4c7acd970b56b567c0edeee91029Aki Tuomi if (hret < 0) {
43ca8bce8a4c4c7acd970b56b567c0edeee91029Aki Tuomi if (stream->istream.stream_errno == 0)
43ca8bce8a4c4c7acd970b56b567c0edeee91029Aki Tuomi /* assume temporary failure */
43ca8bce8a4c4c7acd970b56b567c0edeee91029Aki Tuomi stream->istream.stream_errno = EIO;
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi if (hret == 0 && stream->parent->eof) {
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi /* not encrypted by us */
aefadb693342d9a2cba15e252f0368e47e9a59ebTimo Sirainen stream->istream.stream_errno = EPIPE;
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi io_stream_set_error(&stream->iostream,
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi "Truncated header");
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi return -1;
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi }
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi }
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi if (hret == 0) {
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi /* see if we can get more data */
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen if (ret == -2) {
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen stream->istream.stream_errno = EINVAL;
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen io_stream_set_error(&stream->iostream,
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen "Header too large (more than %"PRIuSIZE_T" bytes)", size);
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen return -1;
8a2f21f2b03878918333efac486f99e4fa0ecce2Timo Sirainen }
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi continue;
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi } else {
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi /* clean up buffer */
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used);
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi buffer_set_used_size(dstream->buf, 0);
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi i_stream_skip(stream->parent, hret);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data = i_stream_get_data(stream->parent, &size);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi decrypt_size = size;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->use_mac) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (stream->parent->eof) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (decrypt_size < dstream->ftr) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Decryption error: footer is longer than data");
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi check_mac = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi /* ignore footer's length of data until we
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi reach EOF */
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi size -= dstream->ftr;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi decrypt_size -= dstream->ftr;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_hmac_update(dstream->ctx_mac,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data, decrypt_size, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "MAC error: %s", error);
b4fd67490cbf9e3fccde972abde3675a4e2b8c9aTimo Sirainen stream->istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (check_mac) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)];
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_t db;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi buffer_create_from_data(&db, dgst, sizeof(dgst));
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Cannot verify MAC: %s", error);
b4fd67490cbf9e3fccde972abde3675a4e2b8c9aTimo Sirainen stream->istream.stream_errno = EINVAL;
b4fd67490cbf9e3fccde972abde3675a4e2b8c9aTimo Sirainen return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Cannot verify MAC: mismatch");
b4fd67490cbf9e3fccde972abde3675a4e2b8c9aTimo Sirainen stream->istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_update(dstream->ctx_sym,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi data, decrypt_size, dstream->buf, &error)) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&stream->iostream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi "Decryption error: %s", error);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi stream->istream.stream_errno = EINVAL;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_stream_skip(stream->parent, size);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi }
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomivoid i_stream_decrypt_close(struct iostream_private *stream,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi bool close_parent)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream =
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi (struct decrypt_istream *)stream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (close_parent)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_stream_close(dstream->istream.parent);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomivoid i_stream_decrypt_destroy(struct iostream_private *stream)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream =
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi (struct decrypt_istream *)stream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
6307d76096764e66bddc63d4a3e5a1aa19cc528fJosef 'Jeff' Sipek buffer_free(&dstream->buf);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->iv != NULL)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_free_and_null(dstream->iv);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->ctx_sym != NULL)
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek dcrypt_ctx_sym_destroy(&dstream->ctx_sym);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (dstream->ctx_mac != NULL)
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek dcrypt_ctx_hmac_destroy(&dstream->ctx_mac);
55a7fe1e4637a8dbc6184b54edeb74ac8276b2c1Aki Tuomi if (dstream->priv_key != NULL)
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek dcrypt_key_unref_private(&dstream->priv_key);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
c5e46dba179864f6f1adf196d46e7a0371b11914Josef 'Jeff' Sipek i_stream_unref(&dstream->istream.parent);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistatic
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct decrypt_istream *i_stream_create_decrypt_common(struct istream *input)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream = i_new(struct decrypt_istream, 1);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.read = i_stream_decrypt_read;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.iostream.close = i_stream_decrypt_close;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.iostream.destroy = i_stream_decrypt_destroy;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.istream.readable_fd = FALSE;
4930d4fef187428f74827d7e47331beb5cc4e39dTimo Sirainen dstream->istream.istream.blocking = input->blocking;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->istream.istream.seekable = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
dd49f993330ae7cd357ddb01747ea7a8db62d90bAki Tuomi dstream->buf = buffer_create_dynamic(default_pool, 512);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi (void)i_stream_create(&dstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return dstream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct istream *
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomii_stream_create_decrypt(struct istream *input, struct dcrypt_private_key *priv_key)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream = i_stream_create_decrypt_common(input);
55a7fe1e4637a8dbc6184b54edeb74ac8276b2c1Aki Tuomi dcrypt_key_ref_private(priv_key);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->priv_key = priv_key;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return &dstream->istream.istream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct istream *
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomii_stream_create_sym_decrypt(struct istream *input, struct dcrypt_context_symmetric *ctx)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi const char *error;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi int ec;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream = i_stream_create_decrypt_common(input);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->use_mac = FALSE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->initialized = TRUE;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi else ec = 0;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->ctx_sym = ctx;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi if (ec != 0) {
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi io_stream_set_error(&dstream->istream.iostream, "Cannot initialize decryption: %s", error);
1ec1404c0133d1d26a0477a50189de54fcf79725Timo Sirainen dstream->istream.istream.stream_errno = EIO;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi };
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return &dstream->istream.istream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomistruct istream *
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomii_stream_create_decrypt_callback(struct istream *input,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_stream_decrypt_get_key_callback_t *callback,
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi void *context)
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi{
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi struct decrypt_istream *dstream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi i_assert(callback != NULL);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream = i_stream_create_decrypt_common(input);
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->key_callback = callback;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi dstream->key_context = context;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi return &dstream->istream.istream;
316cbe323513a0f20d1cf519fe9405e231d633e2Aki Tuomi}