From 4af9d7af2427018f51d392b396cd786ee233975c Mon Sep 17 00:00:00 2001 From: s1fr0 Date: Tue, 12 Apr 2022 12:39:01 +0000 Subject: [PATCH] deploy: cb11c192d63ac22e22b2d5348712471153a26a31 --- tests/v2/test_waku_noise.nim | 58 +++++- .../vendor/libbacktrace-upstream/libtool | 2 +- waku/v2/node/waku_payload.nim | 38 +++- waku/v2/protocol/waku_noise/noise.nim | 168 +++++++++++++++++- 4 files changed, 249 insertions(+), 17 deletions(-) diff --git a/tests/v2/test_waku_noise.nim b/tests/v2/test_waku_noise.nim index 482b37fc9..60e479ef0 100644 --- a/tests/v2/test_waku_noise.nim +++ b/tests/v2/test_waku_noise.nim @@ -4,7 +4,9 @@ import testutils/unittests, std/random, stew/byteutils, + ../../waku/v2/node/waku_payload, ../../waku/v2/protocol/waku_noise/noise, + ../../waku/v2/protocol/waku_message, ../test_helpers procSuite "Waku Noise": @@ -43,7 +45,7 @@ procSuite "Waku Noise": check: plaintext.toBytes() == decryptedCiphertext - test "Encrypt and decrypt Noise public keys": + test "Noise public keys: encrypt and decrypt a public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -55,7 +57,7 @@ procSuite "Waku Noise": check: noisePublicKey == decryptedPk - test "Decrypt unencrypted public key": + test "Noise public keys: decrypt an unencrypted public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -66,7 +68,7 @@ procSuite "Waku Noise": check: noisePublicKey == decryptedPk - test "Encrypt encrypted public key": + test "Noise public keys: encrypt an encrypted public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -78,7 +80,7 @@ procSuite "Waku Noise": check: encryptedPk == encryptedPk2 - test "Encrypt, decrypt and decrypt public key": + test "Noise public keys: encrypt, decrypt and decrypt a public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -91,7 +93,7 @@ procSuite "Waku Noise": check: decryptedPk == decryptedPk2 - test "Serialize and deserialize unencrypted public key": + test "Noise public keys: serialize and deserialize an unencrypted public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -101,7 +103,7 @@ procSuite "Waku Noise": check: noisePublicKey == deserializedNoisePublicKey - test "Encrypt, serialize, deserialize and decrypt public key": + test "Noise public keys: encrypt, serialize, deserialize and decrypt a public key": let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[]) @@ -113,4 +115,46 @@ procSuite "Waku Noise": decryptedPk: NoisePublicKey = decryptNoisePublicKey(cs, deserializedNoisePublicKey) check: - noisePublicKey == decryptedPk \ No newline at end of file + 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 + let msgFromPb = WakuMessage.init(pb.buffer) + + check: + msgFromPb.isOk() + + let decoded = decodePayloadV2(msgFromPb.get()) + + check: + decoded.isOk() + payload2 == decoded.get() \ No newline at end of file diff --git a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool index a57a4f7f5..a24b96734 100755 --- a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool +++ b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool @@ -2,7 +2,7 @@ # libtool - Provide generalized library-building support services. # Generated automatically by config.status (libbacktrace) version-unused -# Libtool was configured on host fv-az129-611: +# Libtool was configured on host fv-az449-568: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, diff --git a/waku/v2/node/waku_payload.nim b/waku/v2/node/waku_payload.nim index c936bd2a6..07e71a65e 100644 --- a/waku/v2/node/waku_payload.nim +++ b/waku/v2/node/waku_payload.nim @@ -4,7 +4,8 @@ import std/options, eth/keys, ../../whisper/whisper_types, - ../protocol/waku_message + ../protocol/waku_message, + ../protocol/waku_noise/noise export whisper_types, keys, options @@ -56,7 +57,7 @@ proc decodePayload*(message: WakuMessage, keyInfo: KeyInfo): return ok(decoded.get()) else: return err("Couldn't decrypt using asymmetric key") - of None: + else: discard else: return err("Unsupported WakuMessage version") @@ -77,3 +78,36 @@ proc encode*(payload: Payload, version: uint32, rng: var BrHmacDrbgContext): return err("Couldn't encode the payload") else: return err("Unsupported WakuMessage version") + + +# Decodes a WakuMessage to a PayloadV2 +# Currently, this is just a wrapper over deserializePayloadV2 and encryption/decryption is done on top (no KeyInfo) +proc decodePayloadV2*(message: WakuMessage): WakuResult[PayloadV2] + {.raises: [Defect, NoiseMalformedHandshake, NoisePublicKeyError].} = + # We check message version (only 2 is supported in this proc) + case message.version + of 2: + # We attempt to decode the WakuMessage payload + let deserializedPayload2 = deserializePayloadV2(message.payload) + if deserializedPayload2.isOk(): + return ok(deserializedPayload2.get()) + else: + return err("Failed to decode WakuMessage") + else: + return err("Wrong message version while decoding payload") + + +# Encodes a PayloadV2 to a WakuMessage +# Currently, this is just a wrapper over serializePayloadV2 and encryption/decryption is done on top (no KeyInfo) +proc encodePayloadV2*(payload2: PayloadV2): WakuResult[WakuMessage] + {.raises: [Defect, NoiseMalformedHandshake, NoisePublicKeyError].} = + + # We attempt to encode the PayloadV2 + let serializedPayload2 = serializePayloadV2(payload2) + if not serializedPayload2.isOk(): + return err("Failed to encode PayloadV2") + + # If successful, we create and return a WakuMessage + let msg = WakuMessage(payload: serializedPayload2.get(), version: 2) + + return ok(msg) \ No newline at end of file diff --git a/waku/v2/protocol/waku_noise/noise.nim b/waku/v2/protocol/waku_noise/noise.nim index 16651670d..c9f91dd03 100644 --- a/waku/v2/protocol/waku_noise/noise.nim +++ b/waku/v2/protocol/waku_noise/noise.nim @@ -7,16 +7,13 @@ {.push raises: [Defect].} -import std/[oids, options] +import std/[options, tables, strutils] import chronos import chronicles import bearssl +import stew/[results, endians2] import nimcrypto/[utils, sha2, hmac] -import libp2p/stream/[connection] -import libp2p/peerid -import libp2p/peerinfo -import libp2p/protobuf/minprotobuf import libp2p/utility import libp2p/errors import libp2p/crypto/[crypto, chacha20poly1305, curve25519] @@ -49,6 +46,7 @@ type # This follows https://rfc.vac.dev/spec/35/#public-keys-serialization # pk contains the X coordinate of the public key, if unencrypted (this implies flag = 0) # or the encryption of the X coordinate concatenated with the authorization tag, if encrypted (this implies flag = 1) + # Note: besides encryption, flag can be used to distinguish among multiple supported Elliptic Curves NoisePublicKey* = object flag: uint8 pk: seq[byte] @@ -64,6 +62,15 @@ type nonce: ChaChaPolyNonce ad: seq[byte] + # PayloadV2 defines an object for Waku payloads with version 2 as in + # https://rfc.vac.dev/spec/35/#public-keys-serialization + # It contains a protocol ID field, the handshake message (for Noise handshakes) and + # a transport message (for Noise handshakes and ChaChaPoly encryptions) + PayloadV2* = object + protocolId: uint8 + handshakeMessage: seq[NoisePublicKey] + transportMessage: seq[byte] + # Some useful error types NoiseError* = object of LPError NoiseHandshakeError* = object of NoiseError @@ -84,7 +91,7 @@ proc randomSeqByte*(rng: var BrHmacDrbgContext, size: int): seq[byte] = brHmacDrbgGenerate(rng, output) return output -# Generate random Curve25519 (public, private) key pairs +# Generate random (public, private) Elliptic Curve key pairs proc genKeyPair*(rng: var BrHmacDrbgContext): KeyPair = var keyPair: KeyPair keyPair.privateKey = EllipticCurveKey.random(rng) @@ -246,4 +253,151 @@ proc decryptNoisePublicKey*(cs: ChaChaPolyCipherState, noisePublicKey: NoisePubl # Otherwise we return the public key as it is else: decryptedNoisePublicKey = noisePublicKey - return decryptedNoisePublicKey \ No newline at end of file + return decryptedNoisePublicKey + + +################################################################# + +# Payload encoding/decoding procedures + +# Checks equality between two PayloadsV2 objects +proc `==`(p1, p2: PayloadV2): bool = + return (p1.protocolId == p2.protocolId) and + (p1.handshakeMessage == p2.handshakeMessage) and + (p1.transportMessage == p2.transportMessage) + + +# Generates a random PayloadV2 +proc randomPayloadV2*(rng: var BrHmacDrbgContext): PayloadV2 = + var payload2: PayloadV2 + # To generate a random protocol id, we generate a random 1-byte long sequence, and we convert the first element to uint8 + payload2.protocolId = randomSeqByte(rng, 1)[0].uint8 + # We set the handshake message to three unencrypted random Noise Public Keys + payload2.handshakeMessage = @[genNoisePublicKey(rng), genNoisePublicKey(rng), genNoisePublicKey(rng)] + # We set the transport message to a random 128-bytes long sequence + payload2.transportMessage = randomSeqByte(rng, 128) + return payload2 + + +# Serializes a PayloadV2 object to a byte sequences according to https://rfc.vac.dev/spec/35/. +# The output serialized payload concatenates the input PayloadV2 object fields as +# payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage) +# The output can be then passed to the payload field of a WakuMessage https://rfc.vac.dev/spec/14/ +proc serializePayloadV2*(self: PayloadV2): Result[seq[byte], cstring] = + + #We collect public keys contained in the handshake message + var + # According to https://rfc.vac.dev/spec/35/, the maximum size for the handshake message is 256 bytes, that is + # the handshake message length can be represented with 1 byte only. (its length can be stored in 1 byte) + # However, to ease public keys length addition operation, we declare it as int and later cast to uit8 + serializedHandshakeMessageLen: int = 0 + # This variables will store the concatenation of the serializations of all public keys in the handshake message + serializedHandshakeMessage = newSeqOfCap[byte](256) + # A variable to store the currently processed public key serialization + serializedPk: seq[byte] + # For each public key in the handshake message + for pk in self.handshakeMessage: + # We serialize the public key + serializedPk = serializeNoisePublicKey(pk) + # We sum its serialized length to the total + serializedHandshakeMessageLen += serializedPk.len + # We add its serialization to the concatenation of all serialized public keys in the handshake message + serializedHandshakeMessage.add serializedPk + # If we are processing more than 256 byte, we return an error + if serializedHandshakeMessageLen > uint8.high.int: + debug "PayloadV2 malformed: too many public keys contained in the handshake message" + return err("Too many public keys in handshake message") + + + # We get the transport message byte length + let transportMessageLen = self.transportMessage.len + + # The output payload as in https://rfc.vac.dev/spec/35/. We concatenate all the PayloadV2 fields as + # payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage) + # We declare it as a byte sequence of length accordingly to the PayloadV2 information read + var payload = newSeqOfCap[byte](1 + # 1 byte for protocol ID + 1 + # 1 byte for length of serializedHandshakeMessage field + serializedHandshakeMessageLen + # serializedHandshakeMessageLen bytes for serializedHandshakeMessage + 8 + # 8 bytes for transportMessageLen + transportMessageLen # transportMessageLen bytes for transportMessage + ) + + # We concatenate all the data + # The protocol ID (1 byte) and handshake message length (1 byte) can be directly casted to byte to allow direct copy to the payload byte sequence + payload.add self.protocolId.byte + payload.add serializedHandshakeMessageLen.byte + payload.add serializedHandshakeMessage + # The transport message length is converted from uint64 to bytes in Little-Endian + payload.add toBytesLE(transportMessageLen.uint64) + payload.add self.transportMessage + + return ok(payload) + + + +# Deserializes a byte sequence to a PayloadV2 object according to https://rfc.vac.dev/spec/35/. +# The input serialized payload concatenates the output PayloadV2 object fields as +# payload = ( protocolId || serializedHandshakeMessageLen || serializedHandshakeMessage || transportMessageLen || transportMessage) +proc deserializePayloadV2*(payload: seq[byte]): Result[PayloadV2, cstring] + {.raises: [Defect, NoisePublicKeyError].} = + + # The output PayloadV2 + var payload2: PayloadV2 + + # i is the read input buffer position index + var i: uint64 = 0 + + # We start reading the Protocol ID + # TODO: when the list of supported protocol ID is defined, check if read protocol ID is supported + payload2.protocolId = payload[i].uint8 + i += 1 + + # We read the Handshake Message lenght (1 byte) + var handshakeMessageLen = payload[i].uint64 + if handshakeMessageLen > uint8.high.uint64: + debug "Payload malformed: too many public keys contained in the handshake message" + #raise newException(NoiseMalformedHandshake, "Too many public keys in handshake message") + return err("Too many public keys in handshake message") + + i += 1 + + # We now read for handshakeMessageLen bytes the buffer and we deserialize each (encrypted/unencrypted) public key read + var + # In handshakeMessage we accumulate the read deserialized Noise Public keys + handshakeMessage: seq[NoisePublicKey] + flag: byte + pkLen: uint64 + written: uint64 = 0 + + # We read the buffer until handshakeMessageLen are read + while written != handshakeMessageLen: + # We obtain the current Noise Public key encryption flag + flag = payload[i] + # If the key is unencrypted, we only read the X coordinate of the EC public key and we deserialize into a Noise Public Key + if flag == 0: + pkLen = 1 + EllipticCurveKey.len + handshake_message.add intoNoisePublicKey(payload[i..