mirror of
https://github.com/logos-messaging/js-noise.git
synced 2026-01-03 22:23:12 +00:00
feat: pairing object
This commit is contained in:
parent
c911333ef1
commit
b60bc1af1e
64
package-lock.json
generated
64
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@waku/noise",
|
"name": "@waku/noise",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@waku/noise",
|
"name": "@waku/noise",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@stablelib/chacha20poly1305": "^1.0.1",
|
"@stablelib/chacha20poly1305": "^1.0.1",
|
||||||
@ -16,7 +16,9 @@
|
|||||||
"@stablelib/sha256": "^1.0.1",
|
"@stablelib/sha256": "^1.0.1",
|
||||||
"@stablelib/x25519": "^1.0.1",
|
"@stablelib/x25519": "^1.0.1",
|
||||||
"bn.js": "^5.2.1",
|
"bn.js": "^5.2.1",
|
||||||
|
"eventemitter3": "^5.0.0",
|
||||||
"js-base64": "^3.7.3",
|
"js-base64": "^3.7.3",
|
||||||
|
"p-event": "^5.0.1",
|
||||||
"pkcs7-padding": "^0.1.1",
|
"pkcs7-padding": "^0.1.1",
|
||||||
"uint8arraylist": "^2.3.2",
|
"uint8arraylist": "^2.3.2",
|
||||||
"uint8arrays": "^4.0.2"
|
"uint8arrays": "^4.0.2"
|
||||||
@ -4785,9 +4787,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io": {
|
"node_modules/engine.io": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
|
||||||
"integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
|
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cookie": "^0.4.1",
|
"@types/cookie": "^0.4.1",
|
||||||
@ -5485,10 +5487,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/eventemitter3": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.0.tgz",
|
||||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
"integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
@ -6415,6 +6416,12 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/http-proxy/node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/https-proxy-agent": {
|
"node_modules/https-proxy-agent": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
@ -8932,7 +8939,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz",
|
||||||
"integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==",
|
"integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"p-timeout": "^5.0.2"
|
"p-timeout": "^5.0.2"
|
||||||
},
|
},
|
||||||
@ -8947,7 +8953,6 @@
|
|||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
||||||
"integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==",
|
"integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -9035,6 +9040,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-queue/node_modules/eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/p-queue/node_modules/p-timeout": {
|
"node_modules/p-queue/node_modules/p-timeout": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
||||||
@ -15743,9 +15754,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engine.io": {
|
"engine.io": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz",
|
||||||
"integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
|
"integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/cookie": "^0.4.1",
|
"@types/cookie": "^0.4.1",
|
||||||
@ -16263,10 +16274,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.0.tgz",
|
||||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
"integrity": "sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
@ -16942,6 +16952,14 @@
|
|||||||
"eventemitter3": "^4.0.0",
|
"eventemitter3": "^4.0.0",
|
||||||
"follow-redirects": "^1.0.0",
|
"follow-redirects": "^1.0.0",
|
||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"http-proxy-agent": {
|
"http-proxy-agent": {
|
||||||
@ -18868,7 +18886,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz",
|
||||||
"integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==",
|
"integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"p-timeout": "^5.0.2"
|
"p-timeout": "^5.0.2"
|
||||||
},
|
},
|
||||||
@ -18876,8 +18893,7 @@
|
|||||||
"p-timeout": {
|
"p-timeout": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
||||||
"integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==",
|
"integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -18936,6 +18952,12 @@
|
|||||||
"p-timeout": "^5.0.2"
|
"p-timeout": "^5.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"eventemitter3": {
|
||||||
|
"version": "4.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"p-timeout": {
|
"p-timeout": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz",
|
||||||
|
|||||||
@ -121,7 +121,9 @@
|
|||||||
"@stablelib/sha256": "^1.0.1",
|
"@stablelib/sha256": "^1.0.1",
|
||||||
"@stablelib/x25519": "^1.0.1",
|
"@stablelib/x25519": "^1.0.1",
|
||||||
"bn.js": "^5.2.1",
|
"bn.js": "^5.2.1",
|
||||||
|
"eventemitter3": "^5.0.0",
|
||||||
"js-base64": "^3.7.3",
|
"js-base64": "^3.7.3",
|
||||||
|
"p-event": "^5.0.1",
|
||||||
"pkcs7-padding": "^0.1.1",
|
"pkcs7-padding": "^0.1.1",
|
||||||
"uint8arraylist": "^2.3.2",
|
"uint8arraylist": "^2.3.2",
|
||||||
"uint8arrays": "^4.0.2"
|
"uint8arrays": "^4.0.2"
|
||||||
|
|||||||
@ -128,6 +128,13 @@ export interface StepHandshakeParameters {
|
|||||||
messageNametag?: Uint8Array;
|
messageNametag?: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MessageNametagError extends Error {
|
||||||
|
constructor(public readonly expectedNametag: Uint8Array, public readonly actualNametag: Uint8Array) {
|
||||||
|
super("the message nametag of the read message doesn't match the expected one");
|
||||||
|
this.name = "MessageNametagError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Handshake {
|
export class Handshake {
|
||||||
hs: HandshakeState;
|
hs: HandshakeState;
|
||||||
constructor({
|
constructor({
|
||||||
@ -198,13 +205,13 @@ export class Handshake {
|
|||||||
// If we write an answer at this handshake step
|
// If we write an answer at this handshake step
|
||||||
if (writing) {
|
if (writing) {
|
||||||
// We initialize a payload v2 and we set proper protocol ID (if supported)
|
// We initialize a payload v2 and we set proper protocol ID (if supported)
|
||||||
try {
|
const protocolId = PayloadV2ProtocolIDs[this.hs.handshakePattern.name];
|
||||||
hsStepResult.payload2.protocolId =
|
if (protocolId === undefined) {
|
||||||
PayloadV2ProtocolIDs[this.hs.handshakePattern.name as keyof typeof PayloadV2ProtocolIDs];
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error("handshake pattern not supported");
|
throw new Error("handshake pattern not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hsStepResult.payload2.protocolId = protocolId;
|
||||||
|
|
||||||
// We set the messageNametag and the handshake and transport messages
|
// We set the messageNametag and the handshake and transport messages
|
||||||
hsStepResult.payload2.messageNametag = toMessageNametag(messageNametag);
|
hsStepResult.payload2.messageNametag = toMessageNametag(messageNametag);
|
||||||
hsStepResult.payload2.handshakeMessage = this.hs.processMessagePatternTokens();
|
hsStepResult.payload2.handshakeMessage = this.hs.processMessagePatternTokens();
|
||||||
@ -218,8 +225,9 @@ export class Handshake {
|
|||||||
// If we read an answer during this handshake step
|
// If we read an answer during this handshake step
|
||||||
} else if (reading) {
|
} else if (reading) {
|
||||||
// If the read message nametag doesn't match the expected input one we raise an error
|
// If the read message nametag doesn't match the expected input one we raise an error
|
||||||
if (!uint8ArrayEquals(readPayloadV2.messageNametag, toMessageNametag(messageNametag))) {
|
const expectedNametag = toMessageNametag(messageNametag);
|
||||||
throw new Error("the message nametag of the read message doesn't match the expected one");
|
if (!uint8ArrayEquals(readPayloadV2.messageNametag, expectedNametag)) {
|
||||||
|
throw new MessageNametagError(expectedNametag, readPayloadV2.messageNametag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We process the read public keys and (eventually decrypt) the read transport message
|
// We process the read public keys and (eventually decrypt) the read transport message
|
||||||
|
|||||||
@ -36,7 +36,7 @@ function randomNoisePublicKey(): NoisePublicKey {
|
|||||||
|
|
||||||
function randomPayloadV2(rng: HMACDRBG): PayloadV2 {
|
function randomPayloadV2(rng: HMACDRBG): PayloadV2 {
|
||||||
const messageNametag = randomBytes(MessageNametagLength, rng);
|
const messageNametag = randomBytes(MessageNametagLength, rng);
|
||||||
const protocolId = Math.floor(Math.random() * 255);
|
const protocolId = 14;
|
||||||
const handshakeMessage = [randomNoisePublicKey(), randomNoisePublicKey(), randomNoisePublicKey()];
|
const handshakeMessage = [randomNoisePublicKey(), randomNoisePublicKey(), randomNoisePublicKey()];
|
||||||
const transportMessage = randomBytes(128);
|
const transportMessage = randomBytes(128);
|
||||||
return new PayloadV2(messageNametag, protocolId, handshakeMessage, transportMessage);
|
return new PayloadV2(messageNametag, protocolId, handshakeMessage, transportMessage);
|
||||||
|
|||||||
26
src/index.ts
26
src/index.ts
@ -3,15 +3,17 @@ import {
|
|||||||
NoiseHandshakeEncoder,
|
NoiseHandshakeEncoder,
|
||||||
NoiseSecureTransferDecoder,
|
NoiseSecureTransferDecoder,
|
||||||
NoiseSecureTransferEncoder,
|
NoiseSecureTransferEncoder,
|
||||||
} from "./codec";
|
} from "./codec.js";
|
||||||
import { generateX25519KeyPair, generateX25519KeyPairFromSeed } from "./crypto";
|
import { generateX25519KeyPair, generateX25519KeyPairFromSeed } from "./crypto.js";
|
||||||
import {
|
import {
|
||||||
Handshake,
|
Handshake,
|
||||||
HandshakeParameters,
|
HandshakeParameters,
|
||||||
HandshakeResult,
|
HandshakeResult,
|
||||||
HandshakeStepResult,
|
HandshakeStepResult,
|
||||||
|
MessageNametagError,
|
||||||
StepHandshakeParameters,
|
StepHandshakeParameters,
|
||||||
} from "./handshake";
|
} from "./handshake.js";
|
||||||
|
import { InitiatorParameters, Receiver, ReceiverParameters, Sender, WakuPairing } from "./pairing.js";
|
||||||
import {
|
import {
|
||||||
EmptyPreMessage,
|
EmptyPreMessage,
|
||||||
HandshakePattern,
|
HandshakePattern,
|
||||||
@ -21,12 +23,19 @@ import {
|
|||||||
NoiseTokens,
|
NoiseTokens,
|
||||||
PayloadV2ProtocolIDs,
|
PayloadV2ProtocolIDs,
|
||||||
PreMessagePattern,
|
PreMessagePattern,
|
||||||
} from "./patterns";
|
} from "./patterns.js";
|
||||||
import { MessageNametagBuffer } from "./payload";
|
import { MessageNametagBuffer } from "./payload.js";
|
||||||
import { ChaChaPolyCipherState, NoisePublicKey } from "./publickey";
|
import { ChaChaPolyCipherState, NoisePublicKey } from "./publickey.js";
|
||||||
import { QR } from "./qr";
|
import { QR } from "./qr.js";
|
||||||
|
|
||||||
export { Handshake, HandshakeParameters, HandshakeResult, HandshakeStepResult, StepHandshakeParameters };
|
export {
|
||||||
|
Handshake,
|
||||||
|
HandshakeParameters,
|
||||||
|
HandshakeResult,
|
||||||
|
HandshakeStepResult,
|
||||||
|
MessageNametagError,
|
||||||
|
StepHandshakeParameters,
|
||||||
|
};
|
||||||
export { generateX25519KeyPair, generateX25519KeyPairFromSeed };
|
export { generateX25519KeyPair, generateX25519KeyPairFromSeed };
|
||||||
export {
|
export {
|
||||||
EmptyPreMessage,
|
EmptyPreMessage,
|
||||||
@ -42,3 +51,4 @@ export { ChaChaPolyCipherState, NoisePublicKey };
|
|||||||
export { MessageNametagBuffer };
|
export { MessageNametagBuffer };
|
||||||
export { NoiseHandshakeDecoder, NoiseHandshakeEncoder, NoiseSecureTransferDecoder, NoiseSecureTransferEncoder };
|
export { NoiseHandshakeDecoder, NoiseHandshakeEncoder, NoiseSecureTransferDecoder, NoiseSecureTransferEncoder };
|
||||||
export { QR };
|
export { QR };
|
||||||
|
export { InitiatorParameters, ReceiverParameters, Sender, Receiver, WakuPairing };
|
||||||
|
|||||||
108
src/pairing.spec.ts
Normal file
108
src/pairing.spec.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
||||||
|
import { randomBytes } from "@stablelib/random";
|
||||||
|
import { expect } from "chai";
|
||||||
|
import { EventEmitter } from "eventemitter3";
|
||||||
|
import { Decoder, Encoder, Message } from "js-waku/lib/interfaces";
|
||||||
|
import { pEvent } from "p-event";
|
||||||
|
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
|
||||||
|
|
||||||
|
import { NoiseHandshakeMessage } from "./codec";
|
||||||
|
import { generateX25519KeyPair } from "./crypto";
|
||||||
|
import { ReceiverParameters, WakuPairing } from "./pairing";
|
||||||
|
import { MessageNametagBufferSize } from "./payload";
|
||||||
|
|
||||||
|
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();
|
||||||
|
const sender = {
|
||||||
|
async publish(encoder: Encoder, msg: Message): Promise<void> {
|
||||||
|
const protoMsg = await encoder.encodeProto(msg);
|
||||||
|
msgEmitter.emit(encoder.contentTopic, protoMsg);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const decoderMap: { [key: string]: Decoder<NoiseHandshakeMessage> } = {};
|
||||||
|
const receiver = {
|
||||||
|
subscribe(decoder: Decoder<NoiseHandshakeMessage>): void {
|
||||||
|
decoderMap[decoder.contentTopic] = decoder;
|
||||||
|
},
|
||||||
|
async nextMessage(contentTopic: string): Promise<NoiseHandshakeMessage> {
|
||||||
|
const msg = await pEvent(msgEmitter, contentTopic);
|
||||||
|
const decodedMessage = await decoderMap[contentTopic].decode(msg);
|
||||||
|
return decodedMessage!;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// =================
|
||||||
|
|
||||||
|
it("should pair", async function () {
|
||||||
|
const bobStaticKey = generateX25519KeyPair();
|
||||||
|
const aliceStaticKey = generateX25519KeyPair();
|
||||||
|
|
||||||
|
const recvParameters = new ReceiverParameters();
|
||||||
|
const bobPairingObj = new WakuPairing(sender, receiver, bobStaticKey, recvParameters);
|
||||||
|
const bobExecP1 = bobPairingObj.execute();
|
||||||
|
|
||||||
|
// Confirmation is done by manually
|
||||||
|
confirmAuthCodeFlow(bobPairingObj, true);
|
||||||
|
|
||||||
|
const initParameters = bobPairingObj.getPairingInfo();
|
||||||
|
const alicePairingObj = new WakuPairing(sender, receiver, aliceStaticKey, initParameters);
|
||||||
|
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.encode({ payload: message });
|
||||||
|
let readMessageProto = await bobDecoder.decodeProto(encodedMsg!);
|
||||||
|
let readMessage = await bobDecoder.decode(readMessageProto!);
|
||||||
|
|
||||||
|
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
|
||||||
|
|
||||||
|
// Bob writes to Alice
|
||||||
|
message = randomBytes(32, rng);
|
||||||
|
encodedMsg = await bobEncoder.encode({ payload: message });
|
||||||
|
readMessageProto = await aliceDecoder.decodeProto(encodedMsg!);
|
||||||
|
readMessage = await aliceDecoder.decode(readMessageProto!);
|
||||||
|
|
||||||
|
expect(uint8ArrayEquals(message, readMessage!.payload)).to.be.true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should timeout", async function () {
|
||||||
|
const bobPairingObj = new WakuPairing(sender, receiver, generateX25519KeyPair(), new ReceiverParameters());
|
||||||
|
const alicePairingObj = new WakuPairing(sender, receiver, generateX25519KeyPair(), bobPairingObj.getPairingInfo());
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
310
src/pairing.ts
Normal file
310
src/pairing.ts
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
import { HMACDRBG } from "@stablelib/hmac-drbg";
|
||||||
|
import { randomBytes } from "@stablelib/random";
|
||||||
|
import { EventEmitter } from "eventemitter3";
|
||||||
|
import { Decoder, Encoder, Message } from "js-waku/lib/interfaces";
|
||||||
|
import { pEvent } from "p-event";
|
||||||
|
import { equals as uint8ArrayEquals } from "uint8arrays/equals";
|
||||||
|
|
||||||
|
import { KeyPair } from "./@types/keypair.js";
|
||||||
|
import {
|
||||||
|
NoiseHandshakeDecoder,
|
||||||
|
NoiseHandshakeEncoder,
|
||||||
|
NoiseHandshakeMessage,
|
||||||
|
NoiseSecureTransferDecoder,
|
||||||
|
NoiseSecureTransferEncoder,
|
||||||
|
} from "./codec";
|
||||||
|
import { commitPublicKey, generateX25519KeyPair } from "./crypto";
|
||||||
|
import { Handshake, HandshakeResult, HandshakeStepResult, MessageNametagError } from "./handshake";
|
||||||
|
import { NoiseHandshakePatterns } from "./patterns";
|
||||||
|
import { MessageNametagLength } from "./payload";
|
||||||
|
import { NoisePublicKey } from "./publickey";
|
||||||
|
import { QR } from "./qr";
|
||||||
|
|
||||||
|
export interface Sender {
|
||||||
|
publish(encoder: Encoder, msg: Message): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Receiver {
|
||||||
|
subscribe(decoder: Decoder<NoiseHandshakeMessage>): void;
|
||||||
|
|
||||||
|
// next message should return messages received in a content topic
|
||||||
|
// messages should be kept in a queue, meaning that nextMessage
|
||||||
|
// will call pop in the queue to remove the oldest message received
|
||||||
|
// (it's important to maintain order of received messages)
|
||||||
|
nextMessage(contentTopic: string): Promise<NoiseHandshakeMessage>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rng = new HMACDRBG();
|
||||||
|
|
||||||
|
export class InitiatorParameters {
|
||||||
|
constructor(public readonly qrCode: string, public readonly qrMessageNameTag: Uint8Array) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReceiverParameters {
|
||||||
|
constructor(
|
||||||
|
public readonly applicationName: string = "waku-noise-sessions",
|
||||||
|
public readonly applicationVersion: string = "0.1",
|
||||||
|
public readonly shardId: string = "10"
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WakuPairing {
|
||||||
|
public readonly contentTopic: string;
|
||||||
|
|
||||||
|
private initiator: boolean;
|
||||||
|
private randomFixLenVal: Uint8Array; // r or s depending on who is sending the message
|
||||||
|
private handshake: Handshake;
|
||||||
|
private myCommittedStaticKey: Uint8Array;
|
||||||
|
private qr: QR;
|
||||||
|
private qrMessageNameTag: Uint8Array;
|
||||||
|
private authCode?: string;
|
||||||
|
private started = false;
|
||||||
|
|
||||||
|
private eventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
private static toContentTopic(qr: QR): string {
|
||||||
|
return (
|
||||||
|
"/" + qr.applicationName + "/" + qr.applicationVersion + "/wakunoise/1/sessions_shard-" + qr.shardId + "/proto"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private sender: Sender,
|
||||||
|
private receiver: Receiver,
|
||||||
|
private myStaticKey: KeyPair,
|
||||||
|
pairingParameters: InitiatorParameters | ReceiverParameters,
|
||||||
|
private myEphemeralKey: KeyPair = generateX25519KeyPair()
|
||||||
|
) {
|
||||||
|
this.randomFixLenVal = randomBytes(32, rng);
|
||||||
|
this.myCommittedStaticKey = commitPublicKey(this.myStaticKey.publicKey, this.randomFixLenVal);
|
||||||
|
|
||||||
|
if (pairingParameters instanceof InitiatorParameters) {
|
||||||
|
this.initiator = true;
|
||||||
|
this.qr = QR.fromString(pairingParameters.qrCode);
|
||||||
|
this.qrMessageNameTag = pairingParameters.qrMessageNameTag;
|
||||||
|
} else {
|
||||||
|
this.initiator = false;
|
||||||
|
this.qrMessageNameTag = randomBytes(MessageNametagLength, rng);
|
||||||
|
this.qr = new QR(
|
||||||
|
pairingParameters.applicationName,
|
||||||
|
pairingParameters.applicationVersion,
|
||||||
|
pairingParameters.shardId,
|
||||||
|
this.myEphemeralKey.publicKey,
|
||||||
|
this.myCommittedStaticKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// We set the contentTopic from the content topic parameters exchanged in the QR
|
||||||
|
this.contentTopic = WakuPairing.toContentTopic(this.qr);
|
||||||
|
|
||||||
|
// Pre-handshake message
|
||||||
|
// <- eB {H(sB||r), contentTopicParams, messageNametag}
|
||||||
|
const preMessagePKs = [NoisePublicKey.fromPublicKey(this.qr.ephemeralKey)];
|
||||||
|
|
||||||
|
this.handshake = new Handshake({
|
||||||
|
hsPattern: NoiseHandshakePatterns.WakuPairing,
|
||||||
|
ephemeralKey: myEphemeralKey,
|
||||||
|
staticKey: myStaticKey,
|
||||||
|
prologue: this.qr.toByteArray(),
|
||||||
|
preMessagePKs,
|
||||||
|
initiator: this.initiator,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPairingInfo(): InitiatorParameters {
|
||||||
|
return new InitiatorParameters(this.qr.toString(), this.qrMessageNameTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAuthCode(): Promise<string> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (this.authCode) {
|
||||||
|
resolve(this.authCode);
|
||||||
|
} else {
|
||||||
|
this.eventEmitter.on("authCodeGenerated", (authCode: string) => {
|
||||||
|
this.authCode = authCode;
|
||||||
|
resolve(authCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public validateAuthCode(confirmed: boolean): void {
|
||||||
|
this.eventEmitter.emit("confirmAuthCode", confirmed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async isAuthCodeConfirmed(): Promise<boolean | undefined> {
|
||||||
|
// wait for user to confirm or not, or for the whole pairing process to time out
|
||||||
|
const p1 = pEvent(this.eventEmitter, "confirmAuthCode");
|
||||||
|
const p2 = pEvent(this.eventEmitter, "pairingTimeout");
|
||||||
|
return Promise.race<boolean | undefined>([p1, p2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async executeReadStepWithNextMessage(
|
||||||
|
contentTopic: string,
|
||||||
|
messageNametag: Uint8Array
|
||||||
|
): Promise<HandshakeStepResult> {
|
||||||
|
// TODO: create test unit for this function
|
||||||
|
let stopLoop = false;
|
||||||
|
|
||||||
|
this.eventEmitter.once("pairingTimeout", () => {
|
||||||
|
stopLoop = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.eventEmitter.once("pairingComplete", () => {
|
||||||
|
stopLoop = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
while (!stopLoop) {
|
||||||
|
try {
|
||||||
|
const hsMessage = await this.receiver.nextMessage(contentTopic);
|
||||||
|
const step = this.handshake.stepHandshake({
|
||||||
|
readPayloadV2: hsMessage.payloadV2,
|
||||||
|
messageNametag,
|
||||||
|
});
|
||||||
|
return step;
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof MessageNametagError) {
|
||||||
|
console.error("Unexpected message nametag", err.expectedNametag, err.actualNametag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("could not obtain next message");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async initiatorHandshake(): Promise<[NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]> {
|
||||||
|
// The handshake initiator writes a Waku2 payload v2 containing the handshake message
|
||||||
|
// and the (encrypted) transport message
|
||||||
|
// The message is sent with a messageNametag equal to the one received through the QR code
|
||||||
|
let hsStep = this.handshake.stepHandshake({
|
||||||
|
transportMessage: this.myCommittedStaticKey,
|
||||||
|
messageNametag: this.qrMessageNameTag,
|
||||||
|
});
|
||||||
|
|
||||||
|
// We prepare a message from initiator's payload2
|
||||||
|
// At this point wakuMsg is sent over the Waku network to receiver content topic
|
||||||
|
let encoder = new NoiseHandshakeEncoder(this.contentTopic, hsStep);
|
||||||
|
await this.sender.publish(encoder, {});
|
||||||
|
|
||||||
|
// We generate an authorization code using the handshake state
|
||||||
|
// this check has to be confirmed with a user interaction, comparing auth codes in both ends
|
||||||
|
this.eventEmitter.emit("authCodeGenerated", this.handshake.genAuthcode());
|
||||||
|
|
||||||
|
const confirmed = await this.isAuthCodeConfirmed();
|
||||||
|
if (!confirmed) {
|
||||||
|
throw new Error("authcode is not confirmed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd step
|
||||||
|
// <- sB, eAsB {r}
|
||||||
|
hsStep = await this.executeReadStepWithNextMessage(this.contentTopic, this.handshake.hs.toMessageNametag());
|
||||||
|
|
||||||
|
if (!this.handshake.hs.rs) throw new Error("invalid handshake state");
|
||||||
|
|
||||||
|
// Initiator further checks if receiver's commitment opens to receiver's static key received
|
||||||
|
const expectedReceiverCommittedStaticKey = commitPublicKey(this.handshake.hs.rs, hsStep.transportMessage);
|
||||||
|
if (!uint8ArrayEquals(expectedReceiverCommittedStaticKey, this.qr.committedStaticKey)) {
|
||||||
|
throw new Error("expected committed static key does not match the receiver actual committed static key");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3rd step
|
||||||
|
// -> sA, sAeB, sAsB {s}
|
||||||
|
// Similarly as in first step, the initiator writes a Waku2 payload containing the handshake message and the (encrypted) transport message
|
||||||
|
hsStep = this.handshake.stepHandshake({
|
||||||
|
transportMessage: this.randomFixLenVal,
|
||||||
|
messageNametag: this.handshake.hs.toMessageNametag(),
|
||||||
|
});
|
||||||
|
|
||||||
|
encoder = new NoiseHandshakeEncoder(this.contentTopic, hsStep);
|
||||||
|
await this.sender.publish(encoder, {});
|
||||||
|
|
||||||
|
// Secure Transfer Phase
|
||||||
|
const hsResult = this.handshake.finalizeHandshake();
|
||||||
|
|
||||||
|
this.eventEmitter.emit("pairingComplete");
|
||||||
|
|
||||||
|
return this.getSecureCodec(hsResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async receiverHandshake(): Promise<[NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]> {
|
||||||
|
// Subscribe to the contact content topic
|
||||||
|
const decoder = new NoiseHandshakeDecoder(this.contentTopic);
|
||||||
|
this.receiver.subscribe(decoder);
|
||||||
|
|
||||||
|
// the received reads the initiator's payloads, and returns the (decrypted) transport message the initiator sent
|
||||||
|
// Note that the received verifies if the received payloadV2 has the expected messageNametag set
|
||||||
|
let hsStep = await this.executeReadStepWithNextMessage(this.contentTopic, this.qrMessageNameTag);
|
||||||
|
|
||||||
|
const initiatorCommittedStaticKey = new Uint8Array(hsStep.transportMessage);
|
||||||
|
|
||||||
|
this.eventEmitter.emit("authCodeGenerated", this.handshake.genAuthcode());
|
||||||
|
|
||||||
|
const confirmed = await this.isAuthCodeConfirmed();
|
||||||
|
if (!confirmed) {
|
||||||
|
throw new Error("authcode is not confirmed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd step
|
||||||
|
// <- sB, eAsB {r}
|
||||||
|
// Receiver writes and returns a payload
|
||||||
|
hsStep = this.handshake.stepHandshake({
|
||||||
|
transportMessage: this.randomFixLenVal,
|
||||||
|
messageNametag: this.handshake.hs.toMessageNametag(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// We prepare a Waku message from receiver's payload2
|
||||||
|
const encoder = new NoiseHandshakeEncoder(this.contentTopic, hsStep);
|
||||||
|
await this.sender.publish(encoder, {});
|
||||||
|
|
||||||
|
// 3rd step
|
||||||
|
// -> sA, sAeB, sAsB {s}
|
||||||
|
|
||||||
|
// The receiver reads the initiator's payload sent by the initiator
|
||||||
|
hsStep = await this.executeReadStepWithNextMessage(this.contentTopic, this.handshake.hs.toMessageNametag());
|
||||||
|
|
||||||
|
if (!this.handshake.hs.rs) throw new Error("invalid handshake state");
|
||||||
|
|
||||||
|
// The receiver further checks if the initiator's commitment opens to the initiator's static key received
|
||||||
|
const expectedInitiatorCommittedStaticKey = commitPublicKey(this.handshake.hs.rs, hsStep.transportMessage);
|
||||||
|
if (!uint8ArrayEquals(expectedInitiatorCommittedStaticKey, initiatorCommittedStaticKey)) {
|
||||||
|
throw new Error("expected committed static key does not match the initiator actual committed static key");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure Transfer Phase
|
||||||
|
const hsResult = this.handshake.finalizeHandshake();
|
||||||
|
|
||||||
|
this.eventEmitter.emit("pairingComplete");
|
||||||
|
|
||||||
|
return this.getSecureCodec(hsResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSecureCodec(hsResult: HandshakeResult): [NoiseSecureTransferEncoder, NoiseSecureTransferDecoder] {
|
||||||
|
const secureEncoder = new NoiseSecureTransferEncoder(this.contentTopic, hsResult);
|
||||||
|
const secureDecoder = new NoiseSecureTransferDecoder(this.contentTopic, hsResult);
|
||||||
|
|
||||||
|
return [secureEncoder, secureDecoder];
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(timeoutMs = 30000): Promise<[NoiseSecureTransferEncoder, NoiseSecureTransferDecoder]> {
|
||||||
|
if (this.started) {
|
||||||
|
throw new Error("pairing already executed. Create new pairing object");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.started = true;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// Limit QR exposure to 30s
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
reject(new Error("pairing has timed out"));
|
||||||
|
this.eventEmitter.emit("pairingTimeout");
|
||||||
|
}, timeoutMs);
|
||||||
|
|
||||||
|
const handshakeFn = this.initiator ? this.initiatorHandshake : this.receiverHandshake;
|
||||||
|
handshakeFn
|
||||||
|
.bind(this)()
|
||||||
|
.then(
|
||||||
|
(response) => resolve(response),
|
||||||
|
(err) => reject(new Error(err))
|
||||||
|
)
|
||||||
|
.finally(() => clearTimeout(timer));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -118,7 +118,7 @@ export const NoiseHandshakePatterns = {
|
|||||||
|
|
||||||
// Supported Protocol ID for PayloadV2 objects
|
// Supported Protocol ID for PayloadV2 objects
|
||||||
// Protocol IDs are defined according to https://rfc.vac.dev/spec/35/#specification
|
// Protocol IDs are defined according to https://rfc.vac.dev/spec/35/#specification
|
||||||
export const PayloadV2ProtocolIDs = {
|
export const PayloadV2ProtocolIDs: { [id: string]: number } = {
|
||||||
"": 0,
|
"": 0,
|
||||||
Noise_K1K1_25519_ChaChaPoly_SHA256: 10,
|
Noise_K1K1_25519_ChaChaPoly_SHA256: 10,
|
||||||
Noise_XK1_25519_ChaChaPoly_SHA256: 11,
|
Noise_XK1_25519_ChaChaPoly_SHA256: 11,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { equals as uint8ArrayEquals } from "uint8arrays/equals";
|
|||||||
|
|
||||||
import { MessageNametag } from "./@types/handshake";
|
import { MessageNametag } from "./@types/handshake";
|
||||||
import { ChachaPolyTagLen, Curve25519KeySize, hashSHA256 } from "./crypto";
|
import { ChachaPolyTagLen, Curve25519KeySize, hashSHA256 } from "./crypto";
|
||||||
|
import { PayloadV2ProtocolIDs } from "./patterns";
|
||||||
import { NoisePublicKey } from "./publickey";
|
import { NoisePublicKey } from "./publickey";
|
||||||
import { readUIntLE, writeUIntLE } from "./utils";
|
import { readUIntLE, writeUIntLE } from "./utils";
|
||||||
|
|
||||||
@ -217,8 +218,12 @@ export class PayloadV2 {
|
|||||||
i += MessageNametagLength;
|
i += MessageNametagLength;
|
||||||
|
|
||||||
// We read the Protocol ID
|
// We read the Protocol ID
|
||||||
// TODO: when the list of supported protocol ID is defined, check if read protocol ID is supported
|
|
||||||
const protocolId = payload[i];
|
const protocolId = payload[i];
|
||||||
|
const protocolName = Object.keys(PayloadV2ProtocolIDs).find((key) => PayloadV2ProtocolIDs[key] === protocolId);
|
||||||
|
if (protocolName === undefined) {
|
||||||
|
throw new Error("protocolId not found");
|
||||||
|
}
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
// We read the Handshake Message length (1 byte)
|
// We read the Handshake Message length (1 byte)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user