diff --git a/tests/v2/test_waku_noise.nim b/tests/v2/test_waku_noise.nim index 8c10c1922..f1bf2abbc 100644 --- a/tests/v2/test_waku_noise.nim +++ b/tests/v2/test_waku_noise.nim @@ -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[]) diff --git a/waku/v2/protocol/waku_noise/noise_handshake_processing.nim b/waku/v2/protocol/waku_noise/noise_handshake_processing.nim index d2ea73c23..d99b3bb63 100644 --- a/waku/v2/protocol/waku_noise/noise_handshake_processing.nim +++ b/waku/v2/protocol/waku_noise/noise_handshake_processing.nim @@ -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 = @[] diff --git a/waku/v2/protocol/waku_noise/noise_types.nim b/waku/v2/protocol/waku_noise/noise_types.nim index 7300ef46b..0f75899f3 100644 --- a/waku/v2/protocol/waku_noise/noise_types.nim +++ b/waku/v2/protocol/waku_noise/noise_types.nim @@ -247,3 +247,7 @@ const "ChaChaPoly": 30.uint8 }.toTable() + +# Other constants +const + NoisePaddingBlockSize* = 248 \ No newline at end of file diff --git a/waku/v2/protocol/waku_noise/noise_utils.nim b/waku/v2/protocol/waku_noise/noise_utils.nim index 9573a0478..9fd30d86f 100644 --- a/waku/v2/protocol/waku_noise/noise_utils.nim +++ b/waku/v2/protocol/waku_noise/noise_utils.nim @@ -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 #################################################################