2022-04-04 15:46:51 +00:00
{. used . }
import
testutils / unittests ,
2022-04-06 12:37:02 +00:00
std / random ,
2022-05-20 13:51:36 +00:00
std / tables ,
2022-04-06 12:37:02 +00:00
stew / byteutils ,
2022-04-12 11:50:51 +00:00
.. / .. / waku / v2 / node / waku_payload ,
2022-08-04 08:47:00 +00:00
.. / .. / waku / v2 / protocol / waku_noise / noise_types ,
.. / .. / waku / v2 / protocol / waku_noise / noise_utils ,
2022-04-04 15:46:51 +00:00
.. / .. / waku / v2 / protocol / waku_noise / noise ,
2022-08-04 08:47:00 +00:00
.. / .. / waku / v2 / protocol / waku_noise / noise_handshake_processing ,
2022-04-12 11:50:51 +00:00
.. / .. / waku / v2 / protocol / waku_message ,
2022-05-20 13:51:36 +00:00
.. / test_helpers ,
libp2p / crypto / chacha20poly1305 ,
2022-09-12 00:23:14 +00:00
libp2p / protobuf / minprotobuf ,
2022-05-20 13:51:36 +00:00
stew / endians2
2022-04-04 15:46:51 +00:00
procSuite " Waku Noise " :
2022-04-06 12:37:02 +00:00
# We initialize the RNG in test_helpers
2022-04-04 15:46:51 +00:00
let rng = rng ( )
2022-04-06 12:37:02 +00:00
# We initialize the RNG in std/random
randomize ( )
2022-04-04 15:46:51 +00:00
2022-08-07 14:20:34 +00:00
test " PKCS#7 Padding/Unpadding " :
# We test padding for different message lengths
let maxMessageLength = 3 * NoisePaddingBlockSize
for messageLen in 0 .. maxMessageLength :
let
message = randomSeqByte ( rng [ ] , messageLen )
padded = pkcs7_pad ( message , NoisePaddingBlockSize )
unpadded = pkcs7_unpad ( padded , NoisePaddingBlockSize )
check :
padded . len ! = 0
padded . len mod NoisePaddingBlockSize = = 0
message = = unpadded
2022-04-06 12:37:02 +00:00
test " ChaChaPoly Encryption/Decryption: random byte sequences " :
2022-04-04 15:46:51 +00:00
let cipherState = randomChaChaPolyCipherState ( rng [ ] )
2022-04-06 12:37:02 +00:00
# We encrypt/decrypt random byte sequences
2022-04-04 15:46:51 +00:00
let
2022-04-06 12:37:02 +00:00
plaintext : seq [ byte ] = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
2022-04-04 15:46:51 +00:00
ciphertext : ChaChaPolyCiphertext = encrypt ( cipherState , plaintext )
decryptedCiphertext : seq [ byte ] = decrypt ( cipherState , ciphertext )
check :
2022-04-06 12:37:02 +00:00
plaintext = = decryptedCiphertext
test " ChaChaPoly Encryption/Decryption: random strings " :
let cipherState = randomChaChaPolyCipherState ( rng [ ] )
# We encrypt/decrypt random strings
var plaintext : string
for _ in 1 .. rand ( 1 .. 128 ) :
add ( plaintext , char ( rand ( int ( ' A ' ) .. int ( ' z ' ) ) ) )
let
ciphertext : ChaChaPolyCiphertext = encrypt ( cipherState , plaintext . toBytes ( ) )
decryptedCiphertext : seq [ byte ] = decrypt ( cipherState , ciphertext )
check :
plaintext . toBytes ( ) = = decryptedCiphertext
2022-04-12 11:50:51 +00:00
test " Noise public keys: encrypt and decrypt a public key " :
2022-04-06 12:37:02 +00:00
let noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
let
cs : ChaChaPolyCipherState = randomChaChaPolyCipherState ( rng [ ] )
encryptedPk : NoisePublicKey = encryptNoisePublicKey ( cs , noisePublicKey )
decryptedPk : NoisePublicKey = decryptNoisePublicKey ( cs , encryptedPk )
check :
noisePublicKey = = decryptedPk
2022-04-12 11:50:51 +00:00
test " Noise public keys: decrypt an unencrypted public key " :
2022-04-06 12:37:02 +00:00
let noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
let
cs : ChaChaPolyCipherState = randomChaChaPolyCipherState ( rng [ ] )
decryptedPk : NoisePublicKey = decryptNoisePublicKey ( cs , noisePublicKey )
check :
noisePublicKey = = decryptedPk
2022-04-12 11:50:51 +00:00
test " Noise public keys: encrypt an encrypted public key " :
2022-04-06 12:37:02 +00:00
let noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
let
cs : ChaChaPolyCipherState = randomChaChaPolyCipherState ( rng [ ] )
encryptedPk : NoisePublicKey = encryptNoisePublicKey ( cs , noisePublicKey )
encryptedPk2 : NoisePublicKey = encryptNoisePublicKey ( cs , encryptedPk )
check :
encryptedPk = = encryptedPk2
2022-04-12 11:50:51 +00:00
test " Noise public keys: encrypt, decrypt and decrypt a public key " :
2022-04-06 12:37:02 +00:00
let noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
let
cs : ChaChaPolyCipherState = randomChaChaPolyCipherState ( rng [ ] )
encryptedPk : NoisePublicKey = encryptNoisePublicKey ( cs , noisePublicKey )
decryptedPk : NoisePublicKey = decryptNoisePublicKey ( cs , encryptedPk )
decryptedPk2 : NoisePublicKey = decryptNoisePublicKey ( cs , decryptedPk )
check :
decryptedPk = = decryptedPk2
2022-04-12 11:50:51 +00:00
test " Noise public keys: serialize and deserialize an unencrypted public key " :
2022-04-06 12:37:02 +00:00
let
noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
serializedNoisePublicKey : seq [ byte ] = serializeNoisePublicKey ( noisePublicKey )
deserializedNoisePublicKey : NoisePublicKey = intoNoisePublicKey ( serializedNoisePublicKey )
check :
noisePublicKey = = deserializedNoisePublicKey
2022-04-12 11:50:51 +00:00
test " Noise public keys: encrypt, serialize, deserialize and decrypt a public key " :
2022-04-06 12:37:02 +00:00
let noisePublicKey : NoisePublicKey = genNoisePublicKey ( rng [ ] )
let
cs : ChaChaPolyCipherState = randomChaChaPolyCipherState ( rng [ ] )
encryptedPk : NoisePublicKey = encryptNoisePublicKey ( cs , noisePublicKey )
serializedNoisePublicKey : seq [ byte ] = serializeNoisePublicKey ( encryptedPk )
deserializedNoisePublicKey : NoisePublicKey = intoNoisePublicKey ( serializedNoisePublicKey )
decryptedPk : NoisePublicKey = decryptNoisePublicKey ( cs , deserializedNoisePublicKey )
check :
2022-04-12 11:50:51 +00:00
noisePublicKey = = decryptedPk
test " PayloadV2: serialize/deserialize PayloadV2 to byte sequence " :
let
payload2 : PayloadV2 = randomPayloadV2 ( rng [ ] )
serializedPayload = serializePayloadV2 ( payload2 )
check :
serializedPayload . isOk ( )
let deserializedPayload = deserializePayloadV2 ( serializedPayload . get ( ) )
check :
deserializedPayload . isOk ( )
payload2 = = deserializedPayload . get ( )
test " PayloadV2: Encode/Decode a Waku Message (version 2) to a PayloadV2 " :
# We encode to a WakuMessage a random PayloadV2
let
payload2 = randomPayloadV2 ( rng [ ] )
msg = encodePayloadV2 ( payload2 )
check :
msg . isOk ( )
# We create ProtoBuffer from WakuMessage
let pb = msg . get ( ) . encode ( )
# We decode the WakuMessage from the ProtoBuffer
2022-11-07 15:24:16 +00:00
let msgFromPb = WakuMessage . decode ( pb . buffer )
2022-04-12 11:50:51 +00:00
check :
msgFromPb . isOk ( )
let decoded = decodePayloadV2 ( msgFromPb . get ( ) )
check :
decoded . isOk ( )
2022-05-20 13:51:36 +00:00
payload2 = = decoded . get ( )
test " Noise State Machine: Diffie-Hellman operation " :
#We generate random keypairs
let
aliceKey = genKeyPair ( rng [ ] )
bobKey = genKeyPair ( rng [ ] )
# A Diffie-Hellman operation between Alice's private key and Bob's public key must be equal to
# a Diffie-hellman operation between Alice's public key and Bob's private key
let
dh1 = dh ( getPrivateKey ( aliceKey ) , getPublicKey ( bobKey ) )
dh2 = dh ( getPrivateKey ( bobKey ) , getPublicKey ( aliceKey ) )
check :
dh1 = = dh2
test " Noise State Machine: Cipher State primitives " :
# We generate a random Cipher State, associated data ad and plaintext
var
cipherState : CipherState = randomCipherState ( rng [ ] )
nonce : uint64 = uint64 ( rand ( 0 .. int . high ) )
ad : seq [ byte ] = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
plaintext : seq [ byte ] = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
# We set the random nonce generated in the cipher state
setNonce ( cipherState , nonce )
# We perform encryption
var ciphertext : seq [ byte ] = encryptWithAd ( cipherState , ad , plaintext )
# After any encryption/decryption operation, the Cipher State's nonce increases by 1
check :
getNonce ( cipherState ) = = nonce + 1
# We set the nonce back to its original value for decryption
setNonce ( cipherState , nonce )
# We decrypt (using the original nonce)
var decrypted : seq [ byte ] = decryptWithAd ( cipherState , ad , ciphertext )
# We check if encryption and decryption are correct and that nonce correctly increased after decryption
check :
getNonce ( cipherState ) = = nonce + 1
plaintext = = decrypted
# If a Cipher State has no key set, encryptWithAd should return the plaintext without increasing the nonce
setCipherStateKey ( cipherState , EmptyKey )
nonce = getNonce ( cipherState )
plaintext = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
ciphertext = encryptWithAd ( cipherState , ad , plaintext )
check :
ciphertext = = plaintext
getNonce ( cipherState ) = = nonce
# If a Cipher State has no key set, decryptWithAd should return the ciphertext without increasing the nonce
setCipherStateKey ( cipherState , EmptyKey )
nonce = getNonce ( cipherState )
# Note that we set ciphertext minimum length to 16 to not trigger checks on authentication tag length
ciphertext = randomSeqByte ( rng [ ] , rand ( 16 .. 128 ) )
plaintext = decryptWithAd ( cipherState , ad , ciphertext )
check :
ciphertext = = plaintext
getNonce ( cipherState ) = = nonce
# A Cipher State cannot have a nonce greater or equal 2^64-1
# Note that NonceMax is uint64.high - 1 = 2^64-1-1 and that nonce is increased after each encryption and decryption operation
# We generate a test Cipher State with nonce set to MaxNonce
cipherState = randomCipherState ( rng [ ] )
setNonce ( cipherState , NonceMax )
plaintext = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
# We test if encryption fails with a NoiseNonceMaxError error. Any subsequent encryption call over the Cipher State should fail similarly and leave the nonce unchanged
for _ in [ 1 .. 5 ] :
expect NoiseNonceMaxError :
ciphertext = encryptWithAd ( cipherState , ad , plaintext )
check :
getNonce ( cipherState ) = = NonceMax + 1
# We generate a test Cipher State
# Since nonce is increased after decryption as well, we need to generate a proper ciphertext in order to test MaxNonceError error handling
# We cannot call encryptWithAd to encrypt a plaintext using a nonce equal MaxNonce, since this will trigger a MaxNonceError.
# To perform such test, we then need to encrypt a test plaintext using directly ChaChaPoly primitive
cipherState = randomCipherState ( rng [ ] )
setNonce ( cipherState , NonceMax )
plaintext = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
# We perform encryption using the Cipher State key, NonceMax and ad
# By Noise specification the nonce is 8 bytes long out of the 12 bytes supported by ChaChaPoly, thus we copy the Little endian conversion of the nonce to a ChaChaPolyNonce
var
encNonce : ChaChaPolyNonce
authorizationTag : ChaChaPolyTag
encNonce [ 4 .. < 12 ] = toBytesLE ( NonceMax )
ChaChaPoly . encrypt ( getKey ( cipherState ) , encNonce , authorizationTag , plaintext , ad )
# The output ciphertext is stored in the plaintext variable after ChaChaPoly.encrypt is called: we copy it along with the authorization tag.
ciphertext = @ [ ]
ciphertext . add ( plaintext )
ciphertext . add ( authorizationTag )
# At this point ciphertext is a proper encryption of the original plaintext obtained with nonce equal to NonceMax
# We can now test if decryption fails with a NoiseNonceMaxError error. Any subsequent decryption call over the Cipher State should fail similarly and leave the nonce unchanged
# Note that decryptWithAd doesn't fail in decrypting the ciphertext (otherwise a NoiseDecryptTagError would have been triggered)
for _ in [ 1 .. 5 ] :
expect NoiseNonceMaxError :
plaintext = decryptWithAd ( cipherState , ad , ciphertext )
check :
getNonce ( cipherState ) = = NonceMax + 1
test " Noise State Machine: Symmetric State primitives " :
# We select one supported handshake pattern and we initialize a symmetric state
var
hsPattern = NoiseHandshakePatterns [ " XX " ]
symmetricState : SymmetricState = SymmetricState . init ( hsPattern )
# We get all the Symmetric State field
# cs : Cipher State
# ck : chaining key
# h : handshake hash
var
cs = getCipherState ( symmetricState )
ck = getChainingKey ( symmetricState )
h = getHandshakeHash ( symmetricState )
# When a Symmetric state is initialized, handshake hash and chaining key are (byte-wise) equal
check :
h . data . intoChaChaPolyKey = = ck
########################################
# mixHash
########################################
# We generate a random byte sequence and execute a mixHash over it
mixHash ( symmetricState , randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) ) )
# mixHash changes only the handshake hash value of the Symmetric state
check :
cs = = getCipherState ( symmetricState )
ck = = getChainingKey ( symmetricState )
h ! = getHandshakeHash ( symmetricState )
# We update test values
h = getHandshakeHash ( symmetricState )
########################################
# mixKey
########################################
# We generate random input key material and we execute mixKey
2022-06-03 19:12:41 +00:00
var inputKeyMaterial = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
2022-05-20 13:51:36 +00:00
mixKey ( symmetricState , inputKeyMaterial )
# mixKey changes the Symmetric State's chaining key and encryption key of the embedded Cipher State
# It further sets to 0 the nonce of the embedded Cipher State
check :
getKey ( cs ) ! = getKey ( getCipherState ( symmetricState ) )
getNonce ( getCipherState ( symmetricState ) ) = = 0 . uint64
cs ! = getCipherState ( symmetricState )
ck ! = getChainingKey ( symmetricState )
h = = getHandshakeHash ( symmetricState )
# We update test values
cs = getCipherState ( symmetricState )
ck = getChainingKey ( symmetricState )
########################################
# mixKeyAndHash
########################################
# We generate random input key material and we execute mixKeyAndHash
2022-06-03 19:12:41 +00:00
inputKeyMaterial = randomSeqByte ( rng [ ] , rand ( 1 .. 128 ) )
2022-05-20 13:51:36 +00:00
mixKeyAndHash ( symmetricState , inputKeyMaterial )
# mixKeyAndHash executes a mixKey and a mixHash using the input key material
# All Symmetric State's fields are updated
check :
cs ! = getCipherState ( symmetricState )
ck ! = getChainingKey ( symmetricState )
h ! = getHandshakeHash ( symmetricState )
# We update test values
cs = getCipherState ( symmetricState )
ck = getChainingKey ( symmetricState )
h = getHandshakeHash ( symmetricState )
########################################
# encryptAndHash and decryptAndHash
########################################
# We store the initial symmetricState in order to correctly perform decryption
var initialSymmetricState = symmetricState
# We generate random plaintext and we execute encryptAndHash
var plaintext = randomChaChaPolyKey ( rng [ ] )
var nonce = getNonce ( getCipherState ( symmetricState ) )
var ciphertext = encryptAndHash ( symmetricState , plaintext )
# encryptAndHash combines encryptWithAd and mixHash over the ciphertext (encryption increases the nonce of the embedded Cipher State but does not change its key)
# We check if only the handshake hash value and the Symmetric State changed accordingly
check :
cs ! = getCipherState ( symmetricState )
getKey ( cs ) = = getKey ( getCipherState ( symmetricState ) )
getNonce ( getCipherState ( symmetricState ) ) = = nonce + 1
ck = = getChainingKey ( symmetricState )
h ! = getHandshakeHash ( symmetricState )
# We restore the symmetric State to its initial value to test decryption
symmetricState = initialSymmetricState
# We execute decryptAndHash over the ciphertext
var decrypted = decryptAndHash ( symmetricState , ciphertext )
# decryptAndHash combines decryptWithAd and mixHash over the ciphertext (encryption increases the nonce of the embedded Cipher State but does not change its key)
# We check if only the handshake hash value and the Symmetric State changed accordingly
# We further check if decryption corresponds to the original plaintext
check :
cs ! = getCipherState ( symmetricState )
getKey ( cs ) = = getKey ( getCipherState ( symmetricState ) )
getNonce ( getCipherState ( symmetricState ) ) = = nonce + 1
ck = = getChainingKey ( symmetricState )
h ! = getHandshakeHash ( symmetricState )
decrypted = = plaintext
########################################
# split
########################################
# If at least one mixKey is executed (as above), ck is non-empty
check :
getChainingKey ( symmetricState ) ! = EmptyKey
# When a Symmetric State's ck is non-empty, we can execute split, which creates two distinct Cipher States cs1 and cs2
# with non-empty encryption keys and nonce set to 0
var ( cs1 , cs2 ) = split ( symmetricState )
check :
getKey ( cs1 ) ! = EmptyKey
getKey ( cs2 ) ! = EmptyKey
getNonce ( cs1 ) = = 0 . uint64
getNonce ( cs2 ) = = 0 . uint64
getKey ( cs1 ) ! = getKey ( cs2 )
2022-06-03 19:12:41 +00:00
test " Noise XX Handhshake and message encryption (extended test) " :
let hsPattern = NoiseHandshakePatterns [ " XX " ]
# We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair ( rng [ ] )
var aliceHS = initialize ( hsPattern = hsPattern , staticKey = aliceStaticKey , initiator = true )
let bobStaticKey = genKeyPair ( rng [ ] )
var bobHS = initialize ( hsPattern = hsPattern , staticKey = bobStaticKey )
var
sentTransportMessage : seq [ byte ]
aliceStep , bobStep : HandshakeStepResult
# Here the handshake starts
# Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
###############
# 1st step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
###############
# 2nd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# At this step, Bob writes and returns a payload
bobStep = stepHandshake ( rng [ ] , bobHS , transportMessage = sentTransportMessage ) . get ( )
# While Alice reads and returns the (decrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , readPayloadV2 = bobStep . payload2 ) . get ( )
check :
aliceStep . transportMessage = = sentTransportMessage
###############
# 3rd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
# Note that for this handshake pattern, no more message patterns are left for processing
# Another call to stepHandshake would return an empty HandshakeStepResult
# We test that extra calls to stepHandshake do not affect parties' handshake states
# and that the intermediate HandshakeStepResult are empty
let prevAliceHS = aliceHS
let prevBobHS = bobHS
let bobStep1 = stepHandshake ( rng [ ] , bobHS , transportMessage = sentTransportMessage ) . get ( )
let aliceStep1 = stepHandshake ( rng [ ] , aliceHS , readPayloadV2 = bobStep1 . payload2 ) . get ( )
let aliceStep2 = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
let bobStep2 = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep2 . payload2 ) . get ( )
check :
aliceStep1 = = default ( HandshakeStepResult )
aliceStep2 = = default ( HandshakeStepResult )
bobStep1 = = default ( HandshakeStepResult )
bobStep2 = = default ( HandshakeStepResult )
aliceHS = = prevAliceHS
bobHS = = prevBobHS
#########################
# After Handshake
#########################
# We finalize the handshake to retrieve the Inbound/Outbound symmetric states
var aliceHSResult , bobHSResult : HandshakeResult
aliceHSResult = finalizeHandshake ( aliceHS )
bobHSResult = finalizeHandshake ( bobHS )
# We test read/write of random messages exchanged between Alice and Bob
var
payload2 : PayloadV2
message : seq [ byte ]
readMessage : seq [ byte ]
2022-09-12 00:23:14 +00:00
defaultMessageNametagBuffer : MessageNametagBuffer
2022-06-03 19:12:41 +00:00
for _ in 0 .. 10 :
# Alice writes to Bob
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( aliceHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( bobHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
# Bob writes to Alice
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( bobHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( aliceHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
test " Noise XXpsk0 Handhshake and message encryption (short test) " :
let hsPattern = NoiseHandshakePatterns [ " XXpsk0 " ]
# We generate a random psk
let psk = randomSeqByte ( rng [ ] , 32 )
# We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair ( rng [ ] )
var aliceHS = initialize ( hsPattern = hsPattern , staticKey = aliceStaticKey , psk = psk , initiator = true )
let bobStaticKey = genKeyPair ( rng [ ] )
var bobHS = initialize ( hsPattern = hsPattern , staticKey = bobStaticKey , psk = psk )
var
sentTransportMessage : seq [ byte ]
aliceStep , bobStep : HandshakeStepResult
# Here the handshake starts
# Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
###############
# 1st step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
###############
# 2nd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# At this step, Bob writes and returns a payload
bobStep = stepHandshake ( rng [ ] , bobHS , transportMessage = sentTransportMessage ) . get ( )
# While Alice reads and returns the (decrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , readPayloadV2 = bobStep . payload2 ) . get ( )
check :
aliceStep . transportMessage = = sentTransportMessage
###############
# 3rd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
# Note that for this handshake pattern, no more message patterns are left for processing
#########################
# After Handshake
#########################
# We finalize the handshake to retrieve the Inbound/Outbound Symmetric States
var aliceHSResult , bobHSResult : HandshakeResult
aliceHSResult = finalizeHandshake ( aliceHS )
bobHSResult = finalizeHandshake ( bobHS )
# We test read/write of random messages exchanged between Alice and Bob
var
payload2 : PayloadV2
message : seq [ byte ]
readMessage : seq [ byte ]
2022-09-12 00:23:14 +00:00
defaultMessageNametagBuffer : MessageNametagBuffer
2022-06-03 19:12:41 +00:00
for _ in 0 .. 10 :
# Alice writes to Bob
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( aliceHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( bobHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
# Bob writes to Alice
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( bobHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( aliceHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
test " Noise K1K1 Handhshake and message encryption (short test) " :
let hsPattern = NoiseHandshakePatterns [ " K1K1 " ]
# We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair ( rng [ ] )
let bobStaticKey = genKeyPair ( rng [ ] )
# This handshake has the following pre-message pattern:
# -> s
# <- s
# ...
# So we define accordingly the sequence of the pre-message public keys
let preMessagePKs : seq [ NoisePublicKey ] = @ [ toNoisePublicKey ( getPublicKey ( aliceStaticKey ) ) , toNoisePublicKey ( getPublicKey ( bobStaticKey ) ) ]
var aliceHS = initialize ( hsPattern = hsPattern , staticKey = aliceStaticKey , preMessagePKs = preMessagePKs , initiator = true )
var bobHS = initialize ( hsPattern = hsPattern , staticKey = bobStaticKey , preMessagePKs = preMessagePKs )
var
sentTransportMessage : seq [ byte ]
aliceStep , bobStep : HandshakeStepResult
# Here the handshake starts
# Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
###############
# 1st step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
###############
# 2nd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# At this step, Bob writes and returns a payload
bobStep = stepHandshake ( rng [ ] , bobHS , transportMessage = sentTransportMessage ) . get ( )
# While Alice reads and returns the (decrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , readPayloadV2 = bobStep . payload2 ) . get ( )
check :
aliceStep . transportMessage = = sentTransportMessage
###############
# 3rd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake_message and the (encrypted) transportMessage
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transportMessage alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
# Note that for this handshake pattern, no more message patterns are left for processing
#########################
# After Handshake
#########################
# We finalize the handshake to retrieve the Inbound/Outbound Symmetric States
var aliceHSResult , bobHSResult : HandshakeResult
aliceHSResult = finalizeHandshake ( aliceHS )
bobHSResult = finalizeHandshake ( bobHS )
# We test read/write of random messages between Alice and Bob
var
payload2 : PayloadV2
message : seq [ byte ]
readMessage : seq [ byte ]
2022-09-12 00:23:14 +00:00
defaultMessageNametagBuffer : MessageNametagBuffer
2022-06-03 19:12:41 +00:00
for _ in 0 .. 10 :
# Alice writes to Bob
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( aliceHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( bobHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
# Bob writes to Alice
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( bobHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( aliceHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
test " Noise XK1 Handhshake and message encryption (short test) " :
let hsPattern = NoiseHandshakePatterns [ " XK1 " ]
# We initialize Alice's and Bob's Handshake State
let aliceStaticKey = genKeyPair ( rng [ ] )
let bobStaticKey = genKeyPair ( rng [ ] )
# This handshake has the following pre-message pattern:
# <- s
# ...
# So we define accordingly the sequence of the pre-message public keys
let preMessagePKs : seq [ NoisePublicKey ] = @ [ toNoisePublicKey ( getPublicKey ( bobStaticKey ) ) ]
var aliceHS = initialize ( hsPattern = hsPattern , staticKey = aliceStaticKey , preMessagePKs = preMessagePKs , initiator = true )
var bobHS = initialize ( hsPattern = hsPattern , staticKey = bobStaticKey , preMessagePKs = preMessagePKs )
var
sentTransportMessage : seq [ byte ]
aliceStep , bobStep : HandshakeStepResult
# Here the handshake starts
# Write and read calls alternate between Alice and Bob: the handhshake progresses by alternatively calling stepHandshake for each user
###############
# 1st step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# By being the handshake initiator, Alice writes a Waku2 payload v2 containing her handshake message
# and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
###############
# 2nd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# At this step, Bob writes and returns a payload
bobStep = stepHandshake ( rng [ ] , bobHS , transportMessage = sentTransportMessage ) . get ( )
# While Alice reads and returns the (decrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , readPayloadV2 = bobStep . payload2 ) . get ( )
check :
aliceStep . transportMessage = = sentTransportMessage
###############
# 3rd step
###############
# We generate a random transport message
sentTransportMessage = randomSeqByte ( rng [ ] , 32 )
# Similarly as in first step, Alice writes a Waku2 payload containing the handshake message and the (encrypted) transport message
aliceStep = stepHandshake ( rng [ ] , aliceHS , transportMessage = sentTransportMessage ) . get ( )
# Bob reads Alice's payloads, and returns the (decrypted) transport message Alice sent to him
bobStep = stepHandshake ( rng [ ] , bobHS , readPayloadV2 = aliceStep . payload2 ) . get ( )
check :
bobStep . transportMessage = = sentTransportMessage
# Note that for this handshake pattern, no more message patterns are left for processing
#########################
# After Handshake
#########################
# We finalize the handshake to retrieve the Inbound/Outbound Symmetric States
var aliceHSResult , bobHSResult : HandshakeResult
aliceHSResult = finalizeHandshake ( aliceHS )
bobHSResult = finalizeHandshake ( bobHS )
# We test read/write of random messages exchanged between Alice and Bob
var
payload2 : PayloadV2
message : seq [ byte ]
readMessage : seq [ byte ]
2022-09-12 00:23:14 +00:00
defaultMessageNametagBuffer : MessageNametagBuffer
2022-06-03 19:12:41 +00:00
for _ in 0 .. 10 :
# Alice writes to Bob
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( aliceHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( bobHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage
# Bob writes to Alice
message = randomSeqByte ( rng [ ] , 32 )
2022-09-12 00:23:14 +00:00
payload2 = writeMessage ( bobHSResult , message , defaultMessageNametagBuffer )
readMessage = readMessage ( aliceHSResult , payload2 , defaultMessageNametagBuffer ) . get ( )
2022-06-03 19:12:41 +00:00
check :
message = = readMessage