2023-02-01 01:54:32 +01:00
import { HMACDRBG } from "@stablelib/hmac-drbg" ;
import { randomBytes } from "@stablelib/random" ;
2023-05-11 00:25:19 +02:00
import type { IDecoder , IEncoder , IMessage , IProtoMessage , IReceiver , ISender } from "@waku/interfaces" ;
2023-02-01 01:54:32 +01:00
import { expect } from "chai" ;
import { EventEmitter } from "eventemitter3" ;
import { pEvent } from "p-event" ;
import { equals as uint8ArrayEquals } from "uint8arrays/equals" ;
import { NoiseHandshakeMessage } from "./codec" ;
2023-11-20 15:56:52 -04:00
import { DH25519 } from "./dh25519" ;
2023-02-01 01:54:32 +01:00
import { MessageNametagBufferSize } from "./messagenametag" ;
import { ResponderParameters , WakuPairing } from "./pairing" ;
2024-04-18 23:49:15 +02:00
const PUBSUB_TOPIC = "/waku/2/default-waku/proto" ;
2023-04-04 01:34:13 +02:00
2023-05-09 16:27:00 +10:00
const EMPTY_PROTOMESSAGE = {
timestamp : undefined ,
2024-04-16 01:58:29 +02:00
contentTopic : "/js-noise/1/message/proto" ,
2023-05-09 16:27:00 +10:00
ephemeral : undefined ,
meta : undefined ,
rateLimitProof : undefined ,
version : undefined ,
} ;
2023-02-01 01:54:32 +01:00
describe ( "js-noise: pairing object" , ( ) = > {
const rng = new HMACDRBG ( ) ;
const confirmAuthCodeFlow = async function ( pairingObj : WakuPairing , shouldConfirm : boolean ) : Promise < void > {
const authCode = await pairingObj . getAuthCode ( ) ;
console . log ( "Authcode: " , authCode ) ; // TODO: compare that authCode is the same in both confirmation flows
pairingObj . validateAuthCode ( shouldConfirm ) ;
} ;
// =================
// Simulate waku. This code is not meant to be used IRL
const msgEmitter = new EventEmitter ( ) ;
2023-04-04 01:34:13 +02:00
const sender : ISender = {
async send ( encoder : IEncoder , msg : IMessage ) {
2023-02-01 01:54:32 +01:00
const protoMsg = await encoder . toProtoObj ( msg ) ;
msgEmitter . emit ( encoder . contentTopic , protoMsg ) ;
2023-04-04 01:34:13 +02:00
return {
2024-04-11 17:15:28 +02:00
successes : [ ] ,
2023-04-04 01:34:13 +02:00
} ;
2023-02-01 01:54:32 +01:00
} ,
} ;
const responder = {
2023-05-11 00:25:19 +02:00
toSubscriptionIterator ( decoder : IDecoder < NoiseHandshakeMessage > ) {
return {
2023-05-12 21:44:21 +02:00
iterator : {
async next() {
const msg = await pEvent ( msgEmitter , decoder . contentTopic ) ;
const decodedMessage = await decoder . fromProtoObj ( PUBSUB_TOPIC , msg ) ;
return {
value : decodedMessage ,
done : false ,
} ;
} ,
} ,
2023-05-11 00:25:19 +02:00
stop() {
// Do nothing. This is just a simulation
console . debug ( "stopping subscription to" , decoder . contentTopic ) ;
} ,
2023-05-11 00:27:56 +02:00
} ;
} ,
2023-05-11 00:25:19 +02:00
} as any as IReceiver ;
2023-02-01 01:54:32 +01:00
// =================
it ( "should pair" , async function ( ) {
2023-11-20 15:56:52 -04:00
const dhKey = new DH25519 ( ) ;
const bobStaticKey = dhKey . generateKeyPair ( ) ;
const aliceStaticKey = dhKey . generateKeyPair ( ) ;
2023-02-01 01:54:32 +01:00
const recvParameters = new ResponderParameters ( ) ;
2024-04-18 23:49:15 +02:00
const bobPairingObj = new WakuPairing ( PUBSUB_TOPIC , sender , responder , bobStaticKey , recvParameters ) ;
2023-02-01 01:54:32 +01:00
const bobExecP1 = bobPairingObj . execute ( ) ;
// Confirmation is done by manually
confirmAuthCodeFlow ( bobPairingObj , true ) ;
const initParameters = bobPairingObj . getPairingInfo ( ) ;
2024-04-18 23:49:15 +02:00
const alicePairingObj = new WakuPairing ( PUBSUB_TOPIC , sender , responder , aliceStaticKey , initParameters ) ;
2023-02-01 01:54:32 +01:00
const aliceExecP1 = alicePairingObj . execute ( ) ;
// Confirmation is done manually
confirmAuthCodeFlow ( alicePairingObj , true ) ;
const [ bobCodecs , aliceCodecs ] = await Promise . all ( [ bobExecP1 , aliceExecP1 ] ) ;
const bobEncoder = bobCodecs [ 0 ] ;
const bobDecoder = bobCodecs [ 1 ] ;
const aliceEncoder = aliceCodecs [ 0 ] ;
const aliceDecoder = aliceCodecs [ 1 ] ;
// We test read/write of random messages exchanged between Alice and Bob
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
for ( let i = 0 ; i < 10 * MessageNametagBufferSize ; i ++ ) {
// Alice writes to Bob
let message = randomBytes ( 32 , rng ) ;
let encodedMsg = await aliceEncoder . toWire ( { payload : message } ) ;
let readMessageProto = await bobDecoder . fromWireToProtoObj ( encodedMsg ! ) ;
2023-04-04 01:34:13 +02:00
let readMessage = await bobDecoder . fromProtoObj ( PUBSUB_TOPIC , readMessageProto ! ) ;
2023-02-01 01:54:32 +01:00
expect ( uint8ArrayEquals ( message , readMessage ! . payload ) ) . to . be . true ;
// Bob writes to Alice
message = randomBytes ( 32 , rng ) ;
encodedMsg = await bobEncoder . toWire ( { payload : message } ) ;
readMessageProto = await aliceDecoder . fromWireToProtoObj ( encodedMsg ! ) ;
2023-04-04 01:34:13 +02:00
readMessage = await aliceDecoder . fromProtoObj ( PUBSUB_TOPIC , readMessageProto ! ) ;
2023-02-01 01:54:32 +01:00
expect ( uint8ArrayEquals ( message , readMessage ! . payload ) ) . to . be . true ;
}
} ) ;
it ( "should timeout" , async function ( ) {
2023-11-20 15:56:52 -04:00
const dhKey = new DH25519 ( ) ;
2024-04-18 23:51:05 +02:00
const bobPairingObj = new WakuPairing (
PUBSUB_TOPIC ,
sender ,
responder ,
dhKey . generateKeyPair ( ) ,
new ResponderParameters ( )
) ;
const alicePairingObj = new WakuPairing (
PUBSUB_TOPIC ,
sender ,
responder ,
dhKey . generateKeyPair ( ) ,
bobPairingObj . getPairingInfo ( )
) ;
2023-02-01 01:54:32 +01:00
const bobExecP1 = bobPairingObj . execute ( 1000 ) ;
const aliceExecP1 = alicePairingObj . execute ( 1000 ) ;
try {
await Promise . all ( [ bobExecP1 , aliceExecP1 ] ) ;
expect ( false , "should not reach here" ) . to . be . true ;
} catch ( err ) {
let message ;
if ( err instanceof Error ) message = err . message ;
else message = String ( err ) ;
expect ( message ) . to . be . equals ( "pairing has timed out" ) ;
}
} ) ;
2023-05-09 16:27:00 +10:00
it ( "pairs and `meta` field is encoded" , async function ( ) {
2023-11-20 15:56:52 -04:00
const dhKey = new DH25519 ( ) ;
const bobStaticKey = dhKey . generateKeyPair ( ) ;
const aliceStaticKey = dhKey . generateKeyPair ( ) ;
2023-05-09 16:27:00 +10:00
// Encode the length of the payload
// Not a relevant real life example
const metaSetter = ( msg : IProtoMessage & { meta : undefined } ) : Uint8Array = > {
const buffer = new ArrayBuffer ( 4 ) ;
const view = new DataView ( buffer ) ;
view . setUint32 ( 0 , msg . payload . length , false ) ;
return new Uint8Array ( buffer ) ;
} ;
const recvParameters = new ResponderParameters ( ) ;
2024-04-18 23:51:05 +02:00
const bobPairingObj = new WakuPairing ( PUBSUB_TOPIC , sender , responder , bobStaticKey , recvParameters , undefined , {
metaSetter ,
} ) ;
2023-05-09 16:27:00 +10:00
const bobExecP1 = bobPairingObj . execute ( ) ;
// Confirmation is done by manually
confirmAuthCodeFlow ( bobPairingObj , true ) ;
const initParameters = bobPairingObj . getPairingInfo ( ) ;
2024-04-18 23:51:05 +02:00
const alicePairingObj = new WakuPairing (
PUBSUB_TOPIC ,
sender ,
responder ,
aliceStaticKey ,
initParameters ,
undefined ,
{
metaSetter ,
}
) ;
2023-05-09 16:27:00 +10:00
const aliceExecP1 = alicePairingObj . execute ( ) ;
// Confirmation is done manually
confirmAuthCodeFlow ( alicePairingObj , true ) ;
const [ bobCodecs , aliceCodecs ] = await Promise . all ( [ bobExecP1 , aliceExecP1 ] ) ;
const bobEncoder = bobCodecs [ 0 ] ;
const bobDecoder = bobCodecs [ 1 ] ;
const aliceEncoder = aliceCodecs [ 0 ] ;
const aliceDecoder = aliceCodecs [ 1 ] ;
// We test read/write of random messages exchanged between Alice and Bob
// Note that we exchange more than the number of messages contained in the nametag buffer to test if they are filled correctly as the communication proceeds
for ( let i = 0 ; i < 10 * MessageNametagBufferSize ; i ++ ) {
// Alice writes to Bob
let message = randomBytes ( 32 , rng ) ;
let encodedMsg = await aliceEncoder . toWire ( { payload : message } ) ;
let readMessageProto = await bobDecoder . fromWireToProtoObj ( encodedMsg ! ) ;
let readMessage = await bobDecoder . fromProtoObj ( PUBSUB_TOPIC , readMessageProto ! ) ;
expect ( uint8ArrayEquals ( message , readMessage ! . payload ) ) . to . be . true ;
let expectedMeta = metaSetter ( {
. . . EMPTY_PROTOMESSAGE ,
payload : readMessageProto ! . payload ,
} ) ;
expect ( readMessage ! . meta ) . to . deep . eq ( expectedMeta ) ;
// Bob writes to Alice
message = randomBytes ( 32 , rng ) ;
encodedMsg = await bobEncoder . toWire ( { payload : message } ) ;
readMessageProto = await aliceDecoder . fromWireToProtoObj ( encodedMsg ! ) ;
readMessage = await aliceDecoder . fromProtoObj ( PUBSUB_TOPIC , readMessageProto ! ) ;
expect ( uint8ArrayEquals ( message , readMessage ! . payload ) ) . to . be . true ;
expectedMeta = metaSetter ( {
. . . EMPTY_PROTOMESSAGE ,
payload : readMessageProto ! . payload ,
} ) ;
expect ( readMessage ! . meta ) . to . deep . eq ( expectedMeta ) ;
}
} ) ;
2023-02-01 01:54:32 +01:00
} ) ;