feat(noise): add support to Noise public keys

This commit is contained in:
s1fr0 2022-03-30 23:30:33 +02:00
parent 2392565881
commit d6024a8fa0
No known key found for this signature in database
GPG Key ID: 2C041D60117BFF46
3 changed files with 195 additions and 25 deletions

View File

@ -2,27 +2,27 @@ import
# Waku v2 tests
# TODO: enable this when it is altered into a proper waku relay test
# ./v2/test_waku,
#./v2/test_wakunode,
#./v2/test_waku_store,
#./v2/test_waku_filter,
#./v2/test_waku_pagination,
#./v2/test_waku_payload,
#./v2/test_waku_swap,
#./v2/test_message_store,
#./v2/test_jsonrpc_waku,
#./v2/test_peer_manager,
#./v2/test_web3, # TODO remove it when rln-relay tests get finalized
#./v2/test_waku_bridge,
#./v2/test_peer_storage,
#./v2/test_waku_keepalive,
#./v2/test_migration_utils,
#./v2/test_namespacing_utils,
#./v2/test_waku_dnsdisc,
#./v2/test_waku_discv5,
#./v2/test_enr_utils,
#./v2/test_waku_store_queue,
#./v2/test_pagination_utils,
#./v2/test_peer_exchange
./v2/test_wakunode,
./v2/test_waku_store,
./v2/test_waku_filter,
./v2/test_waku_pagination,
./v2/test_waku_payload,
./v2/test_waku_swap,
./v2/test_message_store,
./v2/test_jsonrpc_waku,
./v2/test_peer_manager,
./v2/test_web3, # TODO remove it when rln-relay tests get finalized
./v2/test_waku_bridge,
./v2/test_peer_storage,
./v2/test_waku_keepalive,
./v2/test_migration_utils,
./v2/test_namespacing_utils,
./v2/test_waku_dnsdisc,
./v2/test_waku_discv5,
./v2/test_enr_utils,
./v2/test_waku_store_queue,
./v2/test_pagination_utils,
./v2/test_peer_exchange
./v2/test_waku_noise
when defined(rln):

View File

@ -3,4 +3,80 @@
import
testutils/unittests,
../../waku/v2/protocol/waku_noise/noise,
../test_helpers
../test_helpers,
std/tables
procSuite "Waku Noise":
let rng = rng()
test "Encrypt -> decrypt public keys":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
enc_pk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey)
dec_pk: NoisePublicKey = decryptNoisePublicKey(cs, enc_pk)
check:
noisePublicKey == dec_pk
test "Decrypt unencrypted public key":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
dec_pk: NoisePublicKey = decryptNoisePublicKey(cs, noisePublicKey)
check:
noisePublicKey == dec_pk
test "Encrypt -> encrypt public keys":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
enc_pk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey)
enc2_pk: NoisePublicKey = encryptNoisePublicKey(cs, enc_pk)
check enc_pk == enc2_pk
test "Encrypt -> decrypt -> decrypt public keys":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
enc_pk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey)
dec_pk: NoisePublicKey = decryptNoisePublicKey(cs, enc_pk)
dec2_pk: NoisePublicKey = decryptNoisePublicKey(cs, dec_pk)
check:
dec_pk == dec2_pk
test "Serialize -> deserialize public keys (unencrypted)":
let
noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(noisePublicKey)
deserializedNoisePublicKey: NoisePublicKey = intoNoisePublicKey(serializedNoisePublicKey)
check:
noisePublicKey == deserializedNoisePublicKey
test "Encrypt -> serialize -> deserialize -> decrypt public keys":
let noisePublicKey: NoisePublicKey = genNoisePublicKey(rng[])
let
cs: ChaChaPolyCipherState = randomChaChaPolyCipherState(rng[])
enc_pk: NoisePublicKey = encryptNoisePublicKey(cs, noisePublicKey)
serializedNoisePublicKey: seq[byte] = serializeNoisePublicKey(enc_pk)
deserializedNoisePublicKey: NoisePublicKey = intoNoisePublicKey(serializedNoisePublicKey)
dec_pk: NoisePublicKey = decryptNoisePublicKey(cs, deserializedNoisePublicKey)
check:
noisePublicKey == dec_pk

View File

