Skip to content

Commit

Permalink
feat!: add Noise Extensions and update deps (#215)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
MarcoPolo and mpetrunic authored Oct 6, 2022
1 parent a363679 commit ed3e16b
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 132 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
},
"dependencies": {
"@libp2p/crypto": "^1.0.0",
"@libp2p/interface-connection-encrypter": "^2.0.1",
"@libp2p/interface-connection-encrypter": "^3.0.0",
"@libp2p/interface-keys": "^1.0.2",
"@libp2p/interface-peer-id": "^1.0.2",
"@libp2p/logger": "^2.0.0",
Expand All @@ -88,8 +88,8 @@
},
"devDependencies": {
"@libp2p/daemon-client": "^3.0.1",
"@libp2p/daemon-server": "^3.0.0",
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.1",
"@libp2p/daemon-server": "^3.0.1",
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.3",
"@libp2p/interop": "^3.0.1",
"@libp2p/mplex": "^5.0.0",
"@libp2p/peer-id-factory": "^1.0.8",
Expand All @@ -100,7 +100,7 @@
"execa": "^6.1.0",
"go-libp2p": "^0.0.6",
"iso-random-stream": "^2.0.2",
"libp2p": "0.39.2",
"libp2p": "0.39.4",
"mkdirp": "^1.0.4",
"p-defer": "^4.0.0",
"protons": "^5.1.0",
Expand All @@ -111,4 +111,4 @@
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
"util": false
}
}
}
5 changes: 3 additions & 2 deletions src/@types/handshake-interface.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { PeerId } from '@libp2p/interface-peer-id'
import type { bytes } from './basic.js'
import type { NoiseSession } from './handshake.js'
import type { NoiseExtensions } from '../proto/payload.js'

export interface IHandshake {
session: NoiseSession
remotePeer: PeerId
remoteEarlyData: bytes
remoteExtensions: NoiseExtensions
encrypt: (plaintext: bytes, session: NoiseSession) => bytes
decrypt: (ciphertext: bytes, session: NoiseSession) => {plaintext: bytes, valid: boolean}
decrypt: (ciphertext: bytes, session: NoiseSession) => { plaintext: bytes, valid: boolean }
}
7 changes: 3 additions & 4 deletions src/@types/libp2p.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import type { ConnectionEncrypter } from '@libp2p/interface-connection-encrypter'
import type { bytes, bytes32 } from './basic.js'
import type { NoiseExtensions } from '../proto/payload.js'
import type { bytes32 } from './basic.js'

export interface KeyPair {
publicKey: bytes32
privateKey: bytes32
}

export interface INoiseConnection extends ConnectionEncrypter {
remoteEarlyData?: () => bytes
}
export interface INoiseConnection extends ConnectionEncrypter<NoiseExtensions> {}
16 changes: 8 additions & 8 deletions src/handshake-xx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import {
getPeerIdFromPayload,
verifySignedPayload
} from './utils.js'
import type { NoiseExtensions } from './proto/payload.js'

export class XXHandshake implements IHandshake {
public isInitiator: boolean
public session: NoiseSession
public remotePeer!: PeerId
public remoteEarlyData: bytes
public remoteExtensions: NoiseExtensions = { webtransportCerthashes: [] }

protected payload: bytes
protected connection: ProtobufStream
Expand Down Expand Up @@ -55,7 +56,6 @@ export class XXHandshake implements IHandshake {
}
this.xx = handshake ?? new XX(crypto)
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
this.remoteEarlyData = new Uint8Array(0)
}

// stage 0
Expand Down Expand Up @@ -97,7 +97,7 @@ export class XXHandshake implements IHandshake {
const decodedPayload = decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
this.setRemoteNoiseExtension(decodedPayload.extensions)
} catch (e) {
const err = e as Error
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
Expand Down Expand Up @@ -132,7 +132,7 @@ export class XXHandshake implements IHandshake {
const decodedPayload = decodePayload(plaintext)
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
this.setRemoteEarlyData(decodedPayload.data)
this.setRemoteNoiseExtension(decodedPayload.extensions)
} catch (e) {
const err = e as Error
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
Expand All @@ -147,7 +147,7 @@ export class XXHandshake implements IHandshake {
return this.xx.encryptWithAd(cs, new Uint8Array(0), plaintext)
}

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

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

protected setRemoteEarlyData (data: Uint8Array|null|undefined): void {
if (data) {
this.remoteEarlyData = data
protected setRemoteNoiseExtension (e: NoiseExtensions | null | undefined): void {
if (e) {
this.remoteExtensions = e
}
}
}
21 changes: 11 additions & 10 deletions src/noise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { decryptStream, encryptStream } from './crypto/streaming.js'
import { uint16BEDecode, uint16BEEncode } from './encoder.js'
import { XXHandshake } from './handshake-xx.js'
import { getPayload } from './utils.js'
import type { NoiseExtensions } from './proto/payload.js'

interface HandshakeParams {
connection: ProtobufStream
Expand All @@ -29,15 +30,15 @@ export class Noise implements INoiseConnection {

private readonly prologue: Uint8Array
private readonly staticKeys: KeyPair
private readonly earlyData?: bytes
private readonly extensions?: NoiseExtensions

/**
* @param {bytes} staticNoiseKey - x25519 private key, reuse for faster handshakes
* @param {bytes} earlyData
* @param {NoiseExtensions} extensions
*/
constructor (staticNoiseKey?: bytes, earlyData?: bytes, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
this.earlyData = earlyData ?? new Uint8Array(0)
constructor (staticNoiseKey?: bytes, extensions?: NoiseExtensions, crypto: ICryptoInterface = stablelib, prologueBytes?: Uint8Array) {
this.crypto = crypto
this.extensions = extensions

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

return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
remoteExtensions: handshake.remoteExtensions,
remotePeer: handshake.remotePeer
}
}
Expand All @@ -88,7 +89,7 @@ export class Noise implements INoiseConnection {
* @param {PeerId} remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades.
* @returns {Promise<SecuredConnection>}
*/
public async secureInbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection> {
public async secureInbound (localPeer: PeerId, connection: Duplex<Uint8Array>, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> {
const wrappedConnection = pbStream(
connection,
{
Expand All @@ -107,8 +108,8 @@ export class Noise implements INoiseConnection {

return {
conn,
remoteEarlyData: handshake.remoteEarlyData,
remotePeer: handshake.remotePeer
remotePeer: handshake.remotePeer,
remoteExtensions: handshake.remoteExtensions
}
}

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

// run XX handshake
return await this.performXXHandshake(params, payload)
Expand Down
13 changes: 8 additions & 5 deletions src/proto/payload.proto
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
syntax = "proto3";
package pb;

message NoiseHandshakePayload {
bytes identity_key = 1;
bytes identity_sig = 2;
bytes data = 3;
message NoiseExtensions {
repeated bytes webtransport_certhashes = 1;
}

message NoiseHandshakePayload {
bytes identity_key = 1;
bytes identity_sig = 2;
optional NoiseExtensions extensions = 4;
}
Loading

0 comments on commit ed3e16b

Please sign in to comment.