diff options
| author | Roman Smrž <roman.smrz@seznam.cz> | 2023-08-25 21:36:59 +0200 | 
|---|---|---|
| committer | Roman Smrž <roman.smrz@seznam.cz> | 2023-08-27 13:45:37 +0200 | 
| commit | c44b82faaf309c916a1aecf4ec939510e6384ae5 (patch) | |
| tree | 5eaf5754848c42ce9a8d23eff6c166da3032b587 | |
| parent | 1d4fa8fafa707642f948da9b033a21d0bcde0bbf (diff) | |
Switch to ChaCha20-Poly1305 AEAD scheme
| -rw-r--r-- | src/network/channel.cpp | 74 | ||||
| -rw-r--r-- | src/network/channel.h | 11 | 
2 files changed, 49 insertions, 36 deletions
| diff --git a/src/network/channel.cpp b/src/network/channel.cpp index b95e0a1..5fff1fa 100644 --- a/src/network/channel.cpp +++ b/src/network/channel.cpp @@ -17,7 +17,6 @@ Ref ChannelRequestData::store(const Storage & st) const  	for (const auto & p : peers)  		items.emplace_back("peer", p); -	items.emplace_back("enc", "aes-128-gcm");  	items.emplace_back("key", key);  	return st.storeObject(Record(std::move(items))); @@ -26,12 +25,11 @@ Ref ChannelRequestData::store(const Storage & st) const  ChannelRequestData ChannelRequestData::load(const Ref & ref)  {  	if (auto rec = ref->asRecord()) { -		if (rec->item("enc").asText() == "aes-128-gcm") -			if (auto key = rec->item("key").as<PublicKexKey>()) -				return ChannelRequestData { -					.peers = rec->items("peer").as<Signed<IdentityData>>(), -					.key = *key, -				}; +		if (auto key = rec->item("key").as<PublicKexKey>()) +			return ChannelRequestData { +				.peers = rec->items("peer").as<Signed<IdentityData>>(), +				.key = *key, +			};  	}  	return ChannelRequestData { @@ -45,7 +43,6 @@ Ref ChannelAcceptData::store(const Storage & st) const  	vector<Record::Item> items;  	items.emplace_back("req", request); -	items.emplace_back("enc", "aes-128-gcm");  	items.emplace_back("key", key);  	return st.storeObject(Record(std::move(items))); @@ -54,11 +51,10 @@ Ref ChannelAcceptData::store(const Storage & st) const  ChannelAcceptData ChannelAcceptData::load(const Ref & ref)  {  	if (auto rec = ref->asRecord()) -		if (rec->item("enc").asText() == "aes-128-gcm") -			return ChannelAcceptData { -				.request = *rec->item("req").as<ChannelRequest>(), -				.key = *rec->item("key").as<PublicKexKey>(), -			}; +		return ChannelAcceptData { +			.request = *rec->item("req").as<ChannelRequest>(), +			.key = *rec->item("key").as<PublicKexKey>(), +		};  	return ChannelAcceptData {  		.request = Stored<ChannelRequest>::load(ref.storage().zref()), @@ -137,21 +133,26 @@ uint64_t Channel::encrypt(BufferCIt plainBegin, BufferCIt plainEnd,  		Buffer & encBuffer, size_t encOffset)  {  	auto plainSize = plainEnd - plainBegin; -	encBuffer.resize(encOffset + plainSize + 8 + 16 + 16); +	encBuffer.resize(encOffset + plainSize + 1 /* counter */ + 16 /* tag */);  	array<uint8_t, 12> iv; -	uint64_t beCount = htobe64(nonceCounter++); -	std::memcpy(encBuffer.data() + encOffset, &beCount, 8); -	std::copy_n(nonceFixedOur.begin(), 6, iv.begin()); -	std::copy_n(encBuffer.begin() + encOffset + 2, 6, iv.begin() + 6); +	uint64_t count = counterNextOut.fetch_add(1); +	uint64_t beCount = htobe64(count); +	encBuffer[encOffset] = count % 0x100; + +	constexpr size_t nonceFixedSize = std::tuple_size_v<decltype(nonceFixedOur)>; +	static_assert(nonceFixedSize + sizeof beCount == iv.size()); + +	std::copy_n(nonceFixedOur.begin(), nonceFixedSize, iv.begin()); +	std::memcpy(iv.data() + nonceFixedSize, &beCount, sizeof beCount);  	const unique_ptr<EVP_CIPHER_CTX, void(*)(EVP_CIPHER_CTX*)>  		ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); -	EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_gcm(), +	EVP_EncryptInit_ex(ctx.get(), EVP_chacha20_poly1305(),  			nullptr, key.data(), iv.data());  	int outl = 0; -	uint8_t * cur = encBuffer.data() + encOffset + 8; +	uint8_t * cur = encBuffer.data() + encOffset + 1;  	if (EVP_EncryptUpdate(ctx.get(), cur, &outl, &*plainBegin, plainSize) != 1)  		throw runtime_error("failed to encrypt data"); @@ -161,11 +162,8 @@ uint64_t Channel::encrypt(BufferCIt plainBegin, BufferCIt plainEnd,  		throw runtime_error("failed to encrypt data");  	cur += outl; -	EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, 16, cur); -	cur += 16; - -	encBuffer.resize(cur - encBuffer.data()); -	return 0; +	EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, 16, cur); +	return count;  }  optional<uint64_t> Channel::decrypt(BufferCIt encBegin, BufferCIt encEnd, @@ -175,23 +173,33 @@ optional<uint64_t> Channel::decrypt(BufferCIt encBegin, BufferCIt encEnd,  	decBuffer.resize(decOffset + encSize);  	array<uint8_t, 12> iv; -	std::copy_n(nonceFixedPeer.begin(), 6, iv.begin()); -	std::copy_n(encBegin + 2, 6, iv.begin() + 6); +	if (encBegin + 1 /* counter */ + 16 /* tag */ > encEnd) +		return nullopt; + +	uint64_t expectedCount = counterNextIn.load(); +	uint64_t guessedCount = expectedCount - 0x80u + ((0x80u + encBegin[0] - expectedCount) % 0x100u); +	uint64_t beCount = htobe64(guessedCount); + +	constexpr size_t nonceFixedSize = std::tuple_size_v<decltype(nonceFixedPeer)>; +	static_assert(nonceFixedSize + sizeof beCount == iv.size()); + +	std::copy_n(nonceFixedPeer.begin(), nonceFixedSize, iv.begin()); +	std::memcpy(iv.data() + nonceFixedSize, &beCount, sizeof beCount);  	const unique_ptr<EVP_CIPHER_CTX, void(*)(EVP_CIPHER_CTX*)>  		ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); -	EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_gcm(), +	EVP_DecryptInit_ex(ctx.get(), EVP_chacha20_poly1305(),  			nullptr, key.data(), iv.data());  	int outl = 0;  	uint8_t * cur = decBuffer.data() + decOffset;  	if (EVP_DecryptUpdate(ctx.get(), cur, &outl, -				&*encBegin + 8, encSize - 8 - 16) != 1) +				&*encBegin + 1, encSize - 1 - 16) != 1)  		return nullopt;  	cur += outl; -	if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, 16, +	if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, 16,  				(void *) (&*encEnd - 16)))  		return nullopt; @@ -199,6 +207,10 @@ optional<uint64_t> Channel::decrypt(BufferCIt encBegin, BufferCIt encEnd,  		return nullopt;  	cur += outl; +	while (expectedCount < guessedCount + 1 && +			not counterNextIn.compare_exchange_weak(expectedCount, guessedCount + 1)) +		; // empty loop body +  	decBuffer.resize(cur - decBuffer.data()); -	return 0; +	return guessedCount;  } diff --git a/src/network/channel.h b/src/network/channel.h index 98bfd29..bba11b3 100644 --- a/src/network/channel.h +++ b/src/network/channel.h @@ -44,8 +44,8 @@ public:  			vector<uint8_t> && key, bool ourRequest):  		peers(peers),  		key(std::move(key)), -		nonceFixedOur({ uint8_t(ourRequest ? 1 : 2), 0, 0, 0, 0, 0 }), -		nonceFixedPeer({ uint8_t(ourRequest ? 2 : 1), 0, 0, 0, 0, 0 }) +		nonceFixedOur({ uint8_t(ourRequest ? 1 : 2), 0, 0, 0 }), +		nonceFixedPeer({ uint8_t(ourRequest ? 2 : 1), 0, 0, 0 })  	{}  	Channel(const Channel &) = delete; @@ -69,9 +69,10 @@ private:  	const vector<Stored<Signed<IdentityData>>> peers;  	const vector<uint8_t> key; -	const array<uint8_t, 6> nonceFixedOur; -	const array<uint8_t, 6> nonceFixedPeer; -	atomic<uint64_t> nonceCounter = 0; +	const array<uint8_t, 4> nonceFixedOur; +	const array<uint8_t, 4> nonceFixedPeer; +	atomic<uint64_t> counterNextOut = 0; +	atomic<uint64_t> counterNextIn = 0;  };  } |