Example #1
0
void array_sort_i(struct array *array, int (*cmp)(const void *, const void *))
{
	unsigned int count;

	count = array->buffer->used / array->element_size;
	qsort(buffer_get_modifiable_data(array->buffer, NULL),
	      count, array->element_size, cmp);
}
Example #2
0
static void replace_nul_bytes(buffer_t *buf)
{
	unsigned char *data;
	size_t i, size;

	data = buffer_get_modifiable_data(buf, &size);
	for (i = 0; i < size; i++) {
		if (data[i] == '\0')
			data[i] = ' ';
	}
}
Example #3
0
static void pack_whitespace(buffer_t *buf)
{
	char *data, *dest;
	bool last_lwsp;

	data = buffer_get_modifiable_data(buf, NULL);

	/* check if we need to do anything */
	while (*data != '\0') {
		if (*data == '\t' || *data == '\n' || *data == '\r' ||
		    (*data == ' ' && (data[1] == ' ' || data[1] == '\t')))
			break;
		data++;
	}

	if (*data == '\0')
		return;

	/* @UNSAFE: convert/pack the whitespace */
	dest = data; last_lwsp = FALSE;
	while (*data != '\0') {
		if (*data == '\t' || *data == ' ' ||
		    *data == '\r' || *data == '\n') {
			if (!last_lwsp) {
				*dest++ = ' ';
				last_lwsp = TRUE;
			}
		} else {
			*dest++ = *data;
			last_lwsp = FALSE;
		}
		data++;
	}
	*dest = '\0';

	data = buffer_get_modifiable_data(buf, NULL);
	buffer_set_used_size(buf, (size_t) (dest - data)+1);
}
static void status_flags_replace(struct mbox_sync_mail_context *ctx, size_t pos,
				 const struct mbox_flag_type *flags_list)
{
	unsigned char *data;
	size_t size;
	int i, need, have;

	ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE;

	if (ctx->header_first_change > pos)
		ctx->header_first_change = pos;

	/* how many bytes do we need? */
	for (i = 0, need = 0; flags_list[i].chr != 0; i++) {
		if ((ctx->mail.flags & flags_list[i].flag) != 0)
			need++;
	}

	/* how many bytes do we have now? */
	data = buffer_get_modifiable_data(ctx->header, &size);
	for (have = 0; pos < size; pos++) {
		if (data[pos] == '\n' || data[pos] == '\r')
			break;

		/* see if this is unknown flag for us */
		for (i = 0; flags_list[i].chr != 0; i++) {
			if (flags_list[i].chr == (char)data[pos])
				break;
		}

		if (flags_list[i].chr != 0)
			have++;
		else {
			/* save this one */
			data[pos-have] = data[pos];
		}
	}
	pos -= have;
        mbox_sync_move_buffer(ctx, pos, need, have);

	/* @UNSAFE */
	data = buffer_get_space_unsafe(ctx->header, pos, need);
	for (i = 0; flags_list[i].chr != 0; i++) {
		if ((ctx->mail.flags & flags_list[i].flag) != 0)
			*data++ = flags_list[i].chr;
	}

	ctx->mail.flags ^= MBOX_NONRECENT_KLUDGE;
}
Example #5
0
void array_reverse_i(struct array *array)
{
	const unsigned int element_size = array->element_size;
	unsigned int i, count = array_count_i(array);
	size_t size;
	void *data, *tmp;

	data = buffer_get_modifiable_data(array->buffer, &size);
	tmp = t_buffer_get(array->element_size);
	for (i = 0; i+1 < count; i++, count--) {
		memcpy(tmp, PTR_OFFSET(data, i * element_size), element_size);
		memcpy(PTR_OFFSET(data, i * element_size),
		       PTR_OFFSET(data, (count-1) * element_size),
		       element_size);
		memcpy(PTR_OFFSET(data, (count-1) * element_size), tmp,
		       element_size);
	}
}
Example #6
0
char *str_c_modifiable(string_t *str)
{
	str_add_nul(str);
	return buffer_get_modifiable_data(str, NULL);
}
Example #7
0
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);
	}
}
Example #8
0
static
ssize_t i_stream_decrypt_read_header_v1(struct decrypt_istream *stream,
	const unsigned char *data, size_t mlen)
{
	const char *error = NULL;
	size_t keydata_len = 0;
	uint16_t len;
	int ec, i = 0;

	const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, *key_ct_pos = NULL;

	size_t pos = sizeof(IOSTREAM_CRYPT_MAGIC);
	size_t digest_len = 0;
	size_t key_ct_len = 0;
	size_t key_digest_size = 0;

	buffer_t ephemeral_key;
	buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256);
	buffer_t *key = buffer_create_dynamic(pool_datastack_create(), 256);

	if (mlen < 2)
		return 0;
	keydata_len = (data[0] << 8) | data[1];
	if (mlen-2 < keydata_len) {
		/* try to read more */
		return 0;
	}

	data+=2;
	mlen-=2;

	while (i < 4 && mlen > 2) {
		memcpy(&len, data, 2);
		len = ntohs(len);
		data += 2;
		mlen -= 2;
		pos += 2;
		if (len == 0 || len > mlen)
			break;

		switch(i++) {
		case 0:
			buffer_create_from_const_data(&ephemeral_key, data, len);
			break;
		case 1:
			/* public key id */
			digest_pos = data;
			digest_len = len;
			break;
		case 2:
			/* encryption key digest */
			key_digest_pos = data;
			key_digest_size = len;
			break;
		case 3:
			/* encrypted key data */
			key_ct_pos = data;
			key_ct_len = len;
			break;
		}
		pos += len;
		data += len;
		mlen -= len;
	}

	if (i < 4) {
		io_stream_set_error(&stream->istream.iostream, "Invalid or corrupted header");
		stream->istream.istream.stream_errno = EINVAL;
		return -1;
	}

	/* we don't have a private key */
	if (stream->priv_key == NULL) {
		/* see if we can get one */
		if (stream->key_callback != NULL) {
			const char *key_id = binary_to_hex(digest_pos, digest_len);
			int ret = stream->key_callback(key_id, &(stream->priv_key), &error, stream->key_context);
			if (ret < 0) {
				io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error);
				return -1;
			}
			if (ret == 0) {
				io_stream_set_error(&stream->istream.iostream, "Private key not available");
				return -1;
			}
			dcrypt_key_ref_private(stream->priv_key);
		} else {
			io_stream_set_error(&stream->istream.iostream, "Private key not available");
			return -1;
		}
	}

	buffer_t *check = buffer_create_dynamic(pool_datastack_create(), 32);

	if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) {
		io_stream_set_error(&stream->istream.iostream, "Cannot get public key hash: %s", error);
		return -1;
	} else {
		if (memcmp(digest_pos, check->data, I_MIN(digest_len,check->used)) != 0) {
			io_stream_set_error(&stream->istream.iostream, "Private key not available");
			return -1;
		}
	}

	/* derive shared secret */
	if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &ephemeral_key, secret, &error)) {
		io_stream_set_error(&stream->istream.iostream, "Cannot perform ECDH: %s", error);
		return -1;
	}

	/* run it thru SHA256 once */
	const struct hash_method *hash = &hash_method_sha256;
	unsigned char hctx[hash->context_size];
	unsigned char hres[hash->digest_size];
	hash->init(hctx);
	hash->loop(hctx, secret->data, secret->used);
	hash->result(hctx, hres);
	safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);

	/* NB! The old code was broken and used this kind of IV - it is not correct, but
	   we need to stay compatible with old data */

	/* use it to decrypt the actual encryption key */
	struct dcrypt_context_symmetric *dctx;
	if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &dctx, &error)) {
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
		return -1;
	}

	ec = 0;
	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);
	dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size);
	if (!dcrypt_ctx_sym_init(dctx, &error) ||
	    !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) ||
	    !dcrypt_ctx_sym_final(dctx, key, &error)) {
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
		ec = -1;
	}
	dcrypt_ctx_sym_destroy(&dctx);

	if (ec != 0) {
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
		return -1;
	}

	/* see if we got the correct key */
	hash->init(hctx);
	hash->loop(hctx, key->data, key->used);
	hash->result(hctx, hres);

	if (key_digest_size != sizeof(hres)) {
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: invalid digest length");
		return -1;
	}
	if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) {
		io_stream_set_error(&stream->istream.iostream, "Key decryption error: decrypted key is invalid");
		return -1;
	}

	/* prime context with key */
	if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, &(stream->ctx_sym), &error)) {
		io_stream_set_error(&stream->istream.iostream, "Decryption context create error: %s", error);
		return -1;
	}

	/* Again, old code used this IV, so we have to use it too */
	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);
	dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used);

	safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);

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

	stream->use_mac = FALSE;
	stream->initialized = TRUE;
	/* now we are ready to decrypt stream */

	return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len;
}
Example #9
0
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;
}
Example #10
0
static
ssize_t i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, unsigned int rounds,
	const unsigned char *data, const unsigned char *end, buffer_t *key, size_t key_len)
{
	const char *error;
	enum dcrypt_key_type ktype;
	int keys;
	bool have_key = FALSE;
	unsigned char dgst[32];
	uint32_t val;
	buffer_t buf;

	if (data == end)
		return 0;

	keys = *data++;

	/* if we have a key, prefab the digest */
	if (stream->key_callback == NULL) {
		if (stream->priv_key == NULL) {	
			io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available");
			return -1;
		}
		buffer_create_from_data(&buf, dgst, sizeof(dgst));
		dcrypt_key_id_private(stream->priv_key, "sha256", &buf, NULL);
	}

	/* for each key */
	for(;keys>0;keys--) {
		if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst))
			return 0;
		ktype = *data++;

		if (stream->key_callback != NULL) {
			const char *hexdgst = binary_to_hex(data, sizeof(dgst)); /* digest length */
			/* hope you going to give us right key.. */
			int ret = stream->key_callback(hexdgst, &(stream->priv_key), &error, stream->key_context);
			if (ret < 0) {
				io_stream_set_error(&stream->istream.iostream, "Private key not available: %s", error);
				return -1;
			}
			if (ret > 0) {
				dcrypt_key_ref_private(stream->priv_key);
				have_key = TRUE;
				break;
			}
		} else {
			/* see if key matches to the one we have */
			if (memcmp(dgst, data, sizeof(dgst)) == 0) {
			      	have_key = TRUE;
				break;
			}
		}
		data += sizeof(dgst);

		/* wasn't correct key, skip over some data */
		if (!get_msb32(&data, end, &val) ||
		    !get_msb32(&data, end, &val))
			return 0;
	}

	/* didn't find matching key */
	if (!have_key) {
		io_stream_set_error(&stream->istream.iostream, "Decryption error: no private key available");
		return -1;
	}

	data += sizeof(dgst);

	const unsigned char *ephemeral_key;
	uint32_t ep_key_len;
	const unsigned char *encrypted_key;
	uint32_t eklen;
	const unsigned char *ekhash;
	uint32_t ekhash_len;

	/* read ephemeral key (can be missing for RSA) */
	if (!get_msb32(&data, end, &ep_key_len) || (size_t)(end-data) < ep_key_len)
		return 0;
	ephemeral_key = data;
	data += ep_key_len;

	/* read encrypted key */
	if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen)
		return 0;
	encrypted_key = data;
	data += eklen;

	/* read key data hash */
	if (!get_msb32(&data, end, &ekhash_len) || (size_t)(end-data) < ekhash_len)
		return 0;
	ekhash = data;
	data += ekhash_len;

	/* decrypt the seed */
	if (ktype == DCRYPT_KEY_RSA) {
		if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, key, &error)) {
			io_stream_set_error(&stream->istream.iostream, "key decryption error: %s", error);
			return -1;
		}
	} else if (ktype == DCRYPT_KEY_EC) {
		/* perform ECDHE */
		buffer_t *temp_key = buffer_create_dynamic(pool_datastack_create(), 256);
		buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256);
		buffer_t peer_key;
		buffer_create_from_const_data(&peer_key, ephemeral_key, ep_key_len);
		if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, &peer_key, secret, &error)) {
			io_stream_set_error(&stream->istream.iostream, "Key decryption error: corrupted header");
			return -1;
		}

		/* use shared secret and peer key to generate decryption key, AES-256-CBC has 32 byte key and 16 byte IV */
		if (!dcrypt_pbkdf2(secret->data, secret->used, peer_key.data, peer_key.used,
		    malg, rounds, temp_key, 32+16, &error)) {
			safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
			io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
			return -1;
		}

		safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
		if (temp_key->used != 32+16) {
			safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
			io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid temporary key");
			return -1;
		}
		struct dcrypt_context_symmetric *dctx;
		if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, &dctx, &error)) {
			safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
			io_stream_set_error(&stream->istream.iostream, "Key decryption error: %s", error);
			return -1;
		}
		const unsigned char *ptr = temp_key->data;

		/* we use ephemeral_key for IV */
		dcrypt_ctx_sym_set_key(dctx, ptr, 32);
		dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16);
		safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);

		int ec = 0;
		if (!dcrypt_ctx_sym_init(dctx, &error) ||
		    !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, key, &error) ||
		    !dcrypt_ctx_sym_final(dctx, key, &error)) {
			io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: %s", error);
			ec = -1;
		}

		if (key->used != key_len) {
			io_stream_set_error(&stream->istream.iostream, "Cannot perform key decryption: invalid key length");
			ec = -1;
		}

		dcrypt_ctx_sym_destroy(&dctx);
		if (ec != 0) return ec;
	} else {
		io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported key type 0x%02x", ktype);
		return -1;
	}

	/* make sure we were able to decrypt the encrypted key correctly */
	const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg));
	if (hash == NULL) {
		safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);
		io_stream_set_error(&stream->istream.iostream, "Decryption error: unsupported hash algorithm: %s", malg);
		return -1;
	}
	unsigned char hctx[hash->context_size];
	unsigned char hres[hash->digest_size];
	hash->init(hctx);
	hash->loop(hctx, key->data, key->used);
	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);
	}

	/* do the comparison */
	if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) {
		safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used);
		io_stream_set_error(&stream->istream.iostream, "Decryption error: corrupted header ekhash");
		return -1;
	}
	return 1;
}
Example #11
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;
}
Example #12
0
static
int o_stream_encrypt_key_for_pubkey_v2(struct encrypt_ostream *stream, const char *malg,
	const unsigned char *key, size_t key_len, struct dcrypt_public_key *pubkey, buffer_t *res)
{
	enum dcrypt_key_type ktype;
	const char *error;
	buffer_t *encrypted_key, *ephemeral_key, *temp_key;

	ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256);
	encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256);
	temp_key = buffer_create_dynamic(pool_datastack_create(), 48);

	ktype = dcrypt_key_type_public(pubkey);

	if (ktype == DCRYPT_KEY_RSA) {
		/* encrypt key as R (as we don't need DH with RSA)*/
		if (!dcrypt_rsa_encrypt(pubkey, key, key_len, encrypted_key, &error)) {
			io_stream_set_error(&stream->ostream.iostream, "Cannot encrypt key data: %s", error);
			return -1;
		}
	} else if (ktype == DCRYPT_KEY_EC) {
		/* R = our ephemeral public key */
		buffer_t *secret = buffer_create_dynamic(pool_datastack_create(), 256);

		/* derive ephemeral key and shared secret */
		if (!dcrypt_ecdh_derive_secret_peer(pubkey, ephemeral_key, secret, &error)) {
			io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error);
			return -1;
		}

		/* use shared secret and ephemeral key to generate encryption key/iv */
		if (!dcrypt_pbkdf2(secret->data, secret->used, ephemeral_key->data, ephemeral_key->used,
		    malg, IO_STREAM_ENCRYPT_ROUNDS, temp_key, 48, &error)) {
			safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);
			io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error);
		}
		safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);

		/* encrypt key with shared secret */
		struct dcrypt_context_symmetric *dctx;
		if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_ENCRYPT, &dctx, &error)) {
			safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);
			io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error);
			return -1;
		}

		const unsigned char *ptr = temp_key->data;
		i_assert(temp_key->used == 48);

		dcrypt_ctx_sym_set_key(dctx, ptr, 32);
		dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16);
		safe_memset(buffer_get_modifiable_data(temp_key, 0), 0, temp_key->used);

		int ec = 0;
		if (!dcrypt_ctx_sym_init(dctx, &error) ||
		    !dcrypt_ctx_sym_update(dctx, key, key_len, encrypted_key, &error) ||
		    !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) {
			io_stream_set_error(&stream->ostream.iostream, "Cannot perform key encryption: %s", error);
			ec = -1;
		}

		dcrypt_ctx_sym_destroy(&dctx);
		if (ec != 0) return ec;
	} else {
		io_stream_set_error(&stream->ostream.iostream, "Unsupported key type");
		return -1;
	}

	/* store key type */
	char kt = ktype;
	buffer_append(res, &kt, 1);
	/* store hash of public key as ID */
	dcrypt_key_id_public(stream->pub, "sha256", res, NULL);
	/* store ephemeral key (if present) */
	unsigned int val = htonl(ephemeral_key->used);
	buffer_append(res, &val, 4);
	buffer_append_buf(res, ephemeral_key, 0, (size_t)-1);
	/* store encrypted key */
	val = htonl(encrypted_key->used);
	buffer_append(res, &val, 4);
	buffer_append_buf(res, encrypted_key, 0, (size_t)-1);

	return 0;
}
Example #13
0
static
int o_stream_encrypt_keydata_create_v1(struct encrypt_ostream *stream)
{
	buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf;
	const char *error = NULL;
	const struct hash_method *hash = &hash_method_sha256;

	/* various temporary buffers */
	unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE];
	unsigned char pkhash[hash->digest_size];
	unsigned char ekhash[hash->digest_size];
	unsigned char hres[hash->digest_size];

	unsigned char hctx[hash->context_size];

	/* hash the public key first */
	buffer_create_from_data(&buf, pkhash, sizeof(pkhash));
	if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Key hash failed: %s", error);
		return -1;
	}

	/* hash the key base */
	hash->init(hctx);
	hash->loop(hctx, seed, sizeof(seed));
	hash->result(hctx, ekhash);

	ephemeral_key = buffer_create_dynamic(pool_datastack_create(), 256);
	encrypted_key = buffer_create_dynamic(pool_datastack_create(), 256);
	secret = buffer_create_dynamic(pool_datastack_create(), 256);

	if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, secret, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Cannot perform ECDH: %s", error);
		return -1;
	}

	/* hash the secret data */
	hash->init(hctx);
	hash->loop(hctx, secret->data, secret->used);
	hash->result(hctx, hres);
	safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used);

	/* use it to encrypt the actual encryption key */
	struct dcrypt_context_symmetric *dctx;
	if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, &dctx, &error)) {
		io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error);
		return -1;
	}

	random_fill(seed, sizeof(seed));
	hash->init(hctx);
	hash->loop(hctx, seed, sizeof(seed));
	hash->result(hctx, ekhash);

	int ec = 0;

	/* NB! The old code was broken and used this kind of IV - it is not correct, but
	   we need to stay compatible with old data */
	dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
	dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres));

	if (!dcrypt_ctx_sym_init(dctx, &error) ||
	    !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), encrypted_key, &error) ||
	    !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) {
		ec = -1;
	}
	dcrypt_ctx_sym_destroy(&dctx);

	if (ec != 0) {
		safe_memset(seed, 0, sizeof(seed));
		io_stream_set_error(&stream->ostream.iostream, "Key encryption error: %s", error);
		return -1;
	}

	/* same as above */
	dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
	dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed));
	safe_memset(seed, 0, sizeof(seed));

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

	res = buffer_create_dynamic(default_pool, 256);

	/* ephemeral key */
	unsigned short s;
	s = htons(ephemeral_key->used);
	buffer_append(res, &s, 2);
	buffer_append(res, ephemeral_key->data, ephemeral_key->used);
	/* public key hash */
	s = htons(sizeof(pkhash));
	buffer_append(res, &s, 2);
	buffer_append(res, pkhash, sizeof(pkhash));
	/* encrypted key hash */
	s = htons(sizeof(ekhash));
	buffer_append(res, &s, 2);
	buffer_append(res, ekhash, sizeof(ekhash));
	/* encrypted key */
	s = htons(encrypted_key->used);
	buffer_append(res, &s, 2);
	buffer_append(res, encrypted_key->data, encrypted_key->used);

	stream->key_data_len = res->used;
	stream->key_data = buffer_free_without_data(&res);

	return 0;
}