Skip to content

Commit ed3e16b

Browse files
MarcoPolompetrunic
andauthored
feat!: add Noise Extensions and update deps (#215)
* feat: Add Noise Extensions * Remove remoteEarlyData and lint:fix * Add workaround for proto2 issue * Use new interface defs * chore: fix lint * fix: proto file to pass interop tests Co-authored-by: Marin Petrunic <marin.petrunic@gmail.com>
1 parent a363679 commit ed3e16b

10 files changed

Lines changed: 188 additions & 132 deletions

File tree

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
},
6969
"dependencies": {
7070
"@libp2p/crypto": "^1.0.0",
71-
"@libp2p/interface-connection-encrypter": "^2.0.1",
71+
"@libp2p/interface-connection-encrypter": "^3.0.0",
7272
"@libp2p/interface-keys": "^1.0.2",
7373
"@libp2p/interface-peer-id": "^1.0.2",
7474
"@libp2p/logger": "^2.0.0",
@@ -88,8 +88,8 @@
8888
},
8989
"devDependencies": {
9090
"@libp2p/daemon-client": "^3.0.1",
91-
"@libp2p/daemon-server": "^3.0.0",
92-
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.1",
91+
"@libp2p/daemon-server": "^3.0.1",
92+
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.3",
9393
"@libp2p/interop": "^3.0.1",
9494
"@libp2p/mplex": "^5.0.0",
9595
"@libp2p/peer-id-factory": "^1.0.8",
@@ -100,7 +100,7 @@
100100
"execa": "^6.1.0",
101101
"go-libp2p": "^0.0.6",
102102
"iso-random-stream": "^2.0.2",
103-
"libp2p": "0.39.2",
103+
"libp2p": "0.39.4",
104104
"mkdirp": "^1.0.4",
105105
"p-defer": "^4.0.0",
106106
"protons": "^5.1.0",
@@ -111,4 +111,4 @@
111111
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
112112
"util": false
113113
}
114-
}
114+
}

src/@types/handshake-interface.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { PeerId } from '@libp2p/interface-peer-id'
22
import type { bytes } from './basic.js'
33
import type { NoiseSession } from './handshake.js'
4+
import type { NoiseExtensions } from '../proto/payload.js'
45

56
export interface IHandshake {
67
session: NoiseSession
78
remotePeer: PeerId
8-
remoteEarlyData: bytes
9+
remoteExtensions: NoiseExtensions
910
encrypt: (plaintext: bytes, session: NoiseSession) => bytes
10-
decrypt: (ciphertext: bytes, session: NoiseSession) => {plaintext: bytes, valid: boolean}
11+
decrypt: (ciphertext: bytes, session: NoiseSession) => { plaintext: bytes, valid: boolean }
1112
}

src/@types/libp2p.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import type { ConnectionEncrypter } from '@libp2p/interface-connection-encrypter'
2-
import type { bytes, bytes32 } from './basic.js'
2+
import type { NoiseExtensions } from '../proto/payload.js'
3+
import type { bytes32 } from './basic.js'
34

45
export interface KeyPair {
56
publicKey: bytes32
67
privateKey: bytes32
78
}
89

9-
export interface INoiseConnection extends ConnectionEncrypter {
10-
remoteEarlyData?: () => bytes
11-
}
10+
export interface INoiseConnection extends ConnectionEncrypter<NoiseExtensions> {}

src/handshake-xx.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ import {
2121
getPeerIdFromPayload,
2222
verifySignedPayload
2323
} from './utils.js'
24+
import type { NoiseExtensions } from './proto/payload.js'
2425

2526
export class XXHandshake implements IHandshake {
2627
public isInitiator: boolean
2728
public session: NoiseSession
2829
public remotePeer!: PeerId
29-
public remoteEarlyData: bytes
30+
public remoteExtensions: NoiseExtensions = { webtransportCerthashes: [] }
3031

3132
protected payload: bytes
3233
protected connection: ProtobufStream
@@ -55,7 +56,6 @@ export class XXHandshake implements IHandshake {
5556
}
5657
this.xx = handshake ?? new XX(crypto)
5758
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
58-
this.remoteEarlyData = new Uint8Array(0)
5959
}
6060

