feat(noise): add padding to transport messages (#1056)

* feat(noise): add padding to transport messages (handshakes+write+read)

* fix(noise): address reviewers' comments

* fix(noise): update padding block size const name
This commit is contained in:
G 2022-08-07 16:20:34 +02:00 committed by GitHub
parent 8a14b5b243
commit 2aeaba48e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 4 deletions

View File

@ -23,6 +23,22 @@ procSuite "Waku Noise":
# We initialize the RNG in std/random
randomize()
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
test "ChaChaPoly Encryption/Decryption: random byte sequences":
let cipherState = randomChaChaPolyCipherState(rng[])

View File

@ -221,8 +221,10 @@ proc processMessagePatternPayload(hs: var HandshakeState, transportMessage: seq[
# We decrypt the transportMessage, if any
if reading:
payload = hs.ss.decryptAndHash(transportMessage)
payload = pkcs7_unpad(payload, NoisePaddingBlockSize)
elif writing:
payload = hs.ss.encryptAndHash(transportMessage)
payload = pkcs7_pad(transportMessage, NoisePaddingBlockSize)
payload = hs.ss.encryptAndHash(payload)
return payload
@ -558,8 +560,10 @@ proc writeMessage*(hsr: var HandshakeResult, transportMessage: seq[byte]): Paylo
# According to 35/WAKU2-NOISE RFC, no Handshake protocol information is sent when exchanging messages
# This correspond to setting protocol-id to 0
payload2.protocolId = 0.uint8
# We pad the transport message
let paddedTransportMessage = pkcs7_pad(transportMessage, NoisePaddingBlockSize)
# Encryption is done with zero-length associated data as per specification
payload2.transportMessage = encryptWithAd(hsr.csOutbound, @[], transportMessage)
payload2.transportMessage = encryptWithAd(hsr.csOutbound, @[], paddedTransportMessage)
return payload2
@ -578,7 +582,9 @@ proc readMessage*(hsr: var HandshakeResult, readPayload2: PayloadV2): Result[seq
# (this because an attacker may flood the content topic on which messages are exchanged)
try:
# Decryption is done with zero-length associated data as per specification
message = decryptWithAd(hsr.csInbound, @[], readPayload2.transportMessage)
let paddedMessage = decryptWithAd(hsr.csInbound, @[], readPayload2.transportMessage)
# We unpdad the decrypted message
message = pkcs7_unpad(paddedMessage, NoisePaddingBlockSize)
except NoiseDecryptTagError:
debug "A read message failed decryption. Returning empty message as plaintext."
message = @[]

View File

@ -247,3 +247,7 @@ const
"ChaChaPoly": 30.uint8
}.toTable()
# Other constants
const
NoisePaddingBlockSize* = 248

View File

@ -5,7 +5,7 @@
{.push raises: [Defect].}
import std/[oids, options, strutils, tables]
import std/[oids, options, strutils, tables, sequtils]
import chronos
import chronicles
import bearssl
@ -33,6 +33,29 @@ proc randomSeqByte*(rng: var BrHmacDrbgContext, size: int): seq[byte] =
brHmacDrbgGenerate(rng, output)
return output
# Pads a payload according to PKCS#7 as per RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
proc pkcs7_pad*(payload: seq[byte], paddingSize: int): seq[byte] =
assert(paddingSize<256)
let k = paddingSize - (payload.len mod paddingSize)
var padding: seq[byte]
if k != 0:
padding = newSeqWith(k, k.byte)
else:
padding = newSeqWith(paddingSize, paddingSize.byte)
let padded = concat(payload, padding)
return padded
# Unpads a payload according to PKCS#7 as per RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
proc pkcs7_unpad*(payload: seq[byte], paddingSize: int): seq[byte] =
let k = payload[payload.high]
let unpadded = payload[0..payload.high-k.int]
return unpadded
#################################################################