mirror of https://github.com/waku-org/nwaku.git
feat(noise): add support to Noise public keys
This commit is contained in:
parent
2392565881
commit
d6024a8fa0
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue