static
int i_stream_decrypt_header_contents(struct decrypt_istream *stream,
				     const unsigned char *data, size_t size)
{
	const unsigned char *end = data + size;
	bool failed = FALSE;

	/* read cipher OID */
	const char *calg;
	if (!i_stream_decrypt_der(&data, end, &calg))
		return 0;
	if (calg == NULL || !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), NULL)) {
		io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid cipher: %s", calg);
		return -1;
	}

	/* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */
	const char *malg;
	if (!i_stream_decrypt_der(&data, end, &malg))
		return 0;
	if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &(stream->ctx_mac), NULL)) {
		io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported/invalid MAC algorithm: %s", malg);
		return -1;
	}

	/* read rounds (for PBKDF2) */
	uint32_t rounds;
	if (!get_msb32(&data, end, &rounds))
		return 0;
	/* read key data length */
	uint32_t kdlen;
	if (!get_msb32(&data, end, &kdlen))
		return 0;

	size_t tagsize;

	if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
		tagsize = IOSTREAM_TAG_SIZE;
	} else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
		tagsize = IOSTREAM_TAG_SIZE;
	} else {
		tagsize = 0;
	}

	/* how much key data we should be getting */
	size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize;
	buffer_t *keydata = buffer_create_dynamic(pool_datastack_create(), kl);

	/* try to decrypt the keydata with a private key */
	int ret;
	if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, end, keydata, kl)) <= 0)
		return ret;

	/* oh, it worked! */
	const unsigned char *ptr = keydata->data;
	if (keydata->used != kl) {
		/* but returned wrong amount of data */
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: Key data length mismatch");
		return -1;
	}

	/* prime contexts */
	dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym));
	ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym);
	dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
	stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
	memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
	ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym);

	/* based on the chosen MAC, initialize HMAC or AEAD */
	if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
		const char *error;
		dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize);
		if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) {
			io_stream_set_error(&stream->istream.iostream, "MAC error: %s", error);
			stream->istream.istream.stream_errno = EINVAL;
			failed = TRUE;
		}
		stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac);
		stream->use_mac = TRUE;
	} else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
		dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize);
		stream->ftr = tagsize;
		stream->use_mac = TRUE;
	} else {
		stream->use_mac = FALSE;
	}
	/* destroy private key data */
	safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used);
	buffer_set_used_size(keydata, 0);
	return failed ? -1 : 1;
}
Exemple #2
0
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();
}
Exemple #3
0
static
int o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, const char *malg)
{
	const struct hash_method *hash = hash_method_lookup(malg);
	const char *error;
	size_t tagsize;
	const unsigned char *ptr;
	size_t kl;
	unsigned int val;

	buffer_t *keydata, *res;

	if (hash == NULL) {
		io_stream_set_error(&stream->ostream.iostream,
			"Encryption init error: Hash algorithm '%s' not supported", malg);
		return -1;
	}

	/* key data length for internal use */
	if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
		tagsize = IOSTREAM_TAG_SIZE; 
	} else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
		tagsize = IOSTREAM_TAG_SIZE;
	} else {
		/* do not include MAC */
		tagsize = 0;
	}

	/* generate keydata length of random data for key/iv/mac */
	kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize;
	keydata = buffer_create_dynamic(pool_datastack_create(), kl);
	random_fill(buffer_append_space_unsafe(keydata, kl), kl);
	buffer_set_used_size(keydata, kl);
	ptr = keydata->data;

	res = buffer_create_dynamic(default_pool, 256);

	/* store number of public key(s) */
	buffer_append(res, "\1", 1); /* one key for now */

	/* we can do multiple keys at this point, but do it only once now */
	if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, stream->pub, res) != 0) {
		buffer_free(&res);
		return -1;
	}

	/* create hash of the key data */
	unsigned char hctx[hash->context_size];
	unsigned char hres[hash->digest_size];
	hash->init(hctx);
	hash->loop(hctx, ptr, kl);
	hash->result(hctx, hres);

	for(int i = 1; i < 2049; i++) {
		uint32_t i_msb = htonl(i);

		hash->init(hctx);
		hash->loop(hctx, hres, sizeof(hres));
		hash->loop(hctx, &i_msb, sizeof(i_msb));
		hash->result(hctx, hres);
	}

	/* store key data hash */
	val = htonl(sizeof(hres));
	buffer_append(res, &val, 4);
	buffer_append(res, hres, sizeof(hres));

	/* pick up key data that goes into stream */
	stream->key_data_len = res->used;
	stream->key_data = buffer_free_without_data(&res);

	/* prime contexts */
	dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym));
	ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym);
	dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym));
	ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym);

	if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
		dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize);
		dcrypt_ctx_hmac_init(stream->ctx_mac, &error);
	} else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
		dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize);
	}

	/* clear out private key data */
	safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used);

	if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error);
		return -1;
	}
	return 0;
}