@libp2p/noise 12.0.1 → 16.1.4-6059227cb
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/dist/index.min.js +16 -20
- package/dist/index.min.js.map +7 -0
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +1 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/crypto/index.browser.d.ts +2 -0
- package/dist/src/crypto/index.browser.d.ts.map +1 -0
- package/dist/src/crypto/index.browser.js +3 -0
- package/dist/src/crypto/index.browser.js.map +1 -0
- package/dist/src/crypto/index.d.ts +3 -0
- package/dist/src/crypto/index.d.ts.map +1 -0
- package/dist/src/crypto/index.js +188 -0
- package/dist/src/crypto/index.js.map +1 -0
- package/dist/src/crypto/js.d.ts.map +1 -1
- package/dist/src/crypto/js.js +18 -17
- package/dist/src/crypto/js.js.map +1 -1
- package/dist/src/crypto.d.ts +11 -10
- package/dist/src/crypto.d.ts.map +1 -1
- package/dist/src/crypto.js +10 -1
- package/dist/src/crypto.js.map +1 -1
- package/dist/src/encoder.d.ts +5 -10
- package/dist/src/encoder.d.ts.map +1 -1
- package/dist/src/encoder.js +10 -51
- package/dist/src/encoder.js.map +1 -1
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +9 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +50 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +38 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/logger.d.ts +9 -10
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +39 -28
- package/dist/src/logger.js.map +1 -1
- package/dist/src/metrics.d.ts +1 -1
- package/dist/src/metrics.d.ts.map +1 -1
- package/dist/src/noise.d.ts +33 -30
- package/dist/src/noise.d.ts.map +1 -1
- package/dist/src/noise.js +122 -78
- package/dist/src/noise.js.map +1 -1
- package/dist/src/nonce.d.ts +2 -3
- package/dist/src/nonce.d.ts.map +1 -1
- package/dist/src/nonce.js +3 -2
- package/dist/src/nonce.js.map +1 -1
- package/dist/src/performHandshake.d.ts +5 -0
- package/dist/src/performHandshake.d.ts.map +1 -0
- package/dist/src/performHandshake.js +69 -0
- package/dist/src/performHandshake.js.map +1 -0
- package/dist/src/proto/payload.d.ts +4 -3
- package/dist/src/proto/payload.d.ts.map +1 -1
- package/dist/src/proto/payload.js +48 -29
- package/dist/src/proto/payload.js.map +1 -1
- package/dist/src/protocol.d.ts +71 -0
- package/dist/src/protocol.d.ts.map +1 -0
- package/dist/src/protocol.js +257 -0
- package/dist/src/protocol.js.map +1 -0
- package/dist/src/types.d.ts +98 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils.d.ts +10 -19
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +159 -57
- package/dist/src/utils.js.map +1 -1
- package/package.json +43 -48
- package/src/constants.ts +1 -0
- package/src/crypto/index.browser.ts +3 -0
- package/src/crypto/index.ts +219 -0
- package/src/crypto/js.ts +25 -27
- package/src/crypto.ts +21 -10
- package/src/encoder.ts +10 -67
- package/src/errors.ts +10 -0
- package/src/index.ts +54 -6
- package/src/logger.ts +46 -32
- package/src/metrics.ts +1 -1
- package/src/noise.ts +176 -131
- package/src/nonce.ts +7 -7
- package/src/performHandshake.ts +91 -0
- package/src/proto/payload.proto +2 -1
- package/src/proto/payload.ts +53 -31
- package/src/protocol.ts +313 -0
- package/src/types.ts +105 -0
- package/src/utils.ts +189 -79
- package/dist/src/@types/basic.d.ts +0 -5
- package/dist/src/@types/basic.d.ts.map +0 -1
- package/dist/src/@types/basic.js +0 -2
- package/dist/src/@types/basic.js.map +0 -1
- package/dist/src/@types/handshake-interface.d.ts +0 -15
- package/dist/src/@types/handshake-interface.d.ts.map +0 -1
- package/dist/src/@types/handshake-interface.js +0 -2
- package/dist/src/@types/handshake-interface.js.map +0 -1
- package/dist/src/@types/handshake.d.ts +0 -40
- package/dist/src/@types/handshake.d.ts.map +0 -1
- package/dist/src/@types/handshake.js +0 -2
- package/dist/src/@types/handshake.js.map +0 -1
- package/dist/src/@types/libp2p.d.ts +0 -10
- package/dist/src/@types/libp2p.d.ts.map +0 -1
- package/dist/src/@types/libp2p.js +0 -2
- package/dist/src/@types/libp2p.js.map +0 -1
- package/dist/src/crypto/streaming.d.ts +0 -7
- package/dist/src/crypto/streaming.d.ts.map +0 -1
- package/dist/src/crypto/streaming.js +0 -50
- package/dist/src/crypto/streaming.js.map +0 -1
- package/dist/src/handshake-xx.d.ts +0 -33
- package/dist/src/handshake-xx.d.ts.map +0 -1
- package/dist/src/handshake-xx.js +0 -139
- package/dist/src/handshake-xx.js.map +0 -1
- package/dist/src/handshakes/abstract-handshake.d.ts +0 -36
- package/dist/src/handshakes/abstract-handshake.d.ts.map +0 -1
- package/dist/src/handshakes/abstract-handshake.js +0 -140
- package/dist/src/handshakes/abstract-handshake.js.map +0 -1
- package/dist/src/handshakes/xx.d.ts +0 -18
- package/dist/src/handshakes/xx.d.ts.map +0 -1
- package/dist/src/handshakes/xx.js +0 -161
- package/dist/src/handshakes/xx.js.map +0 -1
- package/src/@types/basic.ts +0 -5
- package/src/@types/handshake-interface.ts +0 -12
- package/src/@types/handshake.ts +0 -48
- package/src/@types/libp2p.ts +0 -10
- package/src/crypto/streaming.ts +0 -58
- package/src/handshake-xx.ts +0 -177
- package/src/handshakes/abstract-handshake.ts +0 -180
- package/src/handshakes/xx.ts +0 -184
package/src/proto/payload.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
|
|
5
|
-
/* eslint-disable @typescript-eslint/no-empty-interface */
|
|
6
|
-
|
|
7
|
-
import { encodeMessage, decodeMessage, message } from 'protons-runtime'
|
|
8
|
-
import type { Codec } from 'protons-runtime'
|
|
1
|
+
import { decodeMessage, encodeMessage, MaxLengthError, message } from 'protons-runtime'
|
|
2
|
+
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
|
|
3
|
+
import type { Codec, DecodeOptions } from 'protons-runtime'
|
|
9
4
|
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
10
5
|
|
|
11
6
|
export interface NoiseExtensions {
|
|
12
7
|
webtransportCerthashes: Uint8Array[]
|
|
8
|
+
streamMuxers: string[]
|
|
13
9
|
}
|
|
14
10
|
|
|
15
11
|
export namespace NoiseExtensions {
|
|
@@ -29,12 +25,20 @@ export namespace NoiseExtensions {
|
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
27
|
|
|
28
|
+
if (obj.streamMuxers != null) {
|
|
29
|
+
for (const value of obj.streamMuxers) {
|
|
30
|
+
w.uint32(18)
|
|
31
|
+
w.string(value)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
32
35
|
if (opts.lengthDelimited !== false) {
|
|
33
36
|
w.ldelim()
|
|
34
37
|
}
|
|
35
|
-
}, (reader, length) => {
|
|
38
|
+
}, (reader, length, opts = {}) => {
|
|
36
39
|
const obj: any = {
|
|
37
|
-
webtransportCerthashes: []
|
|
40
|
+
webtransportCerthashes: [],
|
|
41
|
+
streamMuxers: []
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
const end = length == null ? reader.len : reader.pos + length
|
|
@@ -43,12 +47,26 @@ export namespace NoiseExtensions {
|
|
|
43
47
|
const tag = reader.uint32()
|
|
44
48
|
|
|
45
49
|
switch (tag >>> 3) {
|
|
46
|
-
case 1:
|
|
50
|
+
case 1: {
|
|
51
|
+
if (opts.limits?.webtransportCerthashes != null && obj.webtransportCerthashes.length === opts.limits.webtransportCerthashes) {
|
|
52
|
+
throw new MaxLengthError('Decode error - map field "webtransportCerthashes" had too many elements')
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
obj.webtransportCerthashes.push(reader.bytes())
|
|
48
56
|
break
|
|
49
|
-
|
|
57
|
+
}
|
|
58
|
+
case 2: {
|
|
59
|
+
if (opts.limits?.streamMuxers != null && obj.streamMuxers.length === opts.limits.streamMuxers) {
|
|
60
|
+
throw new MaxLengthError('Decode error - map field "streamMuxers" had too many elements')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
obj.streamMuxers.push(reader.string())
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
default: {
|
|
50
67
|
reader.skipType(tag & 7)
|
|
51
68
|
break
|
|
69
|
+
}
|
|
52
70
|
}
|
|
53
71
|
}
|
|
54
72
|
|
|
@@ -63,8 +81,8 @@ export namespace NoiseExtensions {
|
|
|
63
81
|
return encodeMessage(obj, NoiseExtensions.codec())
|
|
64
82
|
}
|
|
65
83
|
|
|
66
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseExtensions => {
|
|
67
|
-
return decodeMessage(buf, NoiseExtensions.codec())
|
|
84
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<NoiseExtensions>): NoiseExtensions => {
|
|
85
|
+
return decodeMessage(buf, NoiseExtensions.codec(), opts)
|
|
68
86
|
}
|
|
69
87
|
}
|
|
70
88
|
|
|
@@ -84,30 +102,28 @@ export namespace NoiseHandshakePayload {
|
|
|
84
102
|
w.fork()
|
|
85
103
|
}
|
|
86
104
|
|
|
87
|
-
if (
|
|
105
|
+
if ((obj.identityKey != null && obj.identityKey.byteLength > 0)) {
|
|
88
106
|
w.uint32(10)
|
|
89
|
-
w.bytes(obj.identityKey
|
|
107
|
+
w.bytes(obj.identityKey)
|
|
90
108
|
}
|
|
91
109
|
|
|
92
|
-
if (
|
|
110
|
+
if ((obj.identitySig != null && obj.identitySig.byteLength > 0)) {
|
|
93
111
|
w.uint32(18)
|
|
94
|
-
w.bytes(obj.identitySig
|
|
112
|
+
w.bytes(obj.identitySig)
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
if (obj.extensions != null) {
|
|
98
116
|
w.uint32(34)
|
|
99
|
-
NoiseExtensions.codec().encode(obj.extensions, w
|
|
100
|
-
writeDefaults: false
|
|
101
|
-
})
|
|
117
|
+
NoiseExtensions.codec().encode(obj.extensions, w)
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
if (opts.lengthDelimited !== false) {
|
|
105
121
|
w.ldelim()
|
|
106
122
|
}
|
|
107
|
-
}, (reader, length) => {
|
|
123
|
+
}, (reader, length, opts = {}) => {
|
|
108
124
|
const obj: any = {
|
|
109
|
-
identityKey:
|
|
110
|
-
identitySig:
|
|
125
|
+
identityKey: uint8ArrayAlloc(0),
|
|
126
|
+
identitySig: uint8ArrayAlloc(0)
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
const end = length == null ? reader.len : reader.pos + length
|
|
@@ -116,18 +132,24 @@ export namespace NoiseHandshakePayload {
|
|
|
116
132
|
const tag = reader.uint32()
|
|
117
133
|
|
|
118
134
|
switch (tag >>> 3) {
|
|
119
|
-
case 1:
|
|
135
|
+
case 1: {
|
|
120
136
|
obj.identityKey = reader.bytes()
|
|
121
137
|
break
|
|
122
|
-
|
|
138
|
+
}
|
|
139
|
+
case 2: {
|
|
123
140
|
obj.identitySig = reader.bytes()
|
|
124
141
|
break
|
|
125
|
-
|
|
126
|
-
|
|
142
|
+
}
|
|
143
|
+
case 4: {
|
|
144
|
+
obj.extensions = NoiseExtensions.codec().decode(reader, reader.uint32(), {
|
|
145
|
+
limits: opts.limits?.extensions
|
|
146
|
+
})
|
|
127
147
|
break
|
|
128
|
-
|
|
148
|
+
}
|
|
149
|
+
default: {
|
|
129
150
|
reader.skipType(tag & 7)
|
|
130
151
|
break
|
|
152
|
+
}
|
|
131
153
|
}
|
|
132
154
|
}
|
|
133
155
|
|
|
@@ -142,7 +164,7 @@ export namespace NoiseHandshakePayload {
|
|
|
142
164
|
return encodeMessage(obj, NoiseHandshakePayload.codec())
|
|
143
165
|
}
|
|
144
166
|
|
|
145
|
-
export const decode = (buf: Uint8Array | Uint8ArrayList): NoiseHandshakePayload => {
|
|
146
|
-
return decodeMessage(buf, NoiseHandshakePayload.codec())
|
|
167
|
+
export const decode = (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<NoiseHandshakePayload>): NoiseHandshakePayload => {
|
|
168
|
+
return decodeMessage(buf, NoiseHandshakePayload.codec(), opts)
|
|
147
169
|
}
|
|
148
170
|
}
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
2
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays'
|
|
3
|
+
import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
|
|
4
|
+
import { InvalidCryptoExchangeError } from './errors.js'
|
|
5
|
+
import { Nonce } from './nonce.js'
|
|
6
|
+
import type { ICipherState, ISymmetricState, IHandshakeState, KeyPair, ICrypto } from './types.js'
|
|
7
|
+
|
|
8
|
+
// Code in this file is a direct translation of a subset of the noise protocol https://noiseprotocol.org/noise.html,
|
|
9
|
+
// agnostic to libp2p's usage of noise
|
|
10
|
+
|
|
11
|
+
export const ZEROLEN = uint8ArrayAlloc(0)
|
|
12
|
+
|
|
13
|
+
interface ICipherStateWithKey extends ICipherState {
|
|
14
|
+
k: Uint8Array
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class CipherState implements ICipherState {
|
|
18
|
+
public k?: Uint8Array
|
|
19
|
+
public n: Nonce
|
|
20
|
+
private readonly crypto: ICrypto
|
|
21
|
+
|
|
22
|
+
constructor (crypto: ICrypto, k: Uint8Array | undefined = undefined, n = 0) {
|
|
23
|
+
this.crypto = crypto
|
|
24
|
+
this.k = k
|
|
25
|
+
this.n = new Nonce(n)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public hasKey (): this is ICipherStateWithKey {
|
|
29
|
+
return Boolean(this.k)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public encryptWithAd (ad: Uint8Array, plaintext: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
33
|
+
if (!this.hasKey()) {
|
|
34
|
+
return plaintext
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.n.assertValue()
|
|
38
|
+
const e = this.crypto.encrypt(plaintext, this.n.getBytes(), ad, this.k)
|
|
39
|
+
this.n.increment()
|
|
40
|
+
|
|
41
|
+
return e
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public decryptWithAd (ad: Uint8Array, ciphertext: Uint8Array | Uint8ArrayList, dst?: Uint8Array): Uint8Array | Uint8ArrayList {
|
|
45
|
+
if (!this.hasKey()) {
|
|
46
|
+
return ciphertext
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.n.assertValue()
|
|
50
|
+
const plaintext = this.crypto.decrypt(ciphertext, this.n.getBytes(), ad, this.k, dst)
|
|
51
|
+
this.n.increment()
|
|
52
|
+
|
|
53
|
+
return plaintext
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class SymmetricState implements ISymmetricState {
|
|
58
|
+
public cs: CipherState
|
|
59
|
+
public ck: Uint8Array
|
|
60
|
+
public h: Uint8Array
|
|
61
|
+
private readonly crypto: ICrypto
|
|
62
|
+
|
|
63
|
+
constructor (crypto: ICrypto, protocolName: string) {
|
|
64
|
+
this.crypto = crypto
|
|
65
|
+
|
|
66
|
+
const protocolNameBytes = uint8ArrayFromString(protocolName, 'utf-8')
|
|
67
|
+
this.h = hashProtocolName(crypto, protocolNameBytes)
|
|
68
|
+
|
|
69
|
+
this.ck = this.h
|
|
70
|
+
this.cs = new CipherState(crypto)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public mixKey (ikm: Uint8Array): void {
|
|
74
|
+
const [ck, tempK] = this.crypto.hkdf(this.ck, ikm)
|
|
75
|
+
this.ck = ck
|
|
76
|
+
this.cs = new CipherState(this.crypto, tempK)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public mixHash (data: Uint8Array | Uint8ArrayList): void {
|
|
80
|
+
this.h = this.crypto.hash(new Uint8ArrayList(this.h, data))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public encryptAndHash (plaintext: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
84
|
+
const ciphertext = this.cs.encryptWithAd(this.h, plaintext)
|
|
85
|
+
this.mixHash(ciphertext)
|
|
86
|
+
return ciphertext
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public decryptAndHash (ciphertext: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
90
|
+
const plaintext = this.cs.decryptWithAd(this.h, ciphertext)
|
|
91
|
+
this.mixHash(ciphertext)
|
|
92
|
+
return plaintext
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public split (): [CipherState, CipherState] {
|
|
96
|
+
const [tempK1, tempK2] = this.crypto.hkdf(this.ck, ZEROLEN)
|
|
97
|
+
return [new CipherState(this.crypto, tempK1), new CipherState(this.crypto, tempK2)]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// const MESSAGE_PATTERNS = ['e', 's', 'ee', 'es', 'se', 'ss'] as const
|
|
102
|
+
// type MessagePattern = Array<typeof MESSAGE_PATTERNS[number]>
|
|
103
|
+
|
|
104
|
+
export interface HandshakeStateInit {
|
|
105
|
+
crypto: ICrypto
|
|
106
|
+
protocolName: string
|
|
107
|
+
initiator: boolean
|
|
108
|
+
prologue: Uint8Array
|
|
109
|
+
s?: KeyPair
|
|
110
|
+
e?: KeyPair
|
|
111
|
+
rs?: Uint8Array | Uint8ArrayList
|
|
112
|
+
re?: Uint8Array | Uint8ArrayList
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export abstract class AbstractHandshakeState implements IHandshakeState {
|
|
116
|
+
public ss: SymmetricState
|
|
117
|
+
public s?: KeyPair
|
|
118
|
+
public e?: KeyPair
|
|
119
|
+
public rs?: Uint8Array | Uint8ArrayList
|
|
120
|
+
public re?: Uint8Array | Uint8ArrayList
|
|
121
|
+
public initiator: boolean
|
|
122
|
+
protected readonly crypto: ICrypto
|
|
123
|
+
|
|
124
|
+
constructor (init: HandshakeStateInit) {
|
|
125
|
+
const { crypto, protocolName, prologue, initiator, s, e, rs, re } = init
|
|
126
|
+
this.crypto = crypto
|
|
127
|
+
this.ss = new SymmetricState(crypto, protocolName)
|
|
128
|
+
this.ss.mixHash(prologue)
|
|
129
|
+
this.initiator = initiator
|
|
130
|
+
this.s = s
|
|
131
|
+
this.e = e
|
|
132
|
+
this.rs = rs
|
|
133
|
+
this.re = re
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
protected writeE (): Uint8Array {
|
|
137
|
+
if (this.e) {
|
|
138
|
+
throw new Error('ephemeral keypair is already set')
|
|
139
|
+
}
|
|
140
|
+
const e = this.crypto.generateKeypair()
|
|
141
|
+
this.ss.mixHash(e.publicKey)
|
|
142
|
+
this.e = e
|
|
143
|
+
return e.publicKey
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
protected writeS (): Uint8Array | Uint8ArrayList {
|
|
147
|
+
if (!this.s) {
|
|
148
|
+
throw new Error('static keypair is not set')
|
|
149
|
+
}
|
|
150
|
+
return this.ss.encryptAndHash(this.s.publicKey)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
protected writeEE (): void {
|
|
154
|
+
if (!this.e) {
|
|
155
|
+
throw new Error('ephemeral keypair is not set')
|
|
156
|
+
}
|
|
157
|
+
if (!this.re) {
|
|
158
|
+
throw new Error('remote ephemeral public key is not set')
|
|
159
|
+
}
|
|
160
|
+
this.ss.mixKey(this.crypto.dh(this.e, this.re))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
protected writeES (): void {
|
|
164
|
+
if (this.initiator) {
|
|
165
|
+
if (!this.e) {
|
|
166
|
+
throw new Error('ephemeral keypair is not set')
|
|
167
|
+
}
|
|
168
|
+
if (!this.rs) {
|
|
169
|
+
throw new Error('remote static public key is not set')
|
|
170
|
+
}
|
|
171
|
+
this.ss.mixKey(this.crypto.dh(this.e, this.rs))
|
|
172
|
+
} else {
|
|
173
|
+
if (!this.s) {
|
|
174
|
+
throw new Error('static keypair is not set')
|
|
175
|
+
}
|
|
176
|
+
if (!this.re) {
|
|
177
|
+
throw new Error('remote ephemeral public key is not set')
|
|
178
|
+
}
|
|
179
|
+
this.ss.mixKey(this.crypto.dh(this.s, this.re))
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
protected writeSE (): void {
|
|
184
|
+
if (this.initiator) {
|
|
185
|
+
if (!this.s) {
|
|
186
|
+
throw new Error('static keypair is not set')
|
|
187
|
+
}
|
|
188
|
+
if (!this.re) {
|
|
189
|
+
throw new Error('remote ephemeral public key is not set')
|
|
190
|
+
}
|
|
191
|
+
this.ss.mixKey(this.crypto.dh(this.s, this.re))
|
|
192
|
+
} else {
|
|
193
|
+
if (!this.e) {
|
|
194
|
+
throw new Error('ephemeral keypair is not set')
|
|
195
|
+
}
|
|
196
|
+
if (!this.rs) {
|
|
197
|
+
throw new Error('remote static public key is not set')
|
|
198
|
+
}
|
|
199
|
+
this.ss.mixKey(this.crypto.dh(this.e, this.rs))
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
protected readE (message: Uint8ArrayList, offset = 0): void {
|
|
204
|
+
if (this.re) {
|
|
205
|
+
throw new Error('remote ephemeral public key is already set')
|
|
206
|
+
}
|
|
207
|
+
if (message.byteLength < offset + 32) {
|
|
208
|
+
throw new Error('message is not long enough')
|
|
209
|
+
}
|
|
210
|
+
this.re = message.sublist(offset, offset + 32)
|
|
211
|
+
this.ss.mixHash(this.re)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
protected readS (message: Uint8ArrayList, offset = 0): number {
|
|
215
|
+
if (this.rs) {
|
|
216
|
+
throw new Error('remote static public key is already set')
|
|
217
|
+
}
|
|
218
|
+
const cipherLength = 32 + (this.ss.cs.hasKey() ? 16 : 0)
|
|
219
|
+
if (message.byteLength < offset + cipherLength) {
|
|
220
|
+
throw new Error('message is not long enough')
|
|
221
|
+
}
|
|
222
|
+
const temp = message.sublist(offset, offset + cipherLength)
|
|
223
|
+
this.rs = this.ss.decryptAndHash(temp)
|
|
224
|
+
return cipherLength
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
protected readEE (): void {
|
|
228
|
+
this.writeEE()
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
protected readES (): void {
|
|
232
|
+
this.writeES()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
protected readSE (): void {
|
|
236
|
+
this.writeSE()
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A IHandshakeState that's optimized for the XX pattern
|
|
242
|
+
*/
|
|
243
|
+
export class XXHandshakeState extends AbstractHandshakeState {
|
|
244
|
+
// e
|
|
245
|
+
writeMessageA (payload: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
246
|
+
return new Uint8ArrayList(this.writeE(), this.ss.encryptAndHash(payload))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// e, ee, s, es
|
|
250
|
+
writeMessageB (payload: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
251
|
+
const e = this.writeE()
|
|
252
|
+
this.writeEE()
|
|
253
|
+
const encS = this.writeS()
|
|
254
|
+
this.writeES()
|
|
255
|
+
|
|
256
|
+
return new Uint8ArrayList(e, encS, this.ss.encryptAndHash(payload))
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// s, se
|
|
260
|
+
writeMessageC (payload: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
261
|
+
const encS = this.writeS()
|
|
262
|
+
this.writeSE()
|
|
263
|
+
|
|
264
|
+
return new Uint8ArrayList(encS, this.ss.encryptAndHash(payload))
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// e
|
|
268
|
+
readMessageA (message: Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
269
|
+
try {
|
|
270
|
+
this.readE(message)
|
|
271
|
+
|
|
272
|
+
return this.ss.decryptAndHash(message.sublist(32))
|
|
273
|
+
} catch (e) {
|
|
274
|
+
throw new InvalidCryptoExchangeError(`handshake stage 0 validation fail: ${(e as Error).message}`)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// e, ee, s, es
|
|
279
|
+
readMessageB (message: Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
280
|
+
try {
|
|
281
|
+
this.readE(message)
|
|
282
|
+
this.readEE()
|
|
283
|
+
const consumed = this.readS(message, 32)
|
|
284
|
+
this.readES()
|
|
285
|
+
|
|
286
|
+
return this.ss.decryptAndHash(message.sublist(32 + consumed))
|
|
287
|
+
} catch (e) {
|
|
288
|
+
throw new InvalidCryptoExchangeError(`handshake stage 1 validation fail: ${(e as Error).message}`)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// s, se
|
|
293
|
+
readMessageC (message: Uint8ArrayList): Uint8Array | Uint8ArrayList {
|
|
294
|
+
try {
|
|
295
|
+
const consumed = this.readS(message)
|
|
296
|
+
this.readSE()
|
|
297
|
+
|
|
298
|
+
return this.ss.decryptAndHash(message.sublist(consumed))
|
|
299
|
+
} catch (e) {
|
|
300
|
+
throw new InvalidCryptoExchangeError(`handshake stage 2 validation fail: ${(e as Error).message}`)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function hashProtocolName (crypto: ICrypto, protocolName: Uint8Array): Uint8Array {
|
|
306
|
+
if (protocolName.length <= 32) {
|
|
307
|
+
const h = uint8ArrayAlloc(32)
|
|
308
|
+
h.set(protocolName)
|
|
309
|
+
return h
|
|
310
|
+
} else {
|
|
311
|
+
return crypto.hash(protocolName)
|
|
312
|
+
}
|
|
313
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Nonce } from './nonce.js'
|
|
2
|
+
import type { NoiseExtensions, NoiseHandshakePayload } from './proto/payload.js'
|
|
3
|
+
import type { ConnectionEncrypter, Logger, PrivateKey, PublicKey } from '@libp2p/interface'
|
|
4
|
+
import type { LengthPrefixedStream } from '@libp2p/utils'
|
|
5
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Crypto functions defined by the noise protocol, abstracted from the
|
|
9
|
+
* underlying implementations
|
|
10
|
+
*/
|
|
11
|
+
export interface ICrypto {
|
|
12
|
+
generateKeypair(): KeyPair
|
|
13
|
+
dh(keypair: KeyPair, publicKey: Uint8Array | Uint8ArrayList): Uint8Array
|
|
14
|
+
encrypt(plaintext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array): Uint8ArrayList | Uint8Array
|
|
15
|
+
decrypt(ciphertext: Uint8Array | Uint8ArrayList, nonce: Uint8Array, ad: Uint8Array, k: Uint8Array, dst?: Uint8Array): Uint8ArrayList | Uint8Array
|
|
16
|
+
hash(data: Uint8Array | Uint8ArrayList): Uint8Array
|
|
17
|
+
hkdf(ck: Uint8Array, ikm: Uint8Array): [Uint8Array, Uint8Array, Uint8Array]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface HandshakeParams {
|
|
21
|
+
log: Logger
|
|
22
|
+
connection: LengthPrefixedStream
|
|
23
|
+
crypto: ICrypto
|
|
24
|
+
privateKey: PrivateKey
|
|
25
|
+
prologue: Uint8Array
|
|
26
|
+
/** static keypair */
|
|
27
|
+
s: KeyPair
|
|
28
|
+
remoteIdentityKey?: PublicKey
|
|
29
|
+
extensions?: NoiseExtensions
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface HandshakeResult {
|
|
33
|
+
payload: NoiseHandshakePayload
|
|
34
|
+
encrypt (plaintext: Uint8Array | Uint8ArrayList): Uint8Array | Uint8ArrayList
|
|
35
|
+
decrypt (ciphertext: Uint8Array | Uint8ArrayList, dst?: Uint8Array): Uint8Array | Uint8ArrayList
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A CipherState object contains k and n variables, which it uses to encrypt and
|
|
40
|
+
* decrypt ciphertext.
|
|
41
|
+
*
|
|
42
|
+
* During the handshake phase each party has a single CipherState, but during
|
|
43
|
+
* the transport phase each party has two CipherState objects: one for sending,
|
|
44
|
+
* and one for receiving.
|
|
45
|
+
*/
|
|
46
|
+
export interface ICipherState {
|
|
47
|
+
/**
|
|
48
|
+
* A cipher key of 32 bytes (which may be empty). Empty is a special value
|
|
49
|
+
* which indicates k has not yet been initialized.
|
|
50
|
+
*/
|
|
51
|
+
k?: Uint8Array
|
|
52
|
+
/**
|
|
53
|
+
* An 8-byte (64-bit) unsigned integer nonce.
|
|
54
|
+
*
|
|
55
|
+
* For performance reasons, the nonce is represented as a Nonce object
|
|
56
|
+
* The nonce is treated as a uint64, even though the underlying `number` only
|
|
57
|
+
* has 52 safely-available bits.
|
|
58
|
+
*/
|
|
59
|
+
n: Nonce
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A SymmetricState object contains a CipherState plus ck and h variables. It is
|
|
64
|
+
* so-named because it encapsulates all the "symmetric crypto" used by Noise.
|
|
65
|
+
*
|
|
66
|
+
* During the handshake phase each party has a single SymmetricState, which can
|
|
67
|
+
* be deleted once the handshake is finished.
|
|
68
|
+
*/
|
|
69
|
+
export interface ISymmetricState {
|
|
70
|
+
cs: ICipherState
|
|
71
|
+
/** A chaining key of 32 bytes. */
|
|
72
|
+
ck: Uint8Array
|
|
73
|
+
/** A hash output of 32 bytes. */
|
|
74
|
+
h: Uint8Array
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A HandshakeState object contains a SymmetricState plus DH variables (s, e,
|
|
79
|
+
* rs, re) and a variable representing the handshake pattern.
|
|
80
|
+
*
|
|
81
|
+
* During the handshake phase each party has a single HandshakeState, which can
|
|
82
|
+
* be deleted once the handshake is finished.
|
|
83
|
+
*/
|
|
84
|
+
export interface IHandshakeState {
|
|
85
|
+
ss: ISymmetricState
|
|
86
|
+
/** The local static key pair */
|
|
87
|
+
s?: KeyPair
|
|
88
|
+
/** The local ephemeral key pair */
|
|
89
|
+
e?: KeyPair
|
|
90
|
+
/** The remote party's static public key */
|
|
91
|
+
rs?: Uint8Array | Uint8ArrayList
|
|
92
|
+
/** The remote party's ephemeral public key */
|
|
93
|
+
re?: Uint8Array | Uint8ArrayList
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface KeyPair {
|
|
97
|
+
publicKey: Uint8Array
|
|
98
|
+
privateKey: Uint8Array
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface INoiseExtensions {
|
|
102
|
+
webtransportCerthashes: Uint8Array[]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface INoiseConnection extends ConnectionEncrypter<INoiseExtensions> { }
|