6161
// stage 0
@@ -97,7 +97,7 @@ export class XXHandshake implements IHandshake {
9797
const decodedPayload = decodePayload(plaintext)
9898
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
9999
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
100-
this.setRemoteEarlyData(decodedPayload.data)
100+
this.setRemoteNoiseExtension(decodedPayload.extensions)
101101
} catch (e) {
102102
const err = e as Error
103103
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
@@ -132,7 +132,7 @@ export class XXHandshake implements IHandshake {
132132
const decodedPayload = decodePayload(plaintext)
133133
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
134134
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
135-
this.setRemoteEarlyData(decodedPayload.data)
135+
this.setRemoteNoiseExtension(decodedPayload.extensions)
136136
} catch (e) {
137137
const err = e as Error
138138
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
@@ -147,7 +147,7 @@ export class XXHandshake implements IHandshake {
147147
return this.xx.encryptWithAd(cs, new Uint8Array(0), plaintext)
148148
}
149149

150-
public decrypt (ciphertext: Uint8Array, session: NoiseSession): {plaintext: bytes, valid: boolean} {
150+
public decrypt (ciphertext: Uint8Array, session: NoiseSession): { plaintext: bytes, valid: boolean } {
151151
const cs = this.getCS(session, false)
152152

153153
return this.xx.decryptWithAd(cs, new Uint8Array(0), ciphertext)
@@ -169,9 +169,9 @@ export class XXHandshake implements IHandshake {
169169
}
170170
}
171171

172-
protected setRemoteEarlyData (data: Uint8Array|null|undefined): void {
173-
if (data) {
174-
this.remoteEarlyData = data
172+
protected setRemoteNoiseExtension (e: NoiseExtensions | null | undefined): void {
173+
if (e) {
174+
this.remoteExtensions = e
175175
}
176176
}
177177
}

src/noise.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { decryptStream, encryptStream } from './crypto/streaming.js'
1515
import { uint16BEDecode, uint16BEEncode } from './encoder.js'
1616
import { XXHandshake } from './handshake-xx.js'
1717
import { getPayload } from './utils.js'
18+
import type { NoiseExtensions } from './proto/payload.js'
1819

1920
interface HandshakeParams {
2021
connection: ProtobufStream
@@ -29,15 +30,15 @@ export class Noise implements INoiseConnection {
2930

3031
private readonly prologue: Uint8Array
3132
private readonly staticKeys: KeyPair
32-
private readonly earlyData?: bytes
33+
private readonly extensions?: NoiseExtensions
3334

3435
/**
3536
* @param {bytes} staticNoiseKey - x25519 private key, reuse for faster handshakes
36-
* @param {bytes} earlyData
37+
* @param {NoiseExtensions} extensions
3738
*/
38-
constructor (staticNoiseKey?: bytes, earlyData?: bytes, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
39-
this.earlyData = earlyData ?? new Uint8Array(0)
39+
constructor (staticNoiseKey?: bytes, extensions?: NoiseExtensions, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
4040
this.crypto = crypto
41+
this.extensions = extensions
4142

4243
if (staticNoiseKey) {
4344
// accepts x25519 private key of length 32
@@ -56,7 +57,7 @@ export class Noise implements INoiseConnection {
5657
* @param {PeerId} remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer.
5758
* @returns {Promise<SecuredConnection>}
5859
*/
59-
public async secureOutbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection> {
60+
public async secureOutbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> {
6061
const wrappedConnection = pbStream(
6162
connection,
6263
{
@@ -75,7 +76,7 @@ export class Noise implements INoiseConnection {
7576

7677
return {
7778
conn,
78-
remoteEarlyData: handshake.remoteEarlyData,
79+
remoteExtensions: handshake.remoteExtensions,
7980
remotePeer: handshake.remotePeer
8081
}
8182
}
@@ -88,7 +89,7 @@ export class Noise implements INoiseConnection {
8889
* @param {PeerId} remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades.
8990
* @returns {Promise<SecuredConnection>}
9091
*/
91-
public async secureInbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection> {
92+
public async secureInbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> {
9293
const wrappedConnection = pbStream(
9394
connection,
9495
{
@@ -107,8 +108,8 @@ export class Noise implements INoiseConnection {
107108

108109
return {
109110
conn,
110-
remoteEarlyData: handshake.remoteEarlyData,
111-
remotePeer: handshake.remotePeer
111+
remotePeer: handshake.remotePeer,
112+
remoteExtensions: handshake.remoteExtensions
112113
}
113114
}
114115

@@ -119,7 +120,7 @@ export class Noise implements INoiseConnection {
119120
* @param {HandshakeParams} params
120121
*/
121122
private async performHandshake (params: HandshakeParams): Promise<IHandshake> {
122-
const payload = await getPayload(params.localPeer, this.staticKeys.publicKey, this.earlyData)
123+
const payload = await getPayload(params.localPeer, this.staticKeys.publicKey, this.extensions)
123124

124125
// run XX handshake
125126
return await this.performXXHandshake(params, payload)

src/proto/payload.proto

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
syntax = "proto3";
2-
package pb;
32

4-
message NoiseHandshakePayload {
5-
bytes identity_key = 1;
6-
bytes identity_sig = 2;
7-
bytes data = 3;
3+
message NoiseExtensions {
4+
repeated bytes webtransport_certhashes = 1;
85
}
6+
7+
message NoiseHandshakePayload {
8+
bytes identity_key = 1;
9+
bytes identity_sig = 2;
10+
optional NoiseExtensions extensions = 4;
11+
}

0 commit comments

Comments
 (0)