mirror of https://github.com/waku-org/nwaku.git
feat(noise): add support to ChaChaPoly encryption (#930)
* feat(noise): adding ChaChaPoly encryption * fix(noise): restored waku2 tests * fix(noise): missing comma * Fixed header * fix(noise): made comment about empty ChaChaPolyKey clearer * refactor(noise): address reviewer's comments * test(noise): add test for ChaChaPoly Encryption/decryption * refactor(noise): removed libp2p specific debug tools usage * style(noise): converted variables to camelCase
This commit is contained in:
parent
54d64a63fe
commit
45b1e10647
|
@ -22,7 +22,8 @@ import
|
|||
./v2/test_enr_utils,
|
||||
./v2/test_waku_store_queue,
|
||||
./v2/test_pagination_utils,
|
||||
./v2/test_peer_exchange
|
||||
./v2/test_peer_exchange,
|
||||
./v2/test_waku_noise
|
||||
|
||||
when defined(rln):
|
||||
import ./v2/test_waku_rln_relay
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
testutils/unittests,
|
||||
../../waku/v2/protocol/waku_noise/noise,
|
||||
../test_helpers
|
||||
|
||||
|
||||
procSuite "Waku Noise":
|
||||
|
||||
let rng = rng()
|
||||
|
||||
test "ChaChaPoly Encryption/Decryption":
|
||||
|
||||
let cipherState = randomChaChaPolyCipherState(rng[])
|
||||
|
||||
let
|
||||
plaintext: seq[byte] = randomSeqByte(rng[], 128)
|
||||
ciphertext: ChaChaPolyCiphertext = encrypt(cipherState, plaintext)
|
||||
decryptedCiphertext: seq[byte] = decrypt(cipherState, ciphertext)
|
||||
|
||||
check:
|
||||
plaintext == decryptedCiphertext
|
|
@ -0,0 +1,94 @@
|
|||
# Waku Noise Protocols for Waku Payload Encryption
|
||||
## See spec for more details:
|
||||
## https://github.com/vacp2p/rfc/tree/master/content/docs/rfcs/35
|
||||
##
|
||||
## Implementation partially inspired by noise-libp2p:
|
||||
## https://github.com/status-im/nim-libp2p/blob/master/libp2p/protocols/secure/noise.nim
|
||||
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import std/[oids, options]
|
||||
import chronos
|
||||
import chronicles
|
||||
import bearssl
|
||||
import nimcrypto/[utils, sha2, hmac]
|
||||
|
||||
import libp2p/stream/[connection]
|
||||
import libp2p/protobuf/minprotobuf
|
||||
import libp2p/utility
|
||||
import libp2p/errors
|
||||
import libp2p/crypto/[crypto, chacha20poly1305]
|
||||
|
||||
logScope:
|
||||
topics = "wakunoise"
|
||||
|
||||
const
|
||||
# EmptyKey is a special value which indicates a ChaChaPolyKey has not yet been initialized.
|
||||
EmptyKey = default(ChaChaPolyKey)
|
||||
|
||||
type
|
||||
ChaChaPolyCiphertext* = object
|
||||
data: seq[byte]
|
||||
tag: ChaChaPolyTag
|
||||
|
||||
ChaChaPolyCipherState* = object
|
||||
k*: ChaChaPolyKey
|
||||
nonce*: ChaChaPolyNonce
|
||||
ad*: seq[byte]
|
||||
|
||||
NoiseError* = object of LPError
|
||||
NoiseDecryptTagError* = object of NoiseError
|
||||
|
||||
#################################################################
|
||||
|
||||
# Utilities
|
||||
|
||||
# Generates random byte sequences of given size
|
||||
proc randomSeqByte*(rng: var BrHmacDrbgContext, size: uint32): seq[byte] =
|
||||
var output = newSeq[byte](size)
|
||||
brHmacDrbgGenerate(rng, output)
|
||||
return output
|
||||
|
||||
#################################################################
|
||||
|
||||
# ChaChaPoly Symmetric Cipher
|
||||
|
||||
# ChaChaPoly encryption
|
||||
# It takes a Cipher State (with key, nonce, and associated data) and encrypts a plaintext
|
||||
proc encrypt*(
|
||||
state: ChaChaPolyCipherState,
|
||||
plaintext: openArray[byte]): ChaChaPolyCiphertext
|
||||
{.noinit, raises: [Defect].} =
|
||||
#TODO: add padding
|
||||
var ciphertext: ChaChaPolyCiphertext
|
||||
ciphertext.data.add plaintext
|
||||
ChaChaPoly.encrypt(state.k, state.nonce, ciphertext.tag, ciphertext.data, state.ad)
|
||||
return ciphertext
|
||||
|
||||
# ChaChaPoly decryption
|
||||
# It takes a Cipher State (with key, nonce, and associated data) and decrypts a ciphertext
|
||||
proc decrypt*(
|
||||
state: ChaChaPolyCipherState,
|
||||
ciphertext: ChaChaPolyCiphertext): seq[byte]
|
||||
{.raises: [Defect, NoiseDecryptTagError].} =
|
||||
var
|
||||
tagIn = ciphertext.tag
|
||||
tagOut: ChaChaPolyTag
|
||||
var plaintext = ciphertext.data
|
||||
ChaChaPoly.decrypt(state.k, state.nonce, tagOut, plaintext, state.ad)
|
||||
#TODO: add unpadding
|
||||
trace "decrypt", tagIn = tagIn.shortLog, tagOut = tagOut.shortLog, nonce = state.nonce
|
||||
if tagIn != tagOut:
|
||||
debug "decrypt failed", plaintext = shortLog(plaintext)
|
||||
raise newException(NoiseDecryptTagError, "decrypt tag authentication failed.")
|
||||
return plaintext
|
||||
|
||||
# Generates a random Cipher Test for testing encryption/decryption
|
||||
proc randomChaChaPolyCipherState*(rng: var BrHmacDrbgContext): ChaChaPolyCipherState =
|
||||
var randomCipherState: ChaChaPolyCipherState
|
||||
brHmacDrbgGenerate(rng, randomCipherState.k)
|
||||
brHmacDrbgGenerate(rng, randomCipherState.nonce)
|
||||
randomCipherState.ad = newSeq[byte](32)
|
||||
brHmacDrbgGenerate(rng, randomCipherState.ad)
|
||||
return randomCipherState
|
Loading…
Reference in New Issue