Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,33 +82,33 @@
"it-pb-stream": "^2.0.2",
"it-pipe": "^2.0.3",
"it-stream-types": "^1.0.4",
"protons-runtime": "^3.1.0",
"protons-runtime": "^4.0.1",
"uint8arraylist": "^2.3.2",
"uint8arrays": "^3.1.0"
"uint8arrays": "^4.0.2"
},
"devDependencies": {
"@libp2p/daemon-client": "^3.0.1",
"@libp2p/daemon-server": "^3.0.1",
"@libp2p/interface-connection-encrypter-compliance-tests": "^2.0.3",
"@libp2p/interface-connection-encrypter-compliance-tests": "^3.0.0",
"@libp2p/interop": "^3.0.1",
"@libp2p/mplex": "^5.0.0",
"@libp2p/mplex": "^7.0.0",
"@libp2p/peer-id-factory": "^1.0.8",
"@libp2p/tcp": "^4.1.0",
"@libp2p/tcp": "^5.0.1",
"@multiformats/multiaddr": "^11.0.3",
"aegir": "^37.3.0",
"benchmark": "^2.1.4",
"execa": "^6.1.0",
"go-libp2p": "^0.0.6",
"iso-random-stream": "^2.0.2",
"libp2p": "0.39.4",
"libp2p": "^0.40.0",
"mkdirp": "^1.0.4",
"p-defer": "^4.0.0",
"protons": "^5.1.0",
"protons": "^6.0.0",
"sinon": "^14.0.0",
"util": "^0.12.4"
},
"browser": {
"./dist/src/alloc-unsafe.js": "./dist/src/alloc-unsafe-browser.js",
"util": false
}
}
}
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import type { ConnectionEncrypter } from '@libp2p/interface-connection-encrypter'
import { Noise } from './noise.js'
import type { NoiseInit } from './noise.js'
export * from './crypto.js'
export * from './crypto/stablelib.js'
export * from './noise.js'

export function noise (init: NoiseInit = {}): () => ConnectionEncrypter {
return () => new Noise(init)
}
20 changes: 14 additions & 6 deletions src/noise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ interface HandshakeParams {
remotePeer?: PeerId
}

export interface NoiseInit {
/**
* x25519 private key, reuse for faster handshakes
*/
staticNoiseKey?: bytes
extensions?: NoiseExtensions
crypto?: ICryptoInterface
prologueBytes?: Uint8Array
}

