mirror of
https://github.com/fluencelabs/js-libp2p-noise
synced 2025-06-09 19:51:36 +00:00
Merge pull request #89 from hugomrdias/feat/new-aegir
fix: fix repo to use new aegir
This commit is contained in:
commit
81a70d1e4f
28
.aegir.js
Normal file
28
.aegir.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
/** @type {import('aegir').Options["build"]["config"]} */
|
||||||
|
const esbuild = {
|
||||||
|
inject: [path.join(__dirname, 'test/fixtures/node-globals.js')]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @type {import('aegir').PartialOptions} */
|
||||||
|
const config = {
|
||||||
|
tsRepo: true,
|
||||||
|
docs: {
|
||||||
|
entryPoint: "src/index.ts"
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
browser :{
|
||||||
|
config: {
|
||||||
|
buildConfig: esbuild
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
bundlesizeMax: '214KB',
|
||||||
|
config: esbuild
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = config
|
174
.github/workflows/ci.yml
vendored
Normal file
174
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Lint and Typecheck
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: yarn run build
|
||||||
|
- run: yarn run check
|
||||||
|
- uses: ipfs/aegir/actions/bundle-size@master
|
||||||
|
name: Check bundle size
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
test-node:
|
||||||
|
name: Test Nodejs
|
||||||
|
needs: check
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: [14]
|
||||||
|
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx aegir test -t node --bail --cov
|
||||||
|
- uses: codecov/codecov-action@v1
|
||||||
|
|
||||||
|
test-chrome:
|
||||||
|
name: Test Chrome
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx aegir test -t browser -t webworker --bail
|
||||||
|
|
||||||
|
test-firefox:
|
||||||
|
name: Test Firefox
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx aegir test -t browser -t webworker --bail -- --browser firefox
|
||||||
|
|
||||||
|
test-webkit:
|
||||||
|
name: Test Webkit
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: microsoft/playwright-github-action@v1
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx aegir test -t browser -t webworker --bail -- --browser webkit
|
||||||
|
|
||||||
|
test-electron-main:
|
||||||
|
name: Test Electron Main
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx xvfb-maybe aegir test -t electron-main --bail
|
||||||
|
|
||||||
|
test-electron-renderer:
|
||||||
|
name: Test Electron Renderer
|
||||||
|
needs: check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node }}
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
- run: yarn --prefer-offline --frozen-lockfile
|
||||||
|
- run: npx xvfb-maybe aegir test -t electron-renderer --bail
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@ node_modules/
|
|||||||
.nyc_output
|
.nyc_output
|
||||||
lib
|
lib
|
||||||
dist
|
dist
|
||||||
|
docs
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
58
.travis.yml
58
.travis.yml
@ -1,58 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
cache: yarn
|
|
||||||
stages:
|
|
||||||
- check
|
|
||||||
- test
|
|
||||||
- cov
|
|
||||||
|
|
||||||
env:
|
|
||||||
- YARN_GPG=no
|
|
||||||
|
|
||||||
node_js:
|
|
||||||
- '12'
|
|
||||||
- '14'
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
- osx
|
|
||||||
- windows
|
|
||||||
|
|
||||||
script: npx nyc -s yarn run test:node --bail
|
|
||||||
after_success:
|
|
||||||
- npm install -g travis-deploy-once
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then travis-deploy-once "npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov"; fi
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: check
|
|
||||||
script:
|
|
||||||
- yarn aegir dep-check
|
|
||||||
- yarn run lint
|
|
||||||
- yarn run build
|
|
||||||
|
|
||||||
- stage: test
|
|
||||||
name: chrome
|
|
||||||
addons:
|
|
||||||
chrome: stable
|
|
||||||
script: npx aegir test -t browser -t webworker --node true --ts
|
|
||||||
|
|
||||||
- stage: test
|
|
||||||
name: firefox
|
|
||||||
addons:
|
|
||||||
firefox: latest
|
|
||||||
script: npx aegir test -t browser -t webworker --ts --node true -- --browsers FirefoxHeadless
|
|
||||||
|
|
||||||
- stage: test
|
|
||||||
name: electron-main
|
|
||||||
os: osx
|
|
||||||
script:
|
|
||||||
- npx aegir test -t electron-main --bail --ts
|
|
||||||
|
|
||||||
- stage: test
|
|
||||||
name: electron-renderer
|
|
||||||
os: osx
|
|
||||||
script:
|
|
||||||
- npx aegir test -t electron-renderer --node true --bail --ts
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
39
package.json
39
package.json
@ -21,33 +21,33 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"bench": "node benchmarks/benchmark.js",
|
"bench": "node benchmarks/benchmark.js",
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"copy-dts": "copyfiles -u 1 \"src/**/*.d.ts\" dist/src",
|
"check": "aegir dep-check && aegir ts -p check",
|
||||||
"build": "aegir build --ts",
|
"build": "aegir build",
|
||||||
"postbuild": "yarn run copy-dts",
|
"lint": "aegir lint",
|
||||||
"lint": "aegir lint --ts",
|
"lint:fix": "aegir lint --fix",
|
||||||
"lint:fix": "aegir lint --ts --fix",
|
"pretest": "yarn run check",
|
||||||
"test": "aegir test --ts --node true",
|
"test": "aegir test",
|
||||||
"test:node": "aegir test -t node --ts",
|
"test:node": "aegir test -t node",
|
||||||
"test:browser": "aegir test -t browser --node true --ts",
|
"test:browser": "aegir test -t browser",
|
||||||
|
"docs": "aegir docs",
|
||||||
"proto:gen": "pbjs -t static-module -o ./src/proto/payload.js ./src/proto/payload.proto && pbts -o ./src/proto/payload.d.ts ./src/proto/payload.js && yarn run lint --fix"
|
"proto:gen": "pbjs -t static-module -o ./src/proto/payload.js ./src/proto/payload.proto && pbts -o ./src/proto/payload.d.ts ./src/proto/payload.js && yarn run lint --fix"
|
||||||
},
|
},
|
||||||
|
"browser": {
|
||||||
|
"util": false
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bl": "^2.1.0",
|
"@types/bl": "^2.1.0",
|
||||||
"@types/chai": "^4.2.4",
|
"aegir": "^31.0.0",
|
||||||
"@types/mocha": "^5.2.7",
|
|
||||||
"aegir": "28.2.0",
|
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
|
"buffer": "^5.7.1",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"copyfiles": "^2.4.1",
|
"events": "^3.2.0",
|
||||||
"eslint-config-ipfs": "^0.1.0",
|
|
||||||
"karma-mocha-webworker": "^1.3.0",
|
|
||||||
"microtime": "^3.0.0",
|
"microtime": "^3.0.0",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
"sinon": "^9.2.4"
|
"sinon": "^9.2.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypto": "^5.3.0",
|
"bcrypto": "^5.4.0",
|
||||||
"buffer": "^5.4.3",
|
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1",
|
||||||
"it-buffer": "^0.1.1",
|
"it-buffer": "^0.1.1",
|
||||||
"it-length-prefixed": "^3.0.0",
|
"it-length-prefixed": "^3.0.0",
|
||||||
@ -65,10 +65,13 @@
|
|||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "ipfs",
|
"extends": "ipfs",
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-unused-vars": "error"
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "warn",
|
||||||
|
"@typescript-eslint/strict-boolean-expressions": "off"
|
||||||
},
|
},
|
||||||
"ignorePatterns": [
|
"ignorePatterns": [
|
||||||
"src/proto/payload.js"
|
"src/proto/payload.js",
|
||||||
|
"test/fixtures/node-globals.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/@types/basic.d.ts
vendored
12
src/@types/basic.d.ts
vendored
@ -1,8 +1,8 @@
|
|||||||
import {Buffer} from 'buffer';
|
import { Buffer } from 'buffer'
|
||||||
|
|
||||||
export type bytes = Buffer;
|
export type bytes = Buffer
|
||||||
export type bytes32 = Buffer;
|
export type bytes32 = Buffer
|
||||||
export type bytes16 = Buffer;
|
export type bytes16 = Buffer
|
||||||
|
|
||||||
export type uint32 = number;
|
export type uint32 = number
|
||||||
export type uint64 = number;
|
export type uint64 = number
|
||||||
|
16
src/@types/handshake-interface.d.ts
vendored
16
src/@types/handshake-interface.d.ts
vendored
@ -1,11 +1,11 @@
|
|||||||
import {bytes} from "./basic";
|
import { bytes } from './basic'
|
||||||
import {NoiseSession} from "./handshake";
|
import { NoiseSession } from './handshake'
|
||||||
import PeerId from "peer-id";
|
import PeerId from 'peer-id'
|
||||||
|
|
||||||
export interface IHandshake {
|
export interface IHandshake {
|
||||||
session: NoiseSession;
|
session: NoiseSession
|
||||||
remotePeer: PeerId;
|
remotePeer: PeerId
|
||||||
remoteEarlyData: Buffer;
|
remoteEarlyData: Buffer
|
||||||
encrypt(plaintext: bytes, session: NoiseSession): bytes;
|
encrypt: (plaintext: bytes, session: NoiseSession) => bytes
|
||||||
decrypt(ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean};
|
decrypt: (ciphertext: bytes, session: NoiseSession) => {plaintext: bytes, valid: boolean}
|
||||||
}
|
}
|
||||||
|
62
src/@types/handshake.d.ts
vendored
62
src/@types/handshake.d.ts
vendored
@ -1,45 +1,45 @@
|
|||||||
import {bytes, bytes32, uint32, uint64} from "./basic";
|
import { bytes, bytes32, uint32, uint64 } from './basic'
|
||||||
import {KeyPair} from "./libp2p";
|
import { KeyPair } from './libp2p'
|
||||||
|
|
||||||
export type Hkdf = [bytes, bytes, bytes];
|
export type Hkdf = [bytes, bytes, bytes]
|
||||||
|
|
||||||
export type MessageBuffer = {
|
export interface MessageBuffer {
|
||||||
ne: bytes32;
|
ne: bytes32
|
||||||
ns: bytes;
|
ns: bytes
|
||||||
ciphertext: bytes;
|
ciphertext: bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CipherState = {
|
export interface CipherState {
|
||||||
k: bytes32;
|
k: bytes32
|
||||||
n: uint32;
|
n: uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SymmetricState = {
|
export interface SymmetricState {
|
||||||
cs: CipherState;
|
cs: CipherState
|
||||||
ck: bytes32; // chaining key
|
ck: bytes32 // chaining key
|
||||||
h: bytes32; // handshake hash
|
h: bytes32 // handshake hash
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HandshakeState = {
|
export interface HandshakeState {
|
||||||
ss: SymmetricState;
|
ss: SymmetricState
|
||||||
s: KeyPair;
|
s: KeyPair
|
||||||
e?: KeyPair;
|
e?: KeyPair
|
||||||
rs: bytes32;
|
rs: bytes32
|
||||||
re: bytes32;
|
re: bytes32
|
||||||
psk: bytes32;
|
psk: bytes32
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NoiseSession = {
|
export interface NoiseSession {
|
||||||
hs: HandshakeState;
|
hs: HandshakeState
|
||||||
h?: bytes32;
|
h?: bytes32
|
||||||
cs1?: CipherState;
|
cs1?: CipherState
|
||||||
cs2?: CipherState;
|
cs2?: CipherState
|
||||||
mc: uint64;
|
mc: uint64
|
||||||
i: boolean;
|
i: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INoisePayload {
|
export interface INoisePayload {
|
||||||
identityKey: bytes;
|
identityKey: bytes
|
||||||
identitySig: bytes;
|
identitySig: bytes
|
||||||
data: bytes;
|
data: bytes
|
||||||
}
|
}
|
||||||
|
30
src/@types/it-length-prefixed/index.d.ts
vendored
30
src/@types/it-length-prefixed/index.d.ts
vendored
@ -3,33 +3,33 @@ declare module 'it-length-prefixed' {
|
|||||||
import { Buffer } from 'buffer'
|
import { Buffer } from 'buffer'
|
||||||
|
|
||||||
interface LengthDecoderFunction {
|
interface LengthDecoderFunction {
|
||||||
(data: Buffer | BufferList): number;
|
(data: Buffer | BufferList): number
|
||||||
bytes: number;
|
bytes: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LengthEncoderFunction {
|
interface LengthEncoderFunction {
|
||||||
(value: number, target: Buffer, offset: number): number|Buffer;
|
(value: number, target: Buffer, offset: number): number|Buffer
|
||||||
bytes: number;
|
bytes: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Encoder {
|
interface Encoder {
|
||||||
(options?: Partial<{lengthEncoder: LengthEncoderFunction}>): AsyncGenerator<BufferList, Buffer>;
|
(options?: Partial<{lengthEncoder: LengthEncoderFunction}>): AsyncGenerator<BufferList, Buffer>
|
||||||
single: (chunk: Buffer, options?: Partial<{lengthEncoder: LengthEncoderFunction}>) => BufferList;
|
single: (chunk: Buffer, options?: Partial<{lengthEncoder: LengthEncoderFunction}>) => BufferList
|
||||||
MIN_POOL_SIZE: number;
|
MIN_POOL_SIZE: number
|
||||||
DEFAULT_POOL_SIZE: number;
|
DEFAULT_POOL_SIZE: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DecoderOptions {
|
interface DecoderOptions {
|
||||||
lengthDecoder: LengthDecoderFunction;
|
lengthDecoder: LengthDecoderFunction
|
||||||
maxLengthLength: number;
|
maxLengthLength: number
|
||||||
maxDataLength: number;
|
maxDataLength: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Decoder {
|
interface Decoder {
|
||||||
(options?: Partial<DecoderOptions>): AsyncGenerator<BufferList, BufferList>;
|
(options?: Partial<DecoderOptions>): AsyncGenerator<BufferList, BufferList>
|
||||||
fromReader: (reader: any, options?: Partial<DecoderOptions>) => BufferList;
|
fromReader: (reader: any, options?: Partial<DecoderOptions>) => BufferList
|
||||||
MAX_LENGTH_LENGTH: number;
|
MAX_LENGTH_LENGTH: number
|
||||||
MAX_DATA_LENGTH: number;
|
MAX_DATA_LENGTH: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encode: Encoder
|
export const encode: Encoder
|
||||||
|
8
src/@types/it-pair/index.d.ts
vendored
8
src/@types/it-pair/index.d.ts
vendored
@ -1,8 +1,8 @@
|
|||||||
declare module 'it-pair' {
|
declare module 'it-pair' {
|
||||||
export type Duplex = [Stream, Stream];
|
export type Duplex = [Stream, Stream]
|
||||||
|
|
||||||
type Stream = {
|
interface Stream {
|
||||||
sink(source: Iterable<any>): void;
|
sink: (source: Iterable<any>) => void
|
||||||
source: Record<string, any>;
|
source: Record<string, any>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/@types/libp2p.d.ts
vendored
25
src/@types/libp2p.d.ts
vendored
@ -1,20 +1,19 @@
|
|||||||
import { bytes, bytes32 } from "./basic";
|
import { bytes, bytes32 } from './basic'
|
||||||
import PeerId from "peer-id";
|
import PeerId from 'peer-id'
|
||||||
|
|
||||||
export type KeyPair = {
|
export interface KeyPair {
|
||||||
publicKey: bytes32;
|
publicKey: bytes32
|
||||||
privateKey: bytes32;
|
privateKey: bytes32
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INoiseConnection {
|
export interface INoiseConnection {
|
||||||
remoteEarlyData?(): bytes;
|
remoteEarlyData?: () => bytes
|
||||||
secureOutbound(localPeer: PeerId, insecure: any, remotePeer: PeerId): Promise<SecureOutbound>;
|
secureOutbound: (localPeer: PeerId, insecure: any, remotePeer: PeerId) => Promise<SecureOutbound>
|
||||||
secureInbound(localPeer: PeerId, insecure: any, remotePeer: PeerId): Promise<SecureOutbound>;
|
secureInbound: (localPeer: PeerId, insecure: any, remotePeer: PeerId) => Promise<SecureOutbound>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SecureOutbound = {
|
export interface SecureOutbound {
|
||||||
conn: any;
|
conn: any
|
||||||
remoteEarlyData: Buffer;
|
remoteEarlyData: Buffer
|
||||||
remotePeer: PeerId;
|
remotePeer: PeerId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { IHandshake } from './@types/handshake-interface'
|
|||||||
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from './constants'
|
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from './constants'
|
||||||
|
|
||||||
interface IReturnEncryptionWrapper {
|
interface IReturnEncryptionWrapper {
|
||||||
(source: Iterable<Uint8Array>): AsyncIterableIterator<Uint8Array>;
|
(source: Iterable<Uint8Array>): AsyncIterableIterator<Uint8Array>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns generator that encrypts payload from the user
|
// Returns generator that encrypts payload from the user
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import BufferList from 'bl'
|
import BufferList from 'bl'
|
||||||
|
|
||||||
export class FailedIKError extends Error {
|
export class FailedIKError extends Error {
|
||||||
public initialMsg: string|BufferList|Buffer;
|
public initialMsg: string|BufferList|Buffer
|
||||||
|
|
||||||
constructor (initialMsg: string|BufferList|Buffer, message?: string) {
|
constructor (initialMsg: string|BufferList|Buffer, message?: string) {
|
||||||
super(message)
|
super(message)
|
||||||
|
@ -19,16 +19,16 @@ import {
|
|||||||
import PeerId from 'peer-id'
|
import PeerId from 'peer-id'
|
||||||
|
|
||||||
export class IKHandshake implements IHandshake {
|
export class IKHandshake implements IHandshake {
|
||||||
public isInitiator: boolean;
|
public isInitiator: boolean
|
||||||
public session: NoiseSession;
|
public session: NoiseSession
|
||||||
public remotePeer!: PeerId;
|
public remotePeer!: PeerId
|
||||||
public remoteEarlyData: Buffer;
|
public remoteEarlyData: Buffer
|
||||||
|
|
||||||
private payload: bytes;
|
private readonly payload: bytes
|
||||||
private prologue: bytes32;
|
private readonly prologue: bytes32
|
||||||
private staticKeypair: KeyPair;
|
private readonly staticKeypair: KeyPair
|
||||||
private connection: WrappedConnection;
|
private readonly connection: WrappedConnection
|
||||||
private ik: IK;
|
private readonly ik: IK
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
isInitiator: boolean,
|
isInitiator: boolean,
|
||||||
@ -48,7 +48,7 @@ export class IKHandshake implements IHandshake {
|
|||||||
if (remotePeer) {
|
if (remotePeer) {
|
||||||
this.remotePeer = remotePeer
|
this.remotePeer = remotePeer
|
||||||
}
|
}
|
||||||
this.ik = handshake || new IK()
|
this.ik = handshake ?? new IK()
|
||||||
this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKey)
|
this.session = this.ik.initSession(this.isInitiator, this.prologue, this.staticKeypair, remoteStaticKey)
|
||||||
this.remoteEarlyData = Buffer.alloc(0)
|
this.remoteEarlyData = Buffer.alloc(0)
|
||||||
}
|
}
|
||||||
@ -79,9 +79,10 @@ export class IKHandshake implements IHandshake {
|
|||||||
logger('IK Stage 0 - Responder successfully verified payload!')
|
logger('IK Stage 0 - Responder successfully verified payload!')
|
||||||
logRemoteEphemeralKey(this.session.hs.re)
|
logRemoteEphemeralKey(this.session.hs.re)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
const err = e as Error
|
||||||
logger('Responder breaking up with IK handshake in stage 0.')
|
logger('Responder breaking up with IK handshake in stage 0.')
|
||||||
|
|
||||||
throw new FailedIKError(receivedMsg, `Error occurred while verifying initiator's signed payload: ${e.message}`)
|
throw new FailedIKError(receivedMsg, `Error occurred while verifying initiator's signed payload: ${err.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,8 +105,9 @@ export class IKHandshake implements IHandshake {
|
|||||||
logger('IK Stage 1 - Initiator successfully verified payload!')
|
logger('IK Stage 1 - Initiator successfully verified payload!')
|
||||||
logRemoteEphemeralKey(this.session.hs.re)
|
logRemoteEphemeralKey(this.session.hs.re)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
const err = e as Error
|
||||||
logger('Initiator breaking up with IK handshake in stage 1.')
|
logger('Initiator breaking up with IK handshake in stage 1.')
|
||||||
throw new FailedIKError(receivedMsg, `Error occurred while verifying responder's signed payload: ${e.message}`)
|
throw new FailedIKError(receivedMsg, `Error occurred while verifying responder's signed payload: ${err.message}`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger('IK Stage 1 - Responder sending message...')
|
logger('IK Stage 1 - Responder sending message...')
|
||||||
@ -117,7 +119,7 @@ export class IKHandshake implements IHandshake {
|
|||||||
logCipherState(this.session)
|
logCipherState(this.session)
|
||||||
}
|
}
|
||||||
|
|
||||||
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
|
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes, valid: boolean} {
|
||||||
const cs = this.getCS(session, false)
|
const cs = this.getCS(session, false)
|
||||||
return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
|
return this.ik.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ import { decode0, decode1 } from './encoder'
|
|||||||
import PeerId from 'peer-id'
|
import PeerId from 'peer-id'
|
||||||
|
|
||||||
export class XXFallbackHandshake extends XXHandshake {
|
export class XXFallbackHandshake extends XXHandshake {
|
||||||
private ephemeralKeys?: KeyPair;
|
private readonly ephemeralKeys?: KeyPair
|
||||||
private initialMsg: bytes;
|
private readonly initialMsg: bytes
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
isInitiator: boolean,
|
isInitiator: boolean,
|
||||||
@ -73,7 +73,8 @@ export class XXFallbackHandshake extends XXHandshake {
|
|||||||
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
||||||
this.setRemoteEarlyData(decodedPayload.data)
|
this.setRemoteEarlyData(decodedPayload.data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error occurred while verifying signed payload from responder: ${e.message}`)
|
const err = e as Error
|
||||||
|
throw new Error(`Error occurred while verifying signed payload from responder: ${err.message}`)
|
||||||
}
|
}
|
||||||
logger('All good with the signature!')
|
logger('All good with the signature!')
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,17 +23,17 @@ import { WrappedConnection } from './noise'
|
|||||||
import PeerId from 'peer-id'
|
import PeerId from 'peer-id'
|
||||||
|
|
||||||
export class XXHandshake implements IHandshake {
|
export class XXHandshake implements IHandshake {
|
||||||
public isInitiator: boolean;
|
public isInitiator: boolean
|
||||||
public session: NoiseSession;
|
public session: NoiseSession
|
||||||
public remotePeer!: PeerId;
|
public remotePeer!: PeerId
|
||||||
public remoteEarlyData: Buffer;
|
public remoteEarlyData: Buffer
|
||||||
|
|
||||||
protected payload: bytes;
|
protected payload: bytes
|
||||||
protected connection: WrappedConnection;
|
protected connection: WrappedConnection
|
||||||
protected xx: XX;
|
protected xx: XX
|
||||||
protected staticKeypair: KeyPair;
|
protected staticKeypair: KeyPair
|
||||||
|
|
||||||
private prologue: bytes32;
|
private readonly prologue: bytes32
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
isInitiator: boolean,
|
isInitiator: boolean,
|
||||||
@ -52,7 +52,7 @@ export class XXHandshake implements IHandshake {
|
|||||||
if (remotePeer) {
|
if (remotePeer) {
|
||||||
this.remotePeer = remotePeer
|
this.remotePeer = remotePeer
|
||||||
}
|
}
|
||||||
this.xx = handshake || new XX()
|
this.xx = handshake ?? new XX()
|
||||||
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
|
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
|
||||||
this.remoteEarlyData = Buffer.alloc(0)
|
this.remoteEarlyData = Buffer.alloc(0)
|
||||||
}
|
}
|
||||||
@ -98,7 +98,8 @@ export class XXHandshake implements IHandshake {
|
|||||||
this.remotePeer = await verifySignedPayload(receivedMessageBuffer.ns, decodedPayload, this.remotePeer)
|
this.remotePeer = await verifySignedPayload(receivedMessageBuffer.ns, decodedPayload, this.remotePeer)
|
||||||
this.setRemoteEarlyData(decodedPayload.data)
|
this.setRemoteEarlyData(decodedPayload.data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error occurred while verifying signed payload: ${e.message}`)
|
const err = e as Error
|
||||||
|
throw new Error(`Error occurred while verifying signed payload: ${err.message}`)
|
||||||
}
|
}
|
||||||
logger('All good with the signature!')
|
logger('All good with the signature!')
|
||||||
} else {
|
} else {
|
||||||
@ -132,7 +133,8 @@ export class XXHandshake implements IHandshake {
|
|||||||
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
||||||
this.setRemoteEarlyData(decodedPayload.data)
|
this.setRemoteEarlyData(decodedPayload.data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error occurred while verifying signed payload: ${e.message}`)
|
const err = e as Error
|
||||||
|
throw new Error(`Error occurred while verifying signed payload: ${err.message}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logCipherState(this.session)
|
logCipherState(this.session)
|
||||||
@ -144,7 +146,7 @@ export class XXHandshake implements IHandshake {
|
|||||||
return this.xx.encryptWithAd(cs, Buffer.alloc(0), plaintext)
|
return this.xx.encryptWithAd(cs, Buffer.alloc(0), plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes; valid: boolean} {
|
public decrypt (ciphertext: bytes, session: NoiseSession): {plaintext: bytes, valid: boolean} {
|
||||||
const cs = this.getCS(session, false)
|
const cs = this.getCS(session, false)
|
||||||
return this.xx.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
|
return this.xx.decryptWithAd(cs, Buffer.alloc(0), ciphertext)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export abstract class AbstractHandshake {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
public decryptWithAd (cs: CipherState, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
|
public decryptWithAd (cs: CipherState, ad: bytes, ciphertext: bytes): {plaintext: bytes, valid: boolean} {
|
||||||
const { plaintext, valid } = this.decrypt(cs.k, cs.n, ad, ciphertext)
|
const { plaintext, valid } = this.decrypt(cs.k, cs.n, ad, ciphertext)
|
||||||
this.setNonce(cs, this.incrementNonce(cs.n))
|
this.setNonce(cs, this.incrementNonce(cs.n))
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ export abstract class AbstractHandshake {
|
|||||||
return ciphertext
|
return ciphertext
|
||||||
}
|
}
|
||||||
|
|
||||||
protected decrypt (k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
|
protected decrypt (k: bytes32, n: uint32, ad: bytes, ciphertext: bytes): {plaintext: bytes, valid: boolean} {
|
||||||
const nonce = this.nonceToBytes(n)
|
const nonce = this.nonceToBytes(n)
|
||||||
const ctx = new AEAD()
|
const ctx = new AEAD()
|
||||||
ciphertext = Buffer.from(ciphertext)
|
ciphertext = Buffer.from(ciphertext)
|
||||||
@ -91,7 +91,7 @@ export abstract class AbstractHandshake {
|
|||||||
return { plaintext: ciphertext, valid: ctx.verify(tag) }
|
return { plaintext: ciphertext, valid: ctx.verify(tag) }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected decryptAndHash (ss: SymmetricState, ciphertext: bytes): {plaintext: bytes; valid: boolean} {
|
protected decryptAndHash (ss: SymmetricState, ciphertext: bytes): {plaintext: bytes, valid: boolean} {
|
||||||
let plaintext: bytes; let valid = true
|
let plaintext: bytes; let valid = true
|
||||||
if (this.hasKey(ss.cs)) {
|
if (this.hasKey(ss.cs)) {
|
||||||
({ plaintext, valid } = this.decryptWithAd(ss.cs, ss.h, ciphertext))
|
({ plaintext, valid } = this.decryptWithAd(ss.cs, ss.h, ciphertext))
|
||||||
@ -125,7 +125,7 @@ export abstract class AbstractHandshake {
|
|||||||
|
|
||||||
protected mixKey (ss: SymmetricState, ikm: bytes32): void {
|
protected mixKey (ss: SymmetricState, ikm: bytes32): void {
|
||||||
const [ck, tempK] = getHkdf(ss.ck, ikm)
|
const [ck, tempK] = getHkdf(ss.ck, ikm)
|
||||||
ss.cs = this.initializeKey(tempK) as CipherState
|
ss.cs = this.initializeKey(tempK)
|
||||||
ss.ck = ck
|
ss.ck = ck
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ export abstract class AbstractHandshake {
|
|||||||
return { ne, ns, ciphertext }
|
return { ne, ns, ciphertext }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readMessageRegular (cs: CipherState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
protected readMessageRegular (cs: CipherState, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext)
|
return this.decryptWithAd(cs, Buffer.alloc(0), message.ciphertext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ export class IK extends AbstractHandshake {
|
|||||||
return messageBuffer
|
return messageBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
let plaintext = Buffer.alloc(0); let valid = false
|
let plaintext = Buffer.alloc(0); let valid = false
|
||||||
if (session.mc === 0) {
|
if (session.mc === 0) {
|
||||||
({ plaintext, valid } = this.readMessageA(session.hs, message))
|
({ plaintext, valid } = this.readMessageA(session.hs, message))
|
||||||
@ -101,7 +101,7 @@ export class IK extends AbstractHandshake {
|
|||||||
return { messageBuffer, cs1, cs2, h: hs.ss.h }
|
return { messageBuffer, cs1, cs2, h: hs.ss.h }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
if (isValidPublicKey(message.ne)) {
|
if (isValidPublicKey(message.ne)) {
|
||||||
hs.re = message.ne
|
hs.re = message.ne
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ export class IK extends AbstractHandshake {
|
|||||||
return { plaintext, valid: (valid1 && valid2) }
|
return { plaintext, valid: (valid1 && valid2) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readMessageB (hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
|
private readMessageB (hs: HandshakeState, message: MessageBuffer): {h: bytes, plaintext: bytes, valid: boolean, cs1: CipherState, cs2: CipherState} {
|
||||||
if (isValidPublicKey(message.ne)) {
|
if (isValidPublicKey(message.ne)) {
|
||||||
hs.re = message.ne
|
hs.re = message.ne
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ export class XX extends AbstractHandshake {
|
|||||||
private writeMessageA (hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer {
|
private writeMessageA (hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer {
|
||||||
const ns = Buffer.alloc(0)
|
const ns = Buffer.alloc(0)
|
||||||
|
|
||||||
if (e) {
|
if (e !== undefined) {
|
||||||
hs.e = e
|
hs.e = e
|
||||||
} else {
|
} else {
|
||||||
hs.e = generateKeypair()
|
hs.e = generateKeypair()
|
||||||
@ -68,7 +68,7 @@ export class XX extends AbstractHandshake {
|
|||||||
return { h: hs.ss.h, messageBuffer, cs1, cs2 }
|
return { h: hs.ss.h, messageBuffer, cs1, cs2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
private readMessageA (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
if (isValidPublicKey(message.ne)) {
|
if (isValidPublicKey(message.ne)) {
|
||||||
hs.re = message.ne
|
hs.re = message.ne
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ export class XX extends AbstractHandshake {
|
|||||||
return this.decryptAndHash(hs.ss, message.ciphertext)
|
return this.decryptAndHash(hs.ss, message.ciphertext)
|
||||||
}
|
}
|
||||||
|
|
||||||
private readMessageB (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
private readMessageB (hs: HandshakeState, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
if (isValidPublicKey(message.ne)) {
|
if (isValidPublicKey(message.ne)) {
|
||||||
hs.re = message.ne
|
hs.re = message.ne
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ export class XX extends AbstractHandshake {
|
|||||||
return { plaintext, valid: (valid1 && valid2) }
|
return { plaintext, valid: (valid1 && valid2) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private readMessageC (hs: HandshakeState, message: MessageBuffer): {h: bytes; plaintext: bytes; valid: boolean; cs1: CipherState; cs2: CipherState} {
|
private readMessageC (hs: HandshakeState, message: MessageBuffer): {h: bytes, plaintext: bytes, valid: boolean, cs1: CipherState, cs2: CipherState} {
|
||||||
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
|
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
|
||||||
if (valid1 && ns.length === 32 && isValidPublicKey(ns)) {
|
if (valid1 && ns.length === 32 && isValidPublicKey(ns)) {
|
||||||
hs.rs = ns
|
hs.rs = ns
|
||||||
@ -164,7 +164,7 @@ export class XX extends AbstractHandshake {
|
|||||||
return messageBuffer
|
return messageBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes; valid: boolean} {
|
public recvMessage (session: NoiseSession, message: MessageBuffer): {plaintext: bytes, valid: boolean} {
|
||||||
let plaintext: bytes = Buffer.alloc(0)
|
let plaintext: bytes = Buffer.alloc(0)
|
||||||
let valid = false
|
let valid = false
|
||||||
if (session.mc === 0) {
|
if (session.mc === 0) {
|
||||||
|
@ -5,7 +5,7 @@ import PeerId from 'peer-id'
|
|||||||
* Storage for static keys of previously connected peers.
|
* Storage for static keys of previously connected peers.
|
||||||
*/
|
*/
|
||||||
class Keycache {
|
class Keycache {
|
||||||
private storage = new Map<Uint8Array, bytes32>();
|
private readonly storage = new Map<Uint8Array, bytes32>()
|
||||||
|
|
||||||
public store (peerId: PeerId, key: bytes32): void {
|
public store (peerId: PeerId, key: bytes32): void {
|
||||||
this.storage.set(peerId.id, key)
|
this.storage.set(peerId.id, key)
|
||||||
@ -15,7 +15,7 @@ class Keycache {
|
|||||||
if (!peerId) {
|
if (!peerId) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return this.storage.get(peerId.id) || null
|
return this.storage.get(peerId.id) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetStorage (): void {
|
public resetStorage (): void {
|
||||||
|
40
src/noise.ts
40
src/noise.ts
@ -21,22 +21,22 @@ import { logger } from './logger'
|
|||||||
import PeerId from 'peer-id'
|
import PeerId from 'peer-id'
|
||||||
import { NOISE_MSG_MAX_LENGTH_BYTES } from './constants'
|
import { NOISE_MSG_MAX_LENGTH_BYTES } from './constants'
|
||||||
|
|
||||||
export type WrappedConnection = ReturnType<typeof Wrap>;
|
export type WrappedConnection = ReturnType<typeof Wrap>
|
||||||
|
|
||||||
type HandshakeParams = {
|
interface HandshakeParams {
|
||||||
connection: WrappedConnection;
|
connection: WrappedConnection
|
||||||
isInitiator: boolean;
|
isInitiator: boolean
|
||||||
localPeer: PeerId;
|
localPeer: PeerId
|
||||||
remotePeer?: PeerId;
|
remotePeer?: PeerId
|
||||||
};
|
}
|
||||||
|
|
||||||
export class Noise implements INoiseConnection {
|
export class Noise implements INoiseConnection {
|
||||||
public protocol = '/noise';
|
public protocol = '/noise'
|
||||||
|
|
||||||
private readonly prologue = Buffer.alloc(0);
|
private readonly prologue = Buffer.alloc(0)
|
||||||
private readonly staticKeys: KeyPair;
|
private readonly staticKeys: KeyPair
|
||||||
private readonly earlyData?: bytes;
|
private readonly earlyData?: bytes
|
||||||
private useNoisePipes: boolean;
|
private readonly useNoisePipes: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -44,7 +44,7 @@ export class Noise implements INoiseConnection {
|
|||||||
* @param {bytes} earlyData
|
* @param {bytes} earlyData
|
||||||
*/
|
*/
|
||||||
constructor (staticNoiseKey?: bytes, earlyData?: bytes) {
|
constructor (staticNoiseKey?: bytes, earlyData?: bytes) {
|
||||||
this.earlyData = earlyData || Buffer.alloc(0)
|
this.earlyData = earlyData ?? Buffer.alloc(0)
|
||||||
// disabled until properly specked
|
// disabled until properly specked
|
||||||
this.useNoisePipes = false
|
this.useNoisePipes = false
|
||||||
|
|
||||||
@ -71,9 +71,6 @@ export class Noise implements INoiseConnection {
|
|||||||
const wrappedConnection = Wrap(
|
const wrappedConnection = Wrap(
|
||||||
connection,
|
connection,
|
||||||
{
|
{
|
||||||
// wrong types in repo
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
lengthEncoder: uint16BEEncode,
|
lengthEncoder: uint16BEEncode,
|
||||||
lengthDecoder: uint16BEDecode,
|
lengthDecoder: uint16BEDecode,
|
||||||
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
|
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
|
||||||
@ -106,9 +103,6 @@ export class Noise implements INoiseConnection {
|
|||||||
const wrappedConnection = Wrap(
|
const wrappedConnection = Wrap(
|
||||||
connection,
|
connection,
|
||||||
{
|
{
|
||||||
// wrong types in repo
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
lengthEncoder: uint16BEEncode,
|
lengthEncoder: uint16BEEncode,
|
||||||
lengthDecoder: uint16BEDecode,
|
lengthDecoder: uint16BEDecode,
|
||||||
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
|
maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
|
||||||
@ -153,7 +147,7 @@ export class Noise implements INoiseConnection {
|
|||||||
this.staticKeys,
|
this.staticKeys,
|
||||||
connection,
|
connection,
|
||||||
// safe to cast as we did checks
|
// safe to cast as we did checks
|
||||||
KeyCache.load(params.remotePeer) || Buffer.alloc(32),
|
KeyCache.load(params.remotePeer) ?? Buffer.alloc(32),
|
||||||
remotePeer as PeerId
|
remotePeer as PeerId
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -189,7 +183,8 @@ export class Noise implements INoiseConnection {
|
|||||||
await handshake.finish()
|
await handshake.finish()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger(e)
|
logger(e)
|
||||||
throw new Error(`Error occurred during XX Fallback handshake: ${e.message}`)
|
const err = e as Error
|
||||||
|
throw new Error(`Error occurred during XX Fallback handshake: ${err.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return handshake
|
return handshake
|
||||||
@ -211,7 +206,8 @@ export class Noise implements INoiseConnection {
|
|||||||
KeyCache.store(handshake.remotePeer, handshake.getRemoteStaticKey())
|
KeyCache.store(handshake.remotePeer, handshake.getRemoteStaticKey())
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error occurred during XX handshake: ${e.message}`)
|
const err = e as Error
|
||||||
|
throw new Error(`Error occurred during XX handshake: ${err.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return handshake
|
return handshake
|
||||||
|
10
src/utils.ts
10
src/utils.ts
@ -28,9 +28,9 @@ export async function getPayload (
|
|||||||
earlyData?: bytes
|
earlyData?: bytes
|
||||||
): Promise<bytes> {
|
): Promise<bytes> {
|
||||||
const signedPayload = await signPayload(localPeer, getHandshakePayload(staticPublicKey))
|
const signedPayload = await signPayload(localPeer, getHandshakePayload(staticPublicKey))
|
||||||
const earlyDataPayload = earlyData || Buffer.alloc(0)
|
const earlyDataPayload = earlyData ?? Buffer.alloc(0)
|
||||||
|
|
||||||
return await createHandshakePayload(
|
return createHandshakePayload(
|
||||||
localPeer.marshalPubKey(),
|
localPeer.marshalPubKey(),
|
||||||
signedPayload,
|
signedPayload,
|
||||||
earlyDataPayload
|
earlyDataPayload
|
||||||
@ -45,7 +45,7 @@ export function createHandshakePayload (
|
|||||||
const payloadInit = NoiseHandshakePayloadProto.create({
|
const payloadInit = NoiseHandshakePayloadProto.create({
|
||||||
identityKey: Buffer.from(libp2pPublicKey),
|
identityKey: Buffer.from(libp2pPublicKey),
|
||||||
identitySig: signedPayload,
|
identitySig: signedPayload,
|
||||||
data: earlyData || null
|
data: earlyData ?? null
|
||||||
})
|
})
|
||||||
|
|
||||||
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish())
|
return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish())
|
||||||
@ -94,10 +94,12 @@ export async function verifySignedPayload (
|
|||||||
const generatedPayload = getHandshakePayload(noiseStaticKey)
|
const generatedPayload = getHandshakePayload(noiseStaticKey)
|
||||||
// Unmarshaling from PublicKey protobuf
|
// Unmarshaling from PublicKey protobuf
|
||||||
const publicKey = keys.unmarshalPublicKey(identityKey)
|
const publicKey = keys.unmarshalPublicKey(identityKey)
|
||||||
|
// TODO remove this after libp2p-crypto ships proper types
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
if (!payload.identitySig || !publicKey.verify(generatedPayload, Buffer.from(payload.identitySig))) {
|
if (!payload.identitySig || !publicKey.verify(generatedPayload, Buffer.from(payload.identitySig))) {
|
||||||
throw new Error("Static key doesn't match to peer that signed payload!")
|
throw new Error("Static key doesn't match to peer that signed payload!")
|
||||||
}
|
}
|
||||||
return PeerId.createFromPubKey(identityKey)
|
return await PeerId.createFromPubKey(identityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHkdf (ck: bytes32, ikm: bytes): Hkdf {
|
export function getHkdf (ck: bytes32, ikm: bytes): Hkdf {
|
||||||
|
2
test/fixtures/node-globals.js
vendored
Normal file
2
test/fixtures/node-globals.js
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
export const { Buffer } = require('buffer')
|
4
test/fixtures/peer.ts
vendored
4
test/fixtures/peer.ts
vendored
@ -19,9 +19,9 @@ const peers = [{
|
|||||||
pubKey: 'CAESIMbnikZaPciAMZhUXqDRVCs7VFOBtmlIk26g0GgOotDA'
|
pubKey: 'CAESIMbnikZaPciAMZhUXqDRVCs7VFOBtmlIk26g0GgOotDA'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
export async function createPeerIdsFromFixtures (length:number): Promise<PeerId[]> {
|
export async function createPeerIdsFromFixtures (length: number): Promise<PeerId[]> {
|
||||||
return await Promise.all(
|
return await Promise.all(
|
||||||
Array.from({ length }).map((_, i) => PeerId.createFromJSON(peers[i]))
|
Array.from({ length }).map(async (_, i) => await PeerId.createFromJSON(peers[i]))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ describe('IK handshake', () => {
|
|||||||
// initiator receives message
|
// initiator receives message
|
||||||
ikI.recvMessage(initiatorSession, messageBuffer2)
|
ikI.recvMessage(initiatorSession, messageBuffer2)
|
||||||
|
|
||||||
assert(initiatorSession?.cs1?.k.equals(responderSession?.cs1?.k || new Uint8Array()))
|
assert(initiatorSession?.cs1?.k.equals(responderSession?.cs1?.k ?? new Uint8Array()))
|
||||||
assert(initiatorSession?.cs2?.k.equals(responderSession?.cs2?.k || new Uint8Array()))
|
assert(initiatorSession?.cs2?.k.equals(responderSession?.cs2?.k ?? new Uint8Array()))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return assert(false, e.message)
|
return assert(false, e.message)
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,10 @@ describe('KeyCache', () => {
|
|||||||
const key = Buffer.from('this is id 007')
|
const key = Buffer.from('this is id 007')
|
||||||
await KeyCache.store(peerA, key)
|
await KeyCache.store(peerA, key)
|
||||||
const result = await KeyCache.load(peerA)
|
const result = await KeyCache.load(peerA)
|
||||||
assert(uint8ArrayEquals(result, key), 'Stored and loaded key are not the same')
|
assert(result !== null && uint8ArrayEquals(result, key), 'Stored and loaded key are not the same')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
assert(false, `Test failed - ${e.message}`)
|
const err = e as Error
|
||||||
|
assert(false, `Test failed - ${err.message}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -25,9 +26,10 @@ describe('KeyCache', () => {
|
|||||||
try {
|
try {
|
||||||
const [newPeer] = await createPeerIds(1)
|
const [newPeer] = await createPeerIds(1)
|
||||||
const result = await KeyCache.load(newPeer)
|
const result = await KeyCache.load(newPeer)
|
||||||
assert(!result)
|
assert(result === null)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
assert(false, `Test failed - ${e.message}`)
|
const err = e as Error
|
||||||
|
assert(false, `Test failed - ${err.message}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -144,7 +144,9 @@ describe('Noise', () => {
|
|||||||
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
|
KeyCache.store(localPeer, staticKeysInitiator.publicKey)
|
||||||
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
|
KeyCache.store(remotePeer, staticKeysResponder.publicKey)
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
const xxSpy = sandbox.spy(noiseInit, 'performXXHandshake')
|
const xxSpy = sandbox.spy(noiseInit, 'performXXHandshake')
|
||||||
|
// @ts-expect-error
|
||||||
const xxFallbackSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
const xxFallbackSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
||||||
|
|
||||||
const [inboundConnection, outboundConnection] = DuplexPair()
|
const [inboundConnection, outboundConnection] = DuplexPair()
|
||||||
@ -171,6 +173,7 @@ describe('Noise', () => {
|
|||||||
const staticKeysInitiator = generateKeypair()
|
const staticKeysInitiator = generateKeypair()
|
||||||
const noiseInit = new Noise(staticKeysInitiator.privateKey)
|
const noiseInit = new Noise(staticKeysInitiator.privateKey)
|
||||||
const noiseResp = new Noise()
|
const noiseResp = new Noise()
|
||||||
|
// @ts-expect-error
|
||||||
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
||||||
|
|
||||||
// Prepare key cache for noise pipes
|
// Prepare key cache for noise pipes
|
||||||
@ -205,6 +208,7 @@ describe('Noise', () => {
|
|||||||
|
|
||||||
const staticKeysResponder = generateKeypair()
|
const staticKeysResponder = generateKeypair()
|
||||||
const noiseResp = new Noise(staticKeysResponder.privateKey, undefined)
|
const noiseResp = new Noise(staticKeysResponder.privateKey, undefined)
|
||||||
|
// @ts-expect-error
|
||||||
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
const xxSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
||||||
|
|
||||||
// Prepare key cache for noise pipes
|
// Prepare key cache for noise pipes
|
||||||
@ -237,7 +241,9 @@ describe('Noise', () => {
|
|||||||
const staticKeysResponder = generateKeypair()
|
const staticKeysResponder = generateKeypair()
|
||||||
|
|
||||||
const noiseResp = new Noise(staticKeysResponder.privateKey)
|
const noiseResp = new Noise(staticKeysResponder.privateKey)
|
||||||
|
// @ts-expect-error
|
||||||
const xxInitSpy = sandbox.spy(noiseInit, 'performXXHandshake')
|
const xxInitSpy = sandbox.spy(noiseInit, 'performXXHandshake')
|
||||||
|
// @ts-expect-error
|
||||||
const xxRespSpy = sandbox.spy(noiseResp, 'performXXFallbackHandshake')
|
const xxRespSpy = sandbox.spy(noiseResp, 'performXXFallbackHandshake')
|
||||||
|
|
||||||
// Prepare key cache for noise pipes
|
// Prepare key cache for noise pipes
|
||||||
@ -271,8 +277,11 @@ describe('Noise', () => {
|
|||||||
const staticKeysResponder = generateKeypair()
|
const staticKeysResponder = generateKeypair()
|
||||||
|
|
||||||
const noiseResp = new Noise(staticKeysResponder.privateKey)
|
const noiseResp = new Noise(staticKeysResponder.privateKey)
|
||||||
|
// @ts-expect-error
|
||||||
const ikInitSpy = sandbox.spy(noiseInit, 'performIKHandshake')
|
const ikInitSpy = sandbox.spy(noiseInit, 'performIKHandshake')
|
||||||
|
// @ts-expect-error
|
||||||
const xxFallbackInitSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
const xxFallbackInitSpy = sandbox.spy(noiseInit, 'performXXFallbackHandshake')
|
||||||
|
// @ts-expect-error
|
||||||
const ikRespSpy = sandbox.spy(noiseResp, 'performIKHandshake')
|
const ikRespSpy = sandbox.spy(noiseResp, 'performIKHandshake')
|
||||||
|
|
||||||
// Prepare key cache for noise pipes
|
// Prepare key cache for noise pipes
|
||||||
|
@ -8,7 +8,7 @@ export async function generateEd25519Keys (): Promise<PrivateKey> {
|
|||||||
|
|
||||||
export function getKeyPairFromPeerId (peerId: PeerId): KeyPair {
|
export function getKeyPairFromPeerId (peerId: PeerId): KeyPair {
|
||||||
return {
|
return {
|
||||||
privateKey: peerId.privKey.marshal().slice(0, 32),
|
privateKey: Buffer.from(peerId.privKey.marshal().slice(0, 32)),
|
||||||
publicKey: peerId.marshalPubKey()
|
publicKey: Buffer.from(peerId.marshalPubKey())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ describe('XX Fallback Handshake', () => {
|
|||||||
// This is the point where initiator falls back from IK
|
// This is the point where initiator falls back from IK
|
||||||
const initialMsgI = await connectionFrom.readLP()
|
const initialMsgI = await connectionFrom.readLP()
|
||||||
const handshakeInit =
|
const handshakeInit =
|
||||||
new XXFallbackHandshake(true, handshakePayload, prologue, staticKeysInitiator, connectionFrom, initialMsgI, peerB, ephemeralKeys)
|
new XXFallbackHandshake(true, handshakePayload, prologue, staticKeysInitiator, connectionFrom, initialMsgI.slice(0), peerB, ephemeralKeys)
|
||||||
|
|
||||||
await handshakeInit.propose()
|
await handshakeInit.propose()
|
||||||
await handshakeInit.exchange()
|
await handshakeInit.exchange()
|
||||||
@ -60,7 +60,10 @@ describe('XX Fallback Handshake', () => {
|
|||||||
const sessionResponder = handshakeResp.session
|
const sessionResponder = handshakeResp.session
|
||||||
|
|
||||||
// Test shared key
|
// Test shared key
|
||||||
if (sessionInitator.cs1 && sessionResponder.cs1 && sessionInitator.cs2 && sessionResponder.cs2) {
|
if (sessionInitator.cs1 !== undefined &&
|
||||||
|
sessionResponder.cs1 !== undefined &&
|
||||||
|
sessionInitator.cs2 !== undefined &&
|
||||||
|
sessionResponder.cs2 !== undefined) {
|
||||||
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k))
|
assert(sessionInitator.cs1.k.equals(sessionResponder.cs1.k))
|
||||||
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k))
|
assert(sessionInitator.cs2.k.equals(sessionResponder.cs2.k))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"incremental": true,
|
||||||
|
"composite": true,
|
||||||
"target": "ES2018",
|
"target": "ES2018",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
@ -14,6 +17,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
"**/test/**/*.ts",
|
||||||
"**/src/**/*.ts",
|
"**/src/**/*.ts",
|
||||||
"**/src/**/*.js"
|
"**/src/**/*.js"
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user