@chainsafe/libp2p-noise
Advanced tools
Comparing version 15.1.2 to 16.0.0
@@ -1,6 +0,1 @@ | ||
export declare class UnexpectedPeerError extends Error { | ||
code: string; | ||
constructor(message?: string); | ||
static readonly code = "ERR_UNEXPECTED_PEER"; | ||
} | ||
export declare class InvalidCryptoExchangeError extends Error { | ||
@@ -7,0 +2,0 @@ code: string; |
@@ -1,9 +0,1 @@ | ||
export class UnexpectedPeerError extends Error { | ||
code; | ||
constructor(message = 'Unexpected Peer') { | ||
super(message); | ||
this.code = UnexpectedPeerError.code; | ||
} | ||
static code = 'ERR_UNEXPECTED_PEER'; | ||
} | ||
export class InvalidCryptoExchangeError extends Error { | ||
@@ -10,0 +2,0 @@ code; |
@@ -0,4 +1,41 @@ | ||
/** | ||
* @packageDocumentation | ||
* | ||
* This repository contains TypeScript implementation of noise protocol, an encryption protocol used in libp2p. | ||
* | ||
* ## Usage | ||
* | ||
* Install with `yarn add @chainsafe/libp2p-noise` or `npm i @chainsafe/libp2p-noise`. | ||
* | ||
* Example of using default noise configuration and passing it to the libp2p config: | ||
* | ||
* ```ts | ||
* import {createLibp2p} from "libp2p" | ||
* import {noise} from "@chainsafe/libp2p-noise" | ||
* | ||
* //custom noise configuration, pass it instead of `noise()` | ||
* //x25519 private key | ||
* const n = noise({ staticNoiseKey }); | ||
* | ||
* const libp2p = await createLibp2p({ | ||
* connectionEncrypters: [noise()], | ||
* //... other options | ||
* }) | ||
* ``` | ||
* | ||
* See the [NoiseInit](https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/noise.ts#L22-L30) interface for noise configuration options. | ||
* | ||
* ## API | ||
* | ||
* This module exposes an implementation of the [ConnectionEncrypter](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.ConnectionEncrypter.html) interface. | ||
* | ||
* ## Bring your own crypto | ||
* | ||
* You can provide a custom crypto implementation (instead of the default, based on [@noble](https://paulmillr.com/noble/)) by adding a `crypto` field to the init argument passed to the `Noise` factory. | ||
* | ||
* The implementation must conform to the `ICryptoInterface`, defined in <https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/crypto.ts> | ||
*/ | ||
import type { NoiseInit } from './noise.js'; | ||
import type { NoiseExtensions } from './proto/payload.js'; | ||
import type { ComponentLogger, ConnectionEncrypter, Metrics, PeerId } from '@libp2p/interface'; | ||
import type { ComponentLogger, ConnectionEncrypter, Metrics, PeerId, PrivateKey } from '@libp2p/interface'; | ||
export type { ICryptoInterface } from './crypto.js'; | ||
@@ -8,2 +45,3 @@ export { pureJsCrypto } from './crypto/js.js'; | ||
peerId: PeerId; | ||
privateKey: PrivateKey; | ||
logger: ComponentLogger; | ||
@@ -10,0 +48,0 @@ metrics?: Metrics; |
@@ -0,1 +1,38 @@ | ||
/** | ||
* @packageDocumentation | ||
* | ||
* This repository contains TypeScript implementation of noise protocol, an encryption protocol used in libp2p. | ||
* | ||
* ## Usage | ||
* | ||
* Install with `yarn add @chainsafe/libp2p-noise` or `npm i @chainsafe/libp2p-noise`. | ||
* | ||
* Example of using default noise configuration and passing it to the libp2p config: | ||
* | ||
* ```ts | ||
* import {createLibp2p} from "libp2p" | ||
* import {noise} from "@chainsafe/libp2p-noise" | ||
* | ||
* //custom noise configuration, pass it instead of `noise()` | ||
* //x25519 private key | ||
* const n = noise({ staticNoiseKey }); | ||
* | ||
* const libp2p = await createLibp2p({ | ||
* connectionEncrypters: [noise()], | ||
* //... other options | ||
* }) | ||
* ``` | ||
* | ||
* See the [NoiseInit](https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/noise.ts#L22-L30) interface for noise configuration options. | ||
* | ||
* ## API | ||
* | ||
* This module exposes an implementation of the [ConnectionEncrypter](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.ConnectionEncrypter.html) interface. | ||
* | ||
* ## Bring your own crypto | ||
* | ||
* You can provide a custom crypto implementation (instead of the default, based on [@noble](https://paulmillr.com/noble/)) by adding a `crypto` field to the init argument passed to the `Noise` factory. | ||
* | ||
* The implementation must conform to the `ICryptoInterface`, defined in <https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/crypto.ts> | ||
*/ | ||
import { Noise } from './noise.js'; | ||
@@ -2,0 +39,0 @@ export { pureJsCrypto } from './crypto/js.js'; |
@@ -1,2 +0,2 @@ | ||
import { type MultiaddrConnection, type SecuredConnection, type PeerId, serviceCapabilities } from '@libp2p/interface'; | ||
import { serviceCapabilities } from '@libp2p/interface'; | ||
import { type ICryptoInterface } from './crypto.js'; | ||
@@ -6,2 +6,3 @@ import type { NoiseComponents } from './index.js'; | ||
import type { ICrypto, INoiseConnection } from './types.js'; | ||
import type { MultiaddrConnection, SecuredConnection, PeerId } from '@libp2p/interface'; | ||
import type { Duplex } from 'it-stream-types'; | ||
@@ -32,5 +33,6 @@ import type { Uint8ArrayList } from 'uint8arraylist'; | ||
* | ||
* @param localPeer - PeerId of the receiving peer | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer. | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
@@ -41,9 +43,9 @@ secureOutbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(connection: Stream, options?: { | ||
}): Promise<SecuredConnection<Stream, NoiseExtensions>>; | ||
secureOutbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>; | ||
/** | ||
* Decrypt incoming data (handshake as responder). | ||
* | ||
* @param localPeer - PeerId of the receiving peer. | ||
* @param connection - streaming iterable duplex that will be encrypted. | ||
* @param remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades. | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
@@ -54,3 +56,2 @@ secureInbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(connection: Stream, options?: { | ||
}): Promise<SecuredConnection<Stream, NoiseExtensions>>; | ||
secureInbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>>; | ||
/** | ||
@@ -65,10 +66,3 @@ * Perform XX handshake as initiator. | ||
private createSecureConnection; | ||
/** | ||
* Detect call signature in `libp2p@1.x.x` or `libp2p@2.x.x` style. | ||
* | ||
* TODO: remove this after `libp2p@2.x.x` is released and only support the | ||
* newer style | ||
*/ | ||
private parseArgs; | ||
} | ||
//# sourceMappingURL=noise.d.ts.map |
@@ -1,4 +0,4 @@ | ||
import { unmarshalPrivateKey } from '@libp2p/crypto/keys'; | ||
import { CodeError, serviceCapabilities, isPeerId } from '@libp2p/interface'; | ||
import { peerIdFromKeys } from '@libp2p/peer-id'; | ||
import { publicKeyFromProtobuf } from '@libp2p/crypto/keys'; | ||
import { serviceCapabilities } from '@libp2p/interface'; | ||
import { peerIdFromPublicKey } from '@libp2p/peer-id'; | ||
import { decode } from 'it-length-prefixed'; | ||
@@ -46,4 +46,11 @@ import { lpStream } from 'it-length-prefixed-stream'; | ||
]; | ||
async secureOutbound(...args) { | ||
const { localPeer, connection, remotePeer, signal } = this.parseArgs(args); | ||
/** | ||
* Encrypt outgoing data to the remote party (handshake as initiator) | ||
* | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
async secureOutbound(connection, options) { | ||
const wrappedConnection = lpStream(connection, { | ||
@@ -54,21 +61,22 @@ lengthEncoder: uint16BEEncode, | ||
}); | ||
if (!localPeer.privateKey) { | ||
throw new CodeError('local peerId does not contain private key', 'ERR_NO_PRIVATE_KEY'); | ||
} | ||
const privateKey = await unmarshalPrivateKey(localPeer.privateKey); | ||
const remoteIdentityKey = remotePeer?.publicKey; | ||
const handshake = await this.performHandshakeInitiator(wrappedConnection, privateKey, remoteIdentityKey, { | ||
signal | ||
}); | ||
const handshake = await this.performHandshakeInitiator(wrappedConnection, this.components.privateKey, options?.remotePeer?.publicKey, options); | ||
const conn = await this.createSecureConnection(wrappedConnection, handshake); | ||
connection.source = conn.source; | ||
connection.sink = conn.sink; | ||
const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey); | ||
return { | ||
conn: connection, | ||
remoteExtensions: handshake.payload.extensions, | ||
remotePeer: await peerIdFromKeys(handshake.payload.identityKey) | ||
remotePeer: peerIdFromPublicKey(publicKey) | ||
}; | ||
} | ||
async secureInbound(...args) { | ||
const { localPeer, connection, remotePeer, signal } = this.parseArgs(args); | ||
/** | ||
* Decrypt incoming data (handshake as responder). | ||
* | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
async secureInbound(connection, options) { | ||
const wrappedConnection = lpStream(connection, { | ||
@@ -79,17 +87,11 @@ lengthEncoder: uint16BEEncode, | ||
}); | ||
if (!localPeer.privateKey) { | ||
throw new CodeError('local peerId does not contain private key', 'ERR_NO_PRIVATE_KEY'); | ||
} | ||
const privateKey = await unmarshalPrivateKey(localPeer.privateKey); | ||
const remoteIdentityKey = remotePeer?.publicKey; | ||
const handshake = await this.performHandshakeResponder(wrappedConnection, privateKey, remoteIdentityKey, { | ||
signal | ||
}); | ||
const handshake = await this.performHandshakeResponder(wrappedConnection, this.components.privateKey, options?.remotePeer?.publicKey, options); | ||
const conn = await this.createSecureConnection(wrappedConnection, handshake); | ||
connection.source = conn.source; | ||
connection.sink = conn.sink; | ||
const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey); | ||
return { | ||
conn: connection, | ||
remoteExtensions: handshake.payload.extensions, | ||
remotePeer: await peerIdFromKeys(handshake.payload.identityKey) | ||
remotePeer: peerIdFromPublicKey(publicKey) | ||
}; | ||
@@ -126,5 +128,3 @@ } | ||
*/ | ||
async performHandshakeResponder(connection, | ||
// TODO: pass private key in noise constructor via Components | ||
privateKey, remoteIdentityKey, options) { | ||
async performHandshakeResponder(connection, privateKey, remoteIdentityKey, options) { | ||
let result; | ||
@@ -163,30 +163,3 @@ try { | ||
} | ||
/** | ||
* Detect call signature in `libp2p@1.x.x` or `libp2p@2.x.x` style. | ||
* | ||
* TODO: remove this after `libp2p@2.x.x` is released and only support the | ||
* newer style | ||
*/ | ||
parseArgs(args) { | ||
// if the first argument is a peer id, we're using the libp2p@1.x.x style | ||
if (isPeerId(args[0])) { | ||
return { | ||
localPeer: args[0], | ||
connection: args[1], | ||
remotePeer: args[2] | ||
}; | ||
} | ||
else { | ||
// handle upcoming changes in libp2p@2.x.x where the first argument is the | ||
// connection and the second is optionally the remote peer | ||
// @see https://github.com/libp2p/js-libp2p/pull/2304 | ||
return { | ||
localPeer: this.components.peerId, | ||
connection: args[0], | ||
remotePeer: args[1]?.remotePeer, | ||
signal: args[1]?.signal | ||
}; | ||
} | ||
} | ||
} | ||
//# sourceMappingURL=noise.js.map |
import type { Nonce } from './nonce'; | ||
import type { NoiseExtensions, NoiseHandshakePayload } from './proto/payload'; | ||
import type { ConnectionEncrypter, Logger, PrivateKey } from '@libp2p/interface'; | ||
import type { ConnectionEncrypter, Logger, PrivateKey, PublicKey } from '@libp2p/interface'; | ||
import type { LengthPrefixedStream } from 'it-length-prefixed-stream'; | ||
@@ -23,3 +23,3 @@ import type { Uint8ArrayList } from 'uint8arraylist'; | ||
s: KeyPair; | ||
remoteIdentityKey?: Uint8Array | Uint8ArrayList; | ||
remoteIdentityKey?: PublicKey; | ||
extensions?: NoiseExtensions; | ||
@@ -26,0 +26,0 @@ } |
@@ -0,7 +1,7 @@ | ||
import { type PrivateKey, type PublicKey } from '@libp2p/interface'; | ||
import { type Uint8ArrayList } from 'uint8arraylist'; | ||
import { type NoiseExtensions, NoiseHandshakePayload } from './proto/payload.js'; | ||
import type { PrivateKey } from '@libp2p/interface'; | ||
export declare function createHandshakePayload(privateKey: PrivateKey, staticPublicKey: Uint8Array | Uint8ArrayList, extensions?: NoiseExtensions): Promise<Uint8Array | Uint8ArrayList>; | ||
export declare function decodeHandshakePayload(payloadBytes: Uint8Array | Uint8ArrayList, remoteStaticKey?: Uint8Array | Uint8ArrayList, remoteIdentityKey?: Uint8Array | Uint8ArrayList): Promise<NoiseHandshakePayload>; | ||
export declare function decodeHandshakePayload(payloadBytes: Uint8Array | Uint8ArrayList, remoteStaticKey?: Uint8Array | Uint8ArrayList, remoteIdentityKey?: PublicKey): Promise<NoiseHandshakePayload>; | ||
export declare function getSignaturePayload(publicKey: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -1,7 +0,6 @@ | ||
import { unmarshalPublicKey } from '@libp2p/crypto/keys'; | ||
import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys'; | ||
import { UnexpectedPeerError } from '@libp2p/interface'; | ||
import {} from 'uint8arraylist'; | ||
import { equals, toString } from 'uint8arrays'; | ||
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'; | ||
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'; | ||
import { UnexpectedPeerError } from './errors.js'; | ||
import { NoiseHandshakePayload } from './proto/payload.js'; | ||
@@ -11,3 +10,3 @@ export async function createHandshakePayload(privateKey, staticPublicKey, extensions) { | ||
return NoiseHandshakePayload.encode({ | ||
identityKey: privateKey.public.bytes, | ||
identityKey: publicKeyToProtobuf(privateKey.publicKey), | ||
identitySig, | ||
@@ -20,7 +19,5 @@ extensions | ||
const payload = NoiseHandshakePayload.decode(payloadBytes); | ||
if (remoteIdentityKey) { | ||
const remoteIdentityKeyBytes = remoteIdentityKey.subarray(); | ||
if (!equals(remoteIdentityKeyBytes, payload.identityKey)) { | ||
throw new Error(`Payload identity key ${toString(payload.identityKey, 'hex')} does not match expected remote identity key ${toString(remoteIdentityKeyBytes, 'hex')}`); | ||
} | ||
const publicKey = publicKeyFromProtobuf(payload.identityKey); | ||
if (remoteIdentityKey?.equals(publicKey) === false) { | ||
throw new Error(`Payload identity key ${publicKey} does not match expected remote identity key ${remoteIdentityKey}`); | ||
} | ||
@@ -31,3 +28,2 @@ if (!remoteStaticKey) { | ||
const signaturePayload = getSignaturePayload(remoteStaticKey); | ||
const publicKey = unmarshalPublicKey(payload.identityKey); | ||
if (!(await publicKey.verify(signaturePayload, payload.identitySig))) { | ||
@@ -34,0 +30,0 @@ throw new Error('Invalid payload signature'); |
128
package.json
{ | ||
"name": "@chainsafe/libp2p-noise", | ||
"version": "15.1.2", | ||
"version": "16.0.0", | ||
"description": "Noise libp2p handshake for js-libp2p", | ||
"author": "ChainSafe <info@chainsafe.io>", | ||
@@ -14,2 +15,6 @@ "license": "Apache-2.0 OR MIT", | ||
}, | ||
"publishConfig": { | ||
"access": "public", | ||
"provenance": true | ||
}, | ||
"keywords": [ | ||
@@ -20,6 +25,2 @@ "crypto", | ||
], | ||
"engines": { | ||
"node": ">=16.0.0", | ||
"npm": ">=7.0.0" | ||
}, | ||
"type": "module", | ||
@@ -42,2 +43,3 @@ "types": "./dist/src/index.d.ts", | ||
"parserOptions": { | ||
"project": true, | ||
"sourceType": "module" | ||
@@ -56,2 +58,87 @@ }, | ||
}, | ||
"release": { | ||
"branches": [ | ||
"master" | ||
], | ||
"plugins": [ | ||
[ | ||
"@semantic-release/commit-analyzer", | ||
{ | ||
"preset": "conventionalcommits", | ||
"releaseRules": [ | ||
{ | ||
"breaking": true, | ||
"release": "major" | ||
}, | ||
{ | ||
"revert": true, | ||
"release": "patch" | ||
}, | ||
{ | ||
"type": "feat", | ||
"release": "minor" | ||
}, | ||
{ | ||
"type": "fix", | ||
"release": "patch" | ||
}, | ||
{ | ||
"type": "docs", | ||
"release": "patch" | ||
}, | ||
{ | ||
"type": "test", | ||
"release": "patch" | ||
}, | ||
{ | ||
"type": "deps", | ||
"release": "patch" | ||
}, | ||
{ | ||
"scope": "no-release", | ||
"release": false | ||
} | ||
] | ||
} | ||
], | ||
[ | ||
"@semantic-release/release-notes-generator", | ||
{ | ||
"preset": "conventionalcommits", | ||
"presetConfig": { | ||
"types": [ | ||
{ | ||
"type": "feat", | ||
"section": "Features" | ||
}, | ||
{ | ||
"type": "fix", | ||
"section": "Bug Fixes" | ||
}, | ||
{ | ||
"type": "chore", | ||
"section": "Trivial Changes" | ||
}, | ||
{ | ||
"type": "docs", | ||
"section": "Documentation" | ||
}, | ||
{ | ||
"type": "deps", | ||
"section": "Dependencies" | ||
}, | ||
{ | ||
"type": "test", | ||
"section": "Tests" | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"@semantic-release/changelog", | ||
"@semantic-release/npm", | ||
"@semantic-release/github", | ||
"@semantic-release/git" | ||
] | ||
}, | ||
"scripts": { | ||
@@ -71,3 +158,4 @@ "bench": "node benchmarks/benchmark.js", | ||
"proto:gen": "protons ./src/proto/payload.proto", | ||
"prepublish": "npm run build" | ||
"prepublish": "npm run build", | ||
"release": "aegir release" | ||
}, | ||
@@ -77,5 +165,5 @@ "dependencies": { | ||
"@chainsafe/as-sha256": "^0.4.1", | ||
"@libp2p/crypto": "^4.0.0", | ||
"@libp2p/interface": "^1.5.0", | ||
"@libp2p/peer-id": "^4.0.0", | ||
"@libp2p/crypto": "^5.0.0", | ||
"@libp2p/interface": "^2.0.0", | ||
"@libp2p/peer-id": "^5.0.0", | ||
"@noble/ciphers": "^0.6.0", | ||
@@ -89,3 +177,3 @@ "@noble/curves": "^1.1.0", | ||
"it-stream-types": "^2.0.1", | ||
"protons-runtime": "^5.0.0", | ||
"protons-runtime": "^5.5.0", | ||
"uint8arraylist": "^2.4.3", | ||
@@ -96,10 +184,9 @@ "uint8arrays": "^5.0.0", | ||
"devDependencies": { | ||
"@chainsafe/libp2p-yamux": "^6.0.1", | ||
"@libp2p/daemon-client": "^8.0.1", | ||
"@libp2p/daemon-server": "^7.0.1", | ||
"@libp2p/interface-compliance-tests": "^5.0.5", | ||
"@libp2p/interop": "^12.1.0", | ||
"@libp2p/logger": "^4.0.0", | ||
"@libp2p/peer-id-factory": "^4.0.4", | ||
"@libp2p/tcp": "^9.0.0", | ||
"@chainsafe/libp2p-yamux": "^7.0.0", | ||
"@libp2p/daemon-client": "^9.0.0", | ||
"@libp2p/daemon-server": "^8.0.0", | ||
"@libp2p/interface-compliance-tests": "^6.0.0", | ||
"@libp2p/interop": "^13.0.0", | ||
"@libp2p/logger": "^5.0.0", | ||
"@libp2p/tcp": "^10.0.0", | ||
"@multiformats/multiaddr": "^12.1.0", | ||
@@ -113,6 +200,7 @@ "@types/sinon": "^17.0.1", | ||
"it-byte-stream": "^1.0.0", | ||
"libp2p": "^1.0.8", | ||
"libp2p": "^2.0.0", | ||
"mkdirp": "^3.0.0", | ||
"multiformats": "^13.2.2", | ||
"p-defer": "^4.0.0", | ||
"protons": "^7.0.0", | ||
"protons": "^7.6.0", | ||
"sinon": "^18.0.0" | ||
@@ -119,0 +207,0 @@ }, |
@@ -1,2 +0,2 @@ | ||
# js-libp2p-noise | ||
# @chainsafe/libp2p-noise | ||
@@ -13,11 +13,25 @@  | ||
[](https://twitter.com/ChainSafeth) | ||
[](https://discord.gg/Q6A3YA2) | ||
[](https://discord.gg/Q6A3YA2) | ||
> Noise libp2p handshake for js-libp2p | ||
# About | ||
<!-- | ||
!IMPORTANT! | ||
Everything in this README between "# About" and "# Install" is automatically | ||
generated and will be overwritten the next time the doc generator is run. | ||
To make changes to this section, please update the @packageDocumentation section | ||
of src/index.js or src/index.ts | ||
To experiment with formatting, please run "npm run docs" from the root of this | ||
repo and examine the changes made. | ||
--> | ||
This repository contains TypeScript implementation of noise protocol, an encryption protocol used in libp2p. | ||
##### Warning: Even though this package works in browser, it will bundle around 600Kb (200Kb gzipped) of code | ||
https://bundlephobia.com/result?p=@chainsafe/libp2p-noise@latest | ||
## Usage | ||
@@ -29,3 +43,3 @@ | ||
```js | ||
```ts | ||
import {createLibp2p} from "libp2p" | ||
@@ -39,3 +53,3 @@ import {noise} from "@chainsafe/libp2p-noise" | ||
const libp2p = await createLibp2p({ | ||
connectionEncryption: [noise()], | ||
connectionEncrypters: [noise()], | ||
//... other options | ||
@@ -45,25 +59,41 @@ }) | ||
See the [NoiseInit](https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/noise.ts#L29-L38) interface for noise configuration options. | ||
See the [NoiseInit](https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/noise.ts#L22-L30) interface for noise configuration options. | ||
## API | ||
This module exposes an implementation of the [ConnectionEncrypter](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.connection_encrypter.ConnectionEncrypter.html) interface. | ||
This module exposes an implementation of the [ConnectionEncrypter](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.ConnectionEncrypter.html) interface. | ||
## Bring your own crypto | ||
You can provide a custom crypto implementation (instead of the default, based on [@noble](https://paulmillr.com/noble/)) by adding a `crypto` field to the init argument passed to the `Noise` factory. | ||
You can provide a custom crypto implementation (instead of the default, based on [@noble](https://paulmillr.com/noble/)) by adding a `crypto` field to the init argument passed to the `Noise` factory. | ||
The implementation must conform to the `ICryptoInterface`, defined in https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/crypto.ts | ||
The implementation must conform to the `ICryptoInterface`, defined in <https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/crypto.ts> | ||
## Contribute | ||
# Install | ||
Feel free to join in. All welcome. Open an issue! | ||
```console | ||
$ npm i @chainsafe/libp2p-noise | ||
``` | ||
[](https://github.com/ipfs/community/blob/master/contributing.md) | ||
## Browser `<script>` tag | ||
## License | ||
Loading this module through a script tag will make its exports available as `ChainsafeLibp2pNoise` in the global namespace. | ||
```html | ||
<script src="https://unpkg.com/@chainsafe/libp2p-noise/dist/index.min.js"></script> | ||
``` | ||
# API Docs | ||
- <https://ChainSafe.github.io/js-libp2p-noise> | ||
# License | ||
Licensed under either of | ||
* Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / http://www.apache.org/licenses/LICENSE-2.0) | ||
* MIT ([LICENSE-MIT](LICENSE-MIT) / http://opensource.org/licenses/MIT) | ||
- Apache 2.0, ([LICENSE-APACHE](https://github.com/ChainSafe/js-libp2p-noise/LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>) | ||
- MIT ([LICENSE-MIT](https://github.com/ChainSafe/js-libp2p-noise/LICENSE-MIT) / <http://opensource.org/licenses/MIT>) | ||
# Contribution | ||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. |
@@ -1,12 +0,1 @@ | ||
export class UnexpectedPeerError extends Error { | ||
public code: string | ||
constructor (message = 'Unexpected Peer') { | ||
super(message) | ||
this.code = UnexpectedPeerError.code | ||
} | ||
static readonly code = 'ERR_UNEXPECTED_PEER' | ||
} | ||
export class InvalidCryptoExchangeError extends Error { | ||
@@ -13,0 +2,0 @@ public code: string |
@@ -0,5 +1,43 @@ | ||
/** | ||
* @packageDocumentation | ||
* | ||
* This repository contains TypeScript implementation of noise protocol, an encryption protocol used in libp2p. | ||
* | ||
* ## Usage | ||
* | ||
* Install with `yarn add @chainsafe/libp2p-noise` or `npm i @chainsafe/libp2p-noise`. | ||
* | ||
* Example of using default noise configuration and passing it to the libp2p config: | ||
* | ||
* ```ts | ||
* import {createLibp2p} from "libp2p" | ||
* import {noise} from "@chainsafe/libp2p-noise" | ||
* | ||
* //custom noise configuration, pass it instead of `noise()` | ||
* //x25519 private key | ||
* const n = noise({ staticNoiseKey }); | ||
* | ||
* const libp2p = await createLibp2p({ | ||
* connectionEncrypters: [noise()], | ||
* //... other options | ||
* }) | ||
* ``` | ||
* | ||
* See the [NoiseInit](https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/noise.ts#L22-L30) interface for noise configuration options. | ||
* | ||
* ## API | ||
* | ||
* This module exposes an implementation of the [ConnectionEncrypter](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.ConnectionEncrypter.html) interface. | ||
* | ||
* ## Bring your own crypto | ||
* | ||
* You can provide a custom crypto implementation (instead of the default, based on [@noble](https://paulmillr.com/noble/)) by adding a `crypto` field to the init argument passed to the `Noise` factory. | ||
* | ||
* The implementation must conform to the `ICryptoInterface`, defined in <https://github.com/ChainSafe/js-libp2p-noise/blob/master/src/crypto.ts> | ||
*/ | ||
import { Noise } from './noise.js' | ||
import type { NoiseInit } from './noise.js' | ||
import type { NoiseExtensions } from './proto/payload.js' | ||
import type { ComponentLogger, ConnectionEncrypter, Metrics, PeerId } from '@libp2p/interface' | ||
import type { ComponentLogger, ConnectionEncrypter, Metrics, PeerId, PrivateKey } from '@libp2p/interface' | ||
export type { ICryptoInterface } from './crypto.js' | ||
@@ -10,2 +48,3 @@ export { pureJsCrypto } from './crypto/js.js' | ||
peerId: PeerId | ||
privateKey: PrivateKey | ||
logger: ComponentLogger | ||
@@ -12,0 +51,0 @@ metrics?: Metrics |
@@ -1,4 +0,4 @@ | ||
import { unmarshalPrivateKey } from '@libp2p/crypto/keys' | ||
import { type MultiaddrConnection, type SecuredConnection, type PeerId, CodeError, type PrivateKey, serviceCapabilities, isPeerId, type AbortOptions } from '@libp2p/interface' | ||
import { peerIdFromKeys } from '@libp2p/peer-id' | ||
import { publicKeyFromProtobuf } from '@libp2p/crypto/keys' | ||
import { serviceCapabilities } from '@libp2p/interface' | ||
import { peerIdFromPublicKey } from '@libp2p/peer-id' | ||
import { decode } from 'it-length-prefixed' | ||
@@ -19,2 +19,3 @@ import { lpStream, type LengthPrefixedStream } from 'it-length-prefixed-stream' | ||
import type { HandshakeResult, ICrypto, INoiseConnection, KeyPair } from './types.js' | ||
import type { MultiaddrConnection, SecuredConnection, PeerId, PrivateKey, PublicKey, AbortOptions } from '@libp2p/interface' | ||
import type { Duplex } from 'it-stream-types' | ||
@@ -72,11 +73,8 @@ import type { Uint8ArrayList } from 'uint8arraylist' | ||
* | ||
* @param localPeer - PeerId of the receiving peer | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer. | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, options?: { remotePeer?: PeerId, signal?: AbortSignal }): Promise<SecuredConnection<Stream, NoiseExtensions>> | ||
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>> | ||
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (...args: any[]): Promise<SecuredConnection<Stream, NoiseExtensions>> { | ||
const { localPeer, connection, remotePeer, signal } = this.parseArgs<Stream>(args) | ||
public async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, options?: { remotePeer?: PeerId, signal?: AbortSignal }): Promise<SecuredConnection<Stream, NoiseExtensions>> { | ||
const wrappedConnection = lpStream( | ||
@@ -91,15 +89,7 @@ connection, | ||
if (!localPeer.privateKey) { | ||
throw new CodeError('local peerId does not contain private key', 'ERR_NO_PRIVATE_KEY') | ||
} | ||
const privateKey = await unmarshalPrivateKey(localPeer.privateKey) | ||
const remoteIdentityKey = remotePeer?.publicKey | ||
const handshake = await this.performHandshakeInitiator( | ||
wrappedConnection, | ||
privateKey, | ||
remoteIdentityKey, { | ||
signal | ||
} | ||
this.components.privateKey, | ||
options?.remotePeer?.publicKey, | ||
options | ||
) | ||
@@ -111,6 +101,8 @@ const conn = await this.createSecureConnection(wrappedConnection, handshake) | ||
const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey) | ||
return { | ||
conn: connection, | ||
remoteExtensions: handshake.payload.extensions, | ||
remotePeer: await peerIdFromKeys(handshake.payload.identityKey) | ||
remotePeer: peerIdFromPublicKey(publicKey) | ||
} | ||
@@ -122,11 +114,8 @@ } | ||
* | ||
* @param localPeer - PeerId of the receiving peer. | ||
* @param connection - streaming iterable duplex that will be encrypted. | ||
* @param remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades. | ||
* @param connection - streaming iterable duplex that will be encrypted | ||
* @param options | ||
* @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer | ||
* @param options.signal - Used to abort the operation | ||
*/ | ||
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, options?: { remotePeer?: PeerId, signal?: AbortSignal }): Promise<SecuredConnection<Stream, NoiseExtensions>> | ||
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localPeer: PeerId, connection: Stream, remotePeer?: PeerId): Promise<SecuredConnection<Stream, NoiseExtensions>> | ||
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (...args: any[]): Promise<SecuredConnection<Stream, NoiseExtensions>> { | ||
const { localPeer, connection, remotePeer, signal } = this.parseArgs<Stream>(args) | ||
public async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (connection: Stream, options?: { remotePeer?: PeerId, signal?: AbortSignal }): Promise<SecuredConnection<Stream, NoiseExtensions>> { | ||
const wrappedConnection = lpStream( | ||
@@ -141,15 +130,7 @@ connection, | ||
if (!localPeer.privateKey) { | ||
throw new CodeError('local peerId does not contain private key', 'ERR_NO_PRIVATE_KEY') | ||
} | ||
const privateKey = await unmarshalPrivateKey(localPeer.privateKey) | ||
const remoteIdentityKey = remotePeer?.publicKey | ||
const handshake = await this.performHandshakeResponder( | ||
wrappedConnection, | ||
privateKey, | ||
remoteIdentityKey, { | ||
signal | ||
} | ||
this.components.privateKey, | ||
options?.remotePeer?.publicKey, | ||
options | ||
) | ||
@@ -161,6 +142,8 @@ const conn = await this.createSecureConnection(wrappedConnection, handshake) | ||
const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey) | ||
return { | ||
conn: connection, | ||
remoteExtensions: handshake.payload.extensions, | ||
remotePeer: await peerIdFromKeys(handshake.payload.identityKey) | ||
remotePeer: peerIdFromPublicKey(publicKey) | ||
} | ||
@@ -176,3 +159,3 @@ } | ||
privateKey: PrivateKey, | ||
remoteIdentityKey?: Uint8Array | Uint8ArrayList, | ||
remoteIdentityKey?: PublicKey, | ||
options?: AbortOptions | ||
@@ -206,5 +189,4 @@ ): Promise<HandshakeResult> { | ||
connection: LengthPrefixedStream, | ||
// TODO: pass private key in noise constructor via Components | ||
privateKey: PrivateKey, | ||
remoteIdentityKey?: Uint8Array | Uint8ArrayList, | ||
remoteIdentityKey?: PublicKey, | ||
options?: AbortOptions | ||
@@ -252,29 +234,2 @@ ): Promise<HandshakeResult> { | ||
} | ||
/** | ||
* Detect call signature in `libp2p@1.x.x` or `libp2p@2.x.x` style. | ||
* | ||
* TODO: remove this after `libp2p@2.x.x` is released and only support the | ||
* newer style | ||
*/ | ||
private parseArgs <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (args: any[]): { localPeer: PeerId, connection: Stream, remotePeer?: PeerId, signal?: AbortSignal } { | ||
// if the first argument is a peer id, we're using the libp2p@1.x.x style | ||
if (isPeerId(args[0])) { | ||
return { | ||
localPeer: args[0], | ||
connection: args[1], | ||
remotePeer: args[2] | ||
} | ||
} else { | ||
// handle upcoming changes in libp2p@2.x.x where the first argument is the | ||
// connection and the second is optionally the remote peer | ||
// @see https://github.com/libp2p/js-libp2p/pull/2304 | ||
return { | ||
localPeer: this.components.peerId, | ||
connection: args[0], | ||
remotePeer: args[1]?.remotePeer, | ||
signal: args[1]?.signal | ||
} | ||
} | ||
} | ||
} |
import type { Nonce } from './nonce' | ||
import type { NoiseExtensions, NoiseHandshakePayload } from './proto/payload' | ||
import type { ConnectionEncrypter, Logger, PrivateKey } from '@libp2p/interface' | ||
import type { ConnectionEncrypter, Logger, PrivateKey, PublicKey } from '@libp2p/interface' | ||
import type { LengthPrefixedStream } from 'it-length-prefixed-stream' | ||
@@ -25,3 +25,3 @@ import type { Uint8ArrayList } from 'uint8arraylist' | ||
s: KeyPair | ||
remoteIdentityKey?: Uint8Array | Uint8ArrayList | ||
remoteIdentityKey?: PublicKey | ||
extensions?: NoiseExtensions | ||
@@ -28,0 +28,0 @@ } |
@@ -1,9 +0,7 @@ | ||
import { unmarshalPublicKey } from '@libp2p/crypto/keys' | ||
import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys' | ||
import { UnexpectedPeerError, type PrivateKey, type PublicKey } from '@libp2p/interface' | ||
import { type Uint8ArrayList } from 'uint8arraylist' | ||
import { equals, toString } from 'uint8arrays' | ||
import { concat as uint8ArrayConcat } from 'uint8arrays/concat' | ||
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' | ||
import { UnexpectedPeerError } from './errors.js' | ||
import { type NoiseExtensions, NoiseHandshakePayload } from './proto/payload.js' | ||
import type { PrivateKey } from '@libp2p/interface' | ||
@@ -18,3 +16,3 @@ export async function createHandshakePayload ( | ||
return NoiseHandshakePayload.encode({ | ||
identityKey: privateKey.public.bytes, | ||
identityKey: publicKeyToProtobuf(privateKey.publicKey), | ||
identitySig, | ||
@@ -28,11 +26,10 @@ extensions | ||
remoteStaticKey?: Uint8Array | Uint8ArrayList, | ||
remoteIdentityKey?: Uint8Array | Uint8ArrayList | ||
remoteIdentityKey?: PublicKey | ||
): Promise<NoiseHandshakePayload> { | ||
try { | ||
const payload = NoiseHandshakePayload.decode(payloadBytes) | ||
if (remoteIdentityKey) { | ||
const remoteIdentityKeyBytes = remoteIdentityKey.subarray() | ||
if (!equals(remoteIdentityKeyBytes, payload.identityKey)) { | ||
throw new Error(`Payload identity key ${toString(payload.identityKey, 'hex')} does not match expected remote identity key ${toString(remoteIdentityKeyBytes, 'hex')}`) | ||
} | ||
const publicKey = publicKeyFromProtobuf(payload.identityKey) | ||
if (remoteIdentityKey?.equals(publicKey) === false) { | ||
throw new Error(`Payload identity key ${publicKey} does not match expected remote identity key ${remoteIdentityKey}`) | ||
} | ||
@@ -45,3 +42,2 @@ | ||
const signaturePayload = getSignaturePayload(remoteStaticKey) | ||
const publicKey = unmarshalPublicKey(payload.identityKey) | ||
@@ -48,0 +44,0 @@ if (!(await publicKey.verify(signaturePayload, payload.identitySig))) { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
96
96
356400
3569
+ Added@libp2p/crypto@5.0.13(transitive)
+ Added@libp2p/interface@2.6.1(transitive)
+ Added@libp2p/peer-id@5.0.14(transitive)
- Removed@libp2p/crypto@4.1.9(transitive)
- Removed@libp2p/interface@1.7.0(transitive)
- Removed@libp2p/peer-id@4.2.4(transitive)
Updated@libp2p/crypto@^5.0.0
Updated@libp2p/interface@^2.0.0
Updated@libp2p/peer-id@^5.0.0
Updatedprotons-runtime@^5.5.0