export class Noise implements INoiseConnection {
public protocol = '/noise'
public crypto: ICryptoInterface
Expand All @@ -32,12 +42,10 @@ export class Noise implements INoiseConnection {
private readonly staticKeys: KeyPair
private readonly extensions?: NoiseExtensions

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

this.crypto = crypto ?? stablelib
this.extensions = extensions

if (staticNoiseKey) {
Expand Down
50 changes: 20 additions & 30 deletions src/proto/payload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable import/export */
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/no-namespace */
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */

import { encodeMessage, decodeMessage, message } from 'protons-runtime'
import type { Uint8ArrayList } from 'uint8arraylist'
Expand All @@ -14,22 +16,20 @@ export namespace NoiseExtensions {

export const codec = (): Codec<NoiseExtensions> => {
if (_codec == null) {
_codec = message<NoiseExtensions>((obj, writer, opts = {}) => {
_codec = message<NoiseExtensions>((obj, w, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
w.fork()
}

if (obj.webtransportCerthashes != null) {
for (const value of obj.webtransportCerthashes) {
writer.uint32(10)
writer.bytes(value)
w.uint32(10)
w.bytes(value)
}
} else {
throw new Error('Protocol error: required field "webtransportCerthashes" was not found in object')
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
w.ldelim()
}
}, (reader, length) => {
const obj: any = {
Expand Down Expand Up @@ -78,32 +78,30 @@ export namespace NoiseHandshakePayload {

export const codec = (): Codec<NoiseHandshakePayload> => {
if (_codec == null) {
_codec = message<NoiseHandshakePayload>((obj, writer, opts = {}) => {
_codec = message<NoiseHandshakePayload>((obj, w, opts = {}) => {
if (opts.lengthDelimited !== false) {
writer.fork()
w.fork()
}

if (obj.identityKey != null) {
writer.uint32(10)
writer.bytes(obj.identityKey)
} else {
throw new Error('Protocol error: required field "identityKey" was not found in object')
if (opts.writeDefaults === true || (obj.identityKey != null && obj.identityKey.byteLength > 0)) {
w.uint32(10)
w.bytes(obj.identityKey)
}

if (obj.identitySig != null) {
writer.uint32(18)
writer.bytes(obj.identitySig)
} else {
throw new Error('Protocol error: required field "identitySig" was not found in object')
if (opts.writeDefaults === true || (obj.identitySig != null && obj.identitySig.byteLength > 0)) {
w.uint32(18)
w.bytes(obj.identitySig)
}

if (obj.extensions != null) {
writer.uint32(34)
NoiseExtensions.codec().encode(obj.extensions, writer)
w.uint32(34)
NoiseExtensions.codec().encode(obj.extensions, w, {
writeDefaults: false
})
}

if (opts.lengthDelimited !== false) {
writer.ldelim()
w.ldelim()
}
}, (reader, length) => {
const obj: any = {
Expand Down Expand Up @@ -132,14 +130,6 @@ export namespace NoiseHandshakePayload {
}
}

if (obj.identityKey == null) {
throw new Error('Protocol error: value for required field "identityKey" was not found in protobuf')
}

if (obj.identitySig == null) {
throw new Error('Protocol error: value for required field "identitySig" was not found in protobuf')
}

return obj
})
}
Expand Down
2 changes: 1 addition & 1 deletion test/compliance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import tests from '@libp2p/interface-connection-encrypter-compliance-tests'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'

describe('spec compliance tests', function () {
tests({
Expand Down
2 changes: 1 addition & 1 deletion test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'aegir/chai'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'

describe('Index', () => {
it('should expose class with tag and required functions', () => {
Expand Down
13 changes: 6 additions & 7 deletions test/interop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ import type { SpawnOptions, Daemon, DaemonFactory } from '@libp2p/interop'
import { createServer } from '@libp2p/daemon-server'
import { createClient } from '@libp2p/daemon-client'
import { createLibp2p, Libp2pOptions } from 'libp2p'
import { TCP } from '@libp2p/tcp'
import { tcp } from '@libp2p/tcp'
import { multiaddr } from '@multiformats/multiaddr'
import { path as p2pd } from 'go-libp2p'
import { execa } from 'execa'
import pDefer from 'p-defer'
import { logger } from '@libp2p/logger'
import { Mplex } from '@libp2p/mplex'
import { mplex } from '@libp2p/mplex'
import fs from 'fs'
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
import type { PeerId } from '@libp2p/interface-peer-id'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { Noise } from '../src/index.js'
import { noise } from '../src/index.js'

async function createGoPeer (options: SpawnOptions): Promise<Daemon> {
const controlPort = Math.floor(Math.random() * (50000 - 10000 + 1)) + 10000
Expand Down Expand Up @@ -76,10 +76,9 @@ async function createJsPeer (options: SpawnOptions): Promise<Daemon> {
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [new TCP()],
streamMuxers: [new Mplex()],
// @ts-expect-error libp2p options is still referencing the old connection encrypter interface https://github.com/libp2p/js-libp2p/pull/1402
connectionEncryption: [new Noise()]
transports: [tcp()],
streamMuxers: [mplex()],
connectionEncryption: [noise()]
}

const node = await createLibp2p(opts)
Expand Down
24 changes: 12 additions & 12 deletions test/noise.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { stablelib } from '../src/crypto/stablelib.js'
import { decode0, decode2, encode1, uint16BEDecode, uint16BEEncode } from '../src/encoder.js'
import { XX } from '../src/handshakes/xx.js'
import { XXHandshake } from '../src/handshake-xx.js'
import { Noise } from '../src/index.js'
import { Noise } from '../src/noise.js'
import { createHandshakePayload, getHandshakePayload, getPayload, signPayload } from '../src/utils.js'
import { createPeerIdsFromFixtures } from './fixtures/peer.js'
import { getKeyPairFromPeerId } from './utils.js'
Expand All @@ -32,8 +32,8 @@ describe('Noise', () => {

it('should communicate through encrypted streams without noise pipes', async () => {
try {
const noiseInit = new Noise(undefined, undefined)
const noiseResp = new Noise(undefined, undefined)
const noiseInit = new Noise({ staticNoiseKey: undefined, extensions: undefined })
const noiseResp = new Noise({ staticNoiseKey: undefined, extensions: undefined })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand All @@ -53,7 +53,7 @@ describe('Noise', () => {
})

it('should test that secureOutbound is spec compliant', async () => {
const noiseInit = new Noise(undefined, undefined)
const noiseInit = new Noise({ staticNoiseKey: undefined })
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()

const [outbound, { wrapped, handshake }] = await Promise.all([
Expand Down Expand Up @@ -108,8 +108,8 @@ describe('Noise', () => {
it('should test large payloads', async function () {
this.timeout(10000)
try {
const noiseInit = new Noise(undefined, undefined)
const noiseResp = new Noise(undefined, undefined)
const noiseInit = new Noise({ staticNoiseKey: undefined })
const noiseResp = new Noise({ staticNoiseKey: undefined })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand All @@ -133,9 +133,9 @@ describe('Noise', () => {
it('should working without remote peer provided in incoming connection', async () => {
try {
const staticKeysInitiator = stablelib.generateX25519KeyPair()
const noiseInit = new Noise(staticKeysInitiator.privateKey)
const noiseInit = new Noise({ staticNoiseKey: staticKeysInitiator.privateKey })
const staticKeysResponder = stablelib.generateX25519KeyPair()
const noiseResp = new Noise(staticKeysResponder.privateKey)
const noiseResp = new Noise({ staticNoiseKey: staticKeysResponder.privateKey })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand Down Expand Up @@ -166,10 +166,10 @@ describe('Noise', () => {
try {
const certhashInit = Buffer.from('certhash data from init')
const staticKeysInitiator = stablelib.generateX25519KeyPair()
const noiseInit = new Noise(staticKeysInitiator.privateKey, { webtransportCerthashes: [certhashInit] })
const noiseInit = new Noise({ staticNoiseKey: staticKeysInitiator.privateKey, extensions: { webtransportCerthashes: [certhashInit] } })
const staticKeysResponder = stablelib.generateX25519KeyPair()
const certhashResp = Buffer.from('certhash data from respon')
const noiseResp = new Noise(staticKeysResponder.privateKey, { webtransportCerthashes: [certhashResp] })
const noiseResp = new Noise({ staticNoiseKey: staticKeysResponder.privateKey, extensions: { webtransportCerthashes: [certhashResp] } })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand All @@ -187,8 +187,8 @@ describe('Noise', () => {

it('should accept a prologue', async () => {
try {
const noiseInit = new Noise(undefined, undefined, stablelib, Buffer.from('Some prologue'))
const noiseResp = new Noise(undefined, undefined, stablelib, Buffer.from('Some prologue'))
const noiseInit = new Noise({ staticNoiseKey: undefined, crypto: stablelib, prologueBytes: Buffer.from('Some prologue') })
const noiseResp = new Noise({ staticNoiseKey: undefined, crypto: stablelib, prologueBytes: Buffer.from('Some prologue') })

const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>()
const [outbound, inbound] = await Promise.all([
Expand Down