@ -9,17 +9,22 @@
{.push raises: [Defect].}
import std/[oids, options]
import std/[oids, options, tables]
import chronos
import chronicles
import bearssl
import strutils
import stew/[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]
import libp2p/crypto/[crypto, chacha20poly1305, curve25519]
when defined(libp2p_dump):
import libp2p/debugutils
@ -30,8 +35,20 @@ logScope:
const
# Empty is a special value which indicates k has not yet been initialized.
EmptyKey = default(ChaChaPolyKey)
NonceMax = uint64.high - 1 # max is reserved
NoiseSize = 32
MaxPlainSize = int(uint16.high - NoiseSize - ChaChaPolyTag.len)
type
KeyPair* = object
privateKey: Curve25519Key
publicKey: Curve25519Key
NoisePublicKey* = object
flag: uint8
pk: seq[byte]
ChaChaPolyCiphertext* = object
data: seq[byte]
tag: ChaChaPolyTag
@ -42,7 +59,16 @@ type
ad*: seq[byte]
NoiseError* = object of LPError
NoiseHandshakeError* = object of NoiseError
NoiseDecryptTagError* = object of NoiseError
NoiseNonceMaxError* = object of NoiseError # drop connection on purpose
NoisePublicKeyError* = object of NoiseError
NoiseMalformedHandshake* = object of NoiseError
#################################################################
# ChaChaPoly encryption
proc encrypt*(
@ -73,4 +99,72 @@ proc randomChaChaPolyCipherState*(rng: var BrHmacDrbgContext): ChaChaPolyCipherS
brHmacDrbgGenerate(rng, result.k)
brHmacDrbgGenerate(rng, result.nonce)
result.ad = newSeq[byte](32)
brHmacDrbgGenerate(rng, result.ad)
brHmacDrbgGenerate(rng, result.ad)
#################################################################
# Utility
proc genKeyPair*(rng: var BrHmacDrbgContext): KeyPair =
result.privateKey = Curve25519Key.random(rng)
result.publicKey = result.privateKey.public()
# Public keys serializations/encryption
proc `==`(k1, k2: NoisePublicKey): bool =
result = (k1.flag == k2.flag) and (k1.pk == k2.pk)
proc keyPairToNoisePublicKey*(keyPair: KeyPair): NoisePublicKey =
result.flag = 0
result.pk = getBytes(keyPair.publicKey)
proc genNoisePublicKey*(rng: var BrHmacDrbgContext): NoisePublicKey =
let keyPair: KeyPair = genKeyPair(rng)
result.flag = 0
result.pk = getBytes(keyPair.publicKey)
proc serializeNoisePublicKey*(noisePublicKey: NoisePublicKey): seq[byte] =
result.add noisePublicKey.flag
result.add noisePublicKey.pk
#TODO: strip pk_auth if pk not encrypted
proc intoNoisePublicKey*(serializedNoisePublicKey: seq[byte]): NoisePublicKey =
result.flag = serializedNoisePublicKey[0]
assert result.flag == 0 or result.flag == 1
result.pk = serializedNoisePublicKey[1..<serializedNoisePublicKey.len]
# Public keys encryption/decryption
proc encryptNoisePublicKey*(cs: ChaChaPolyCipherState, noisePublicKey: NoisePublicKey): NoisePublicKey
{.raises: [Defect, NoiseNonceMaxError].} =
if cs.k != EmptyKey and noisePublicKey.flag == 0:
let enc_pk = encrypt(cs, noisePublicKey.pk)
result.flag = 1
result.pk = enc_pk.data
result.pk.add enc_pk.tag
else:
result = noisePublicKey
proc decryptNoisePublicKey*(cs: ChaChaPolyCipherState, noisePublicKey: NoisePublicKey): NoisePublicKey
{.raises: [Defect, NoiseDecryptTagError].} =
if cs.k != EmptyKey and noisePublicKey.flag == 1:
#let ciphertext = ChaChaPolyCiphertext(data: noisePublicKey.pk, tag: noisePublicKey.pk_auth)
let pk_len = noisePublicKey.pk.len - ChaChaPolyTag.len
let pk = noisePublicKey.pk[0..<pk_len]
let pk_auth = intoChaChaPolyTag(noisePublicKey.pk[pk_len..<pk_len+ChaChaPolyTag.len])
let ciphertext = ChaChaPolyCiphertext(data: pk, tag: pk_auth)
result.pk = decrypt(cs, ciphertext)
result.flag = 0
else:
if cs.k == EmptyKey:
debug "No key in cipher state."
if noisePublicKey.flag == 0:
debug "Public key is not encrypted."
debug "Public key is left unchanged"
result = noisePublicKey