@libp2p/noise 12.0.1 → 16.1.4-8484de8a2
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/crypto/streaming.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { TAG_LENGTH } from '@stablelib/chacha20poly1305'
|
|
2
|
-
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from '../constants.js'
|
|
3
|
-
import { uint16BEEncode } from '../encoder.js'
|
|
4
|
-
import type { IHandshake } from '../@types/handshake-interface.js'
|
|
5
|
-
import type { MetricsRegistry } from '../metrics.js'
|
|
6
|
-
import type { Transform } from 'it-stream-types'
|
|
7
|
-
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
8
|
-
|
|
9
|
-
// Returns generator that encrypts payload from the user
|
|
10
|
-
export function encryptStream (handshake: IHandshake, metrics?: MetricsRegistry): Transform<AsyncIterable<Uint8Array>> {
|
|
11
|
-
return async function * (source) {
|
|
12
|
-
for await (const chunk of source) {
|
|
13
|
-
for (let i = 0; i < chunk.length; i += NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG) {
|
|
14
|
-
let end = i + NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG
|
|
15
|
-
if (end > chunk.length) {
|
|
16
|
-
end = chunk.length
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const data = handshake.encrypt(chunk.subarray(i, end), handshake.session)
|
|
20
|
-
metrics?.encryptedPackets.increment()
|
|
21
|
-
|
|
22
|
-
yield uint16BEEncode(data.byteLength)
|
|
23
|
-
yield data
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Decrypt received payload to the user
|
|
30
|
-
export function decryptStream (handshake: IHandshake, metrics?: MetricsRegistry): Transform<AsyncIterable<Uint8ArrayList>, AsyncIterable<Uint8Array>> {
|
|
31
|
-
return async function * (source) {
|
|
32
|
-
for await (const chunk of source) {
|
|
33
|
-
for (let i = 0; i < chunk.length; i += NOISE_MSG_MAX_LENGTH_BYTES) {
|
|
34
|
-
let end = i + NOISE_MSG_MAX_LENGTH_BYTES
|
|
35
|
-
if (end > chunk.length) {
|
|
36
|
-
end = chunk.length
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (end - TAG_LENGTH < i) {
|
|
40
|
-
throw new Error('Invalid chunk')
|
|
41
|
-
}
|
|
42
|
-
const encrypted = chunk.subarray(i, end)
|
|
43
|
-
// memory allocation is not cheap so reuse the encrypted Uint8Array
|
|
44
|
-
// see https://github.com/ChainSafe/js-libp2p-noise/pull/242#issue-1422126164
|
|
45
|
-
// this is ok because chacha20 reads bytes one by one and don't reread after that
|
|
46
|
-
// it's also tested in https://github.com/ChainSafe/as-chacha20poly1305/pull/1/files#diff-25252846b58979dcaf4e41d47b3eadd7e4f335e7fb98da6c049b1f9cd011f381R48
|
|
47
|
-
const dst = chunk.subarray(i, end - TAG_LENGTH)
|
|
48
|
-
const { plaintext: decrypted, valid } = handshake.decrypt(encrypted, handshake.session, dst)
|
|
49
|
-
if (!valid) {
|
|
50
|
-
metrics?.decryptErrors.increment()
|
|
51
|
-
throw new Error('Failed to validate decrypted chunk')
|
|
52
|
-
}
|
|
53
|
-
metrics?.decryptedPackets.increment()
|
|
54
|
-
yield decrypted
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
package/src/handshake-xx.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { InvalidCryptoExchangeError, UnexpectedPeerError } from '@libp2p/interface/errors'
|
|
2
|
-
import { decode0, decode1, decode2, encode0, encode1, encode2 } from './encoder.js'
|
|
3
|
-
import { XX } from './handshakes/xx.js'
|
|
4
|
-
import {
|
|
5
|
-
logger,
|
|
6
|
-
logLocalStaticKeys,
|
|
7
|
-
logLocalEphemeralKeys,
|
|
8
|
-
logRemoteEphemeralKey,
|
|
9
|
-
logRemoteStaticKey,
|
|
10
|
-
logCipherState
|
|
11
|
-
} from './logger.js'
|
|
12
|
-
import {
|
|
13
|
-
decodePayload,
|
|
14
|
-
getPeerIdFromPayload,
|
|
15
|
-
verifySignedPayload
|
|
16
|
-
} from './utils.js'
|
|
17
|
-
import type { bytes, bytes32 } from './@types/basic.js'
|
|
18
|
-
import type { IHandshake } from './@types/handshake-interface.js'
|
|
19
|
-
import type { CipherState, NoiseSession } from './@types/handshake.js'
|
|
20
|
-
import type { KeyPair } from './@types/libp2p.js'
|
|
21
|
-
import type { ICryptoInterface } from './crypto.js'
|
|
22
|
-
import type { NoiseExtensions } from './proto/payload.js'
|
|
23
|
-
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
24
|
-
import type { LengthPrefixedStream } from 'it-length-prefixed-stream'
|
|
25
|
-
|
|
26
|
-
export class XXHandshake implements IHandshake {
|
|
27
|
-
public isInitiator: boolean
|
|
28
|
-
public session: NoiseSession
|
|
29
|
-
public remotePeer!: PeerId
|
|
30
|
-
public remoteExtensions: NoiseExtensions = { webtransportCerthashes: [] }
|
|
31
|
-
|
|
32
|
-
protected payload: bytes
|
|
33
|
-
protected connection: LengthPrefixedStream
|
|
34
|
-
protected xx: XX
|
|
35
|
-
protected staticKeypair: KeyPair
|
|
36
|
-
|
|
37
|
-
private readonly prologue: bytes32
|
|
38
|
-
|
|
39
|
-
constructor (
|
|
40
|
-
isInitiator: boolean,
|
|
41
|
-
payload: bytes,
|
|
42
|
-
prologue: bytes32,
|
|
43
|
-
crypto: ICryptoInterface,
|
|
44
|
-
staticKeypair: KeyPair,
|
|
45
|
-
connection: LengthPrefixedStream,
|
|
46
|
-
remotePeer?: PeerId,
|
|
47
|
-
handshake?: XX
|
|
48
|
-
) {
|
|
49
|
-
this.isInitiator = isInitiator
|
|
50
|
-
this.payload = payload
|
|
51
|
-
this.prologue = prologue
|
|
52
|
-
this.staticKeypair = staticKeypair
|
|
53
|
-
this.connection = connection
|
|
54
|
-
if (remotePeer) {
|
|
55
|
-
this.remotePeer = remotePeer
|
|
56
|
-
}
|
|
57
|
-
this.xx = handshake ?? new XX(crypto)
|
|
58
|
-
this.session = this.xx.initSession(this.isInitiator, this.prologue, this.staticKeypair)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// stage 0
|
|
62
|
-
public async propose (): Promise<void> {
|
|
63
|
-
logLocalStaticKeys(this.session.hs.s)
|
|
64
|
-
if (this.isInitiator) {
|
|
65
|
-
logger.trace('Stage 0 - Initiator starting to send first message.')
|
|
66
|
-
const messageBuffer = this.xx.sendMessage(this.session, new Uint8Array(0))
|
|
67
|
-
await this.connection.write(encode0(messageBuffer))
|
|
68
|
-
logger.trace('Stage 0 - Initiator finished sending first message.')
|
|
69
|
-
logLocalEphemeralKeys(this.session.hs.e)
|
|
70
|
-
} else {
|
|
71
|
-
logger.trace('Stage 0 - Responder waiting to receive first message...')
|
|
72
|
-
const receivedMessageBuffer = decode0((await this.connection.read()).subarray())
|
|
73
|
-
const { valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
|
|
74
|
-
if (!valid) {
|
|
75
|
-
throw new InvalidCryptoExchangeError('xx handshake stage 0 validation fail')
|
|
76
|
-
}
|
|
77
|
-
logger.trace('Stage 0 - Responder received first message.')
|
|
78
|
-
logRemoteEphemeralKey(this.session.hs.re)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// stage 1
|
|
83
|
-
public async exchange (): Promise<void> {
|
|
84
|
-
if (this.isInitiator) {
|
|
85
|
-
logger.trace('Stage 1 - Initiator waiting to receive first message from responder...')
|
|
86
|
-
const receivedMessageBuffer = decode1((await this.connection.read()).subarray())
|
|
87
|
-
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
|
|
88
|
-
if (!valid) {
|
|
89
|
-
throw new InvalidCryptoExchangeError('xx handshake stage 1 validation fail')
|
|
90
|
-
}
|
|
91
|
-
logger.trace('Stage 1 - Initiator received the message.')
|
|
92
|
-
logRemoteEphemeralKey(this.session.hs.re)
|
|
93
|
-
logRemoteStaticKey(this.session.hs.rs)
|
|
94
|
-
|
|
95
|
-
logger.trace("Initiator going to check remote's signature...")
|
|
96
|
-
try {
|
|
97
|
-
const decodedPayload = decodePayload(plaintext)
|
|
98
|
-
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
|
|
99
|
-
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
|
100
|
-
this.setRemoteNoiseExtension(decodedPayload.extensions)
|
|
101
|
-
} catch (e) {
|
|
102
|
-
const err = e as Error
|
|
103
|
-
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
|
|
104
|
-
}
|
|
105
|
-
logger.trace('All good with the signature!')
|
|
106
|
-
} else {
|
|
107
|
-
logger.trace('Stage 1 - Responder sending out first message with signed payload and static key.')
|
|
108
|
-
const messageBuffer = this.xx.sendMessage(this.session, this.payload)
|
|
109
|
-
await this.connection.write(encode1(messageBuffer))
|
|
110
|
-
logger.trace('Stage 1 - Responder sent the second handshake message with signed payload.')
|
|
111
|
-
logLocalEphemeralKeys(this.session.hs.e)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// stage 2
|
|
116
|
-
public async finish (): Promise<void> {
|
|
117
|
-
if (this.isInitiator) {
|
|
118
|
-
logger.trace('Stage 2 - Initiator sending third handshake message.')
|
|
119
|
-
const messageBuffer = this.xx.sendMessage(this.session, this.payload)
|
|
120
|
-
await this.connection.write(encode2(messageBuffer))
|
|
121
|
-
logger.trace('Stage 2 - Initiator sent message with signed payload.')
|
|
122
|
-
} else {
|
|
123
|
-
logger.trace('Stage 2 - Responder waiting for third handshake message...')
|
|
124
|
-
const receivedMessageBuffer = decode2((await this.connection.read()).subarray())
|
|
125
|
-
const { plaintext, valid } = this.xx.recvMessage(this.session, receivedMessageBuffer)
|
|
126
|
-
if (!valid) {
|
|
127
|
-
throw new InvalidCryptoExchangeError('xx handshake stage 2 validation fail')
|
|
128
|
-
}
|
|
129
|
-
logger.trace('Stage 2 - Responder received the message, finished handshake.')
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const decodedPayload = decodePayload(plaintext)
|
|
133
|
-
this.remotePeer = this.remotePeer || await getPeerIdFromPayload(decodedPayload)
|
|
134
|
-
await verifySignedPayload(this.session.hs.rs, decodedPayload, this.remotePeer)
|
|
135
|
-
this.setRemoteNoiseExtension(decodedPayload.extensions)
|
|
136
|
-
} catch (e) {
|
|
137
|
-
const err = e as Error
|
|
138
|
-
throw new UnexpectedPeerError(`Error occurred while verifying signed payload: ${err.message}`)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
logCipherState(this.session)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
public encrypt (plaintext: Uint8Array, session: NoiseSession): bytes {
|
|
145
|
-
const cs = this.getCS(session)
|
|
146
|
-
|
|
147
|
-
return this.xx.encryptWithAd(cs, new Uint8Array(0), plaintext)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public decrypt (ciphertext: Uint8Array, session: NoiseSession, dst?: Uint8Array): { plaintext: bytes, valid: boolean } {
|
|
151
|
-
const cs = this.getCS(session, false)
|
|
152
|
-
|
|
153
|
-
return this.xx.decryptWithAd(cs, new Uint8Array(0), ciphertext, dst)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
public getRemoteStaticKey (): bytes {
|
|
157
|
-
return this.session.hs.rs
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private getCS (session: NoiseSession, encryption = true): CipherState {
|
|
161
|
-
if (!session.cs1 || !session.cs2) {
|
|
162
|
-
throw new InvalidCryptoExchangeError('Handshake not completed properly, cipher state does not exist.')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (this.isInitiator) {
|
|
166
|
-
return encryption ? session.cs1 : session.cs2
|
|
167
|
-
} else {
|
|
168
|
-
return encryption ? session.cs2 : session.cs1
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
protected setRemoteNoiseExtension (e: NoiseExtensions | null | undefined): void {
|
|
173
|
-
if (e) {
|
|
174
|
-
this.remoteExtensions = e
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { fromString as uint8ArrayFromString } from 'uint8arrays'
|
|
2
|
-
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
3
|
-
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
|
|
4
|
-
import { logger } from '../logger.js'
|
|
5
|
-
import { Nonce } from '../nonce.js'
|
|
6
|
-
import type { bytes, bytes32 } from '../@types/basic.js'
|
|
7
|
-
import type { CipherState, MessageBuffer, SymmetricState } from '../@types/handshake.js'
|
|
8
|
-
import type { ICryptoInterface } from '../crypto.js'
|
|
9
|
-
|
|
10
|
-
export interface DecryptedResult {
|
|
11
|
-
plaintext: bytes
|
|
12
|
-
valid: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface SplitState {
|
|
16
|
-
cs1: CipherState
|
|
17
|
-
cs2: CipherState
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export abstract class AbstractHandshake {
|
|
21
|
-
public crypto: ICryptoInterface
|
|
22
|
-
|
|
23
|
-
constructor (crypto: ICryptoInterface) {
|
|
24
|
-
this.crypto = crypto
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
public encryptWithAd (cs: CipherState, ad: Uint8Array, plaintext: Uint8Array): bytes {
|
|
28
|
-
const e = this.encrypt(cs.k, cs.n, ad, plaintext)
|
|
29
|
-
cs.n.increment()
|
|
30
|
-
|
|
31
|
-
return e
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public decryptWithAd (cs: CipherState, ad: Uint8Array, ciphertext: Uint8Array, dst?: Uint8Array): DecryptedResult {
|
|
35
|
-
const { plaintext, valid } = this.decrypt(cs.k, cs.n, ad, ciphertext, dst)
|
|
36
|
-
if (valid) cs.n.increment()
|
|
37
|
-
|
|
38
|
-
return { plaintext, valid }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Cipher state related
|
|
42
|
-
protected hasKey (cs: CipherState): boolean {
|
|
43
|
-
return !this.isEmptyKey(cs.k)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
protected createEmptyKey (): bytes32 {
|
|
47
|
-
return new Uint8Array(32)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
protected isEmptyKey (k: bytes32): boolean {
|
|
51
|
-
const emptyKey = this.createEmptyKey()
|
|
52
|
-
return uint8ArrayEquals(emptyKey, k)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
protected encrypt (k: bytes32, n: Nonce, ad: Uint8Array, plaintext: Uint8Array): bytes {
|
|
56
|
-
n.assertValue()
|
|
57
|
-
|
|
58
|
-
return this.crypto.chaCha20Poly1305Encrypt(plaintext, n.getBytes(), ad, k)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
protected encryptAndHash (ss: SymmetricState, plaintext: bytes): bytes {
|
|
62
|
-
let ciphertext
|
|
63
|
-
if (this.hasKey(ss.cs)) {
|
|
64
|
-
ciphertext = this.encryptWithAd(ss.cs, ss.h, plaintext)
|
|
65
|
-
} else {
|
|
66
|
-
ciphertext = plaintext
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this.mixHash(ss, ciphertext)
|
|
70
|
-
return ciphertext
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
protected decrypt (k: bytes32, n: Nonce, ad: bytes, ciphertext: bytes, dst?: Uint8Array): DecryptedResult {
|
|
74
|
-
n.assertValue()
|
|
75
|
-
|
|
76
|
-
const encryptedMessage = this.crypto.chaCha20Poly1305Decrypt(ciphertext, n.getBytes(), ad, k, dst)
|
|
77
|
-
|
|
78
|
-
if (encryptedMessage) {
|
|
79
|
-
return {
|
|
80
|
-
plaintext: encryptedMessage,
|
|
81
|
-
valid: true
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
return {
|
|
85
|
-
plaintext: new Uint8Array(0),
|
|
86
|
-
valid: false
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
protected decryptAndHash (ss: SymmetricState, ciphertext: bytes): DecryptedResult {
|
|
92
|
-
let plaintext: bytes; let valid = true
|
|
93
|
-
if (this.hasKey(ss.cs)) {
|
|
94
|
-
({ plaintext, valid } = this.decryptWithAd(ss.cs, ss.h, ciphertext))
|
|
95
|
-
} else {
|
|
96
|
-
plaintext = ciphertext
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
this.mixHash(ss, ciphertext)
|
|
100
|
-
return { plaintext, valid }
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
protected dh (privateKey: bytes32, publicKey: bytes32): bytes32 {
|
|
104
|
-
try {
|
|
105
|
-
const derivedU8 = this.crypto.generateX25519SharedKey(privateKey, publicKey)
|
|
106
|
-
|
|
107
|
-
if (derivedU8.length === 32) {
|
|
108
|
-
return derivedU8
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return derivedU8.subarray(0, 32)
|
|
112
|
-
} catch (e) {
|
|
113
|
-
const err = e as Error
|
|
114
|
-
logger.error(err)
|
|
115
|
-
return new Uint8Array(32)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
protected mixHash (ss: SymmetricState, data: bytes): void {
|
|
120
|
-
ss.h = this.getHash(ss.h, data)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
protected getHash (a: Uint8Array, b: Uint8Array): bytes32 {
|
|
124
|
-
const u = this.crypto.hashSHA256(uint8ArrayConcat([a, b], a.length + b.length))
|
|
125
|
-
return u
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
protected mixKey (ss: SymmetricState, ikm: bytes32): void {
|
|
129
|
-
const [ck, tempK] = this.crypto.getHKDF(ss.ck, ikm)
|
|
130
|
-
ss.cs = this.initializeKey(tempK)
|
|
131
|
-
ss.ck = ck
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
protected initializeKey (k: bytes32): CipherState {
|
|
135
|
-
return { k, n: new Nonce() }
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Symmetric state related
|
|
139
|
-
|
|
140
|
-
protected initializeSymmetric (protocolName: string): SymmetricState {
|
|
141
|
-
const protocolNameBytes = uint8ArrayFromString(protocolName, 'utf-8')
|
|
142
|
-
const h = this.hashProtocolName(protocolNameBytes)
|
|
143
|
-
|
|
144
|
-
const ck = h
|
|
145
|
-
const key = this.createEmptyKey()
|
|
146
|
-
const cs: CipherState = this.initializeKey(key)
|
|
147
|
-
|
|
148
|
-
return { cs, ck, h }
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
protected hashProtocolName (protocolName: Uint8Array): bytes32 {
|
|
152
|
-
if (protocolName.length <= 32) {
|
|
153
|
-
const h = new Uint8Array(32)
|
|
154
|
-
h.set(protocolName)
|
|
155
|
-
return h
|
|
156
|
-
} else {
|
|
157
|
-
return this.getHash(protocolName, new Uint8Array(0))
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
protected split (ss: SymmetricState): SplitState {
|
|
162
|
-
const [tempk1, tempk2] = this.crypto.getHKDF(ss.ck, new Uint8Array(0))
|
|
163
|
-
const cs1 = this.initializeKey(tempk1)
|
|
164
|
-
const cs2 = this.initializeKey(tempk2)
|
|
165
|
-
|
|
166
|
-
return { cs1, cs2 }
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
protected writeMessageRegular (cs: CipherState, payload: bytes): MessageBuffer {
|
|
170
|
-
const ciphertext = this.encryptWithAd(cs, new Uint8Array(0), payload)
|
|
171
|
-
const ne = this.createEmptyKey()
|
|
172
|
-
const ns = new Uint8Array(0)
|
|
173
|
-
|
|
174
|
-
return { ne, ns, ciphertext }
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
protected readMessageRegular (cs: CipherState, message: MessageBuffer): DecryptedResult {
|
|
178
|
-
return this.decryptWithAd(cs, new Uint8Array(0), message.ciphertext)
|
|
179
|
-
}
|
|
180
|
-
}
|
package/src/handshakes/xx.ts
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { isValidPublicKey } from '../utils.js'
|
|
2
|
-
import { AbstractHandshake, type DecryptedResult } from './abstract-handshake.js'
|
|
3
|
-
import type { bytes32, bytes } from '../@types/basic.js'
|
|
4
|
-
import type { CipherState, HandshakeState, MessageBuffer, NoiseSession } from '../@types/handshake.js'
|
|
5
|
-
import type { KeyPair } from '../@types/libp2p.js'
|
|
6
|
-
|
|
7
|
-
export class XX extends AbstractHandshake {
|
|
8
|
-
private initializeInitiator (prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState {
|
|
9
|
-
const name = 'Noise_XX_25519_ChaChaPoly_SHA256'
|
|
10
|
-
const ss = this.initializeSymmetric(name)
|
|
11
|
-
this.mixHash(ss, prologue)
|
|
12
|
-
const re = new Uint8Array(32)
|
|
13
|
-
|
|
14
|
-
return { ss, s, rs, psk, re }
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
private initializeResponder (prologue: bytes32, s: KeyPair, rs: bytes32, psk: bytes32): HandshakeState {
|
|
18
|
-
const name = 'Noise_XX_25519_ChaChaPoly_SHA256'
|
|
19
|
-
const ss = this.initializeSymmetric(name)
|
|
20
|
-
this.mixHash(ss, prologue)
|
|
21
|
-
const re = new Uint8Array(32)
|
|
22
|
-
|
|
23
|
-
return { ss, s, rs, psk, re }
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private writeMessageA (hs: HandshakeState, payload: bytes, e?: KeyPair): MessageBuffer {
|
|
27
|
-
const ns = new Uint8Array(0)
|
|
28
|
-
|
|
29
|
-
if (e !== undefined) {
|
|
30
|
-
hs.e = e
|
|
31
|
-
} else {
|
|
32
|
-
hs.e = this.crypto.generateX25519KeyPair()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const ne = hs.e.publicKey
|
|
36
|
-
|
|
37
|
-
this.mixHash(hs.ss, ne)
|
|
38
|
-
const ciphertext = this.encryptAndHash(hs.ss, payload)
|
|
39
|
-
|
|
40
|
-
return { ne, ns, ciphertext }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
private writeMessageB (hs: HandshakeState, payload: bytes): MessageBuffer {
|
|
44
|
-
hs.e = this.crypto.generateX25519KeyPair()
|
|
45
|
-
const ne = hs.e.publicKey
|
|
46
|
-
this.mixHash(hs.ss, ne)
|
|
47
|
-
|
|
48
|
-
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re))
|
|
49
|
-
const spk = hs.s.publicKey
|
|
50
|
-
const ns = this.encryptAndHash(hs.ss, spk)
|
|
51
|
-
|
|
52
|
-
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re))
|
|
53
|
-
const ciphertext = this.encryptAndHash(hs.ss, payload)
|
|
54
|
-
|
|
55
|
-
return { ne, ns, ciphertext }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private writeMessageC (hs: HandshakeState, payload: bytes): { messageBuffer: MessageBuffer, cs1: CipherState, cs2: CipherState, h: bytes } {
|
|
59
|
-
const spk = hs.s.publicKey
|
|
60
|
-
const ns = this.encryptAndHash(hs.ss, spk)
|
|
61
|
-
this.mixKey(hs.ss, this.dh(hs.s.privateKey, hs.re))
|
|
62
|
-
const ciphertext = this.encryptAndHash(hs.ss, payload)
|
|
63
|
-
const ne = this.createEmptyKey()
|
|
64
|
-
const messageBuffer: MessageBuffer = { ne, ns, ciphertext }
|
|
65
|
-
const { cs1, cs2 } = this.split(hs.ss)
|
|
66
|
-
|
|
67
|
-
return { h: hs.ss.h, messageBuffer, cs1, cs2 }
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private readMessageA (hs: HandshakeState, message: MessageBuffer): DecryptedResult {
|
|
71
|
-
if (isValidPublicKey(message.ne)) {
|
|
72
|
-
hs.re = message.ne
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this.mixHash(hs.ss, hs.re)
|
|
76
|
-
return this.decryptAndHash(hs.ss, message.ciphertext)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private readMessageB (hs: HandshakeState, message: MessageBuffer): DecryptedResult {
|
|
80
|
-
if (isValidPublicKey(message.ne)) {
|
|
81
|
-
hs.re = message.ne
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this.mixHash(hs.ss, hs.re)
|
|
85
|
-
if (!hs.e) {
|
|
86
|
-
throw new Error('Handshake state `e` param is missing.')
|
|
87
|
-
}
|
|
88
|
-
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.re))
|
|
89
|
-
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
|
|
90
|
-
if (valid1 && isValidPublicKey(ns)) {
|
|
91
|
-
hs.rs = ns
|
|
92
|
-
}
|
|
93
|
-
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs))
|
|
94
|
-
const { plaintext, valid: valid2 } = this.decryptAndHash(hs.ss, message.ciphertext)
|
|
95
|
-
return { plaintext, valid: (valid1 && valid2) }
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private readMessageC (hs: HandshakeState, message: MessageBuffer): { h: bytes, plaintext: bytes, valid: boolean, cs1: CipherState, cs2: CipherState } {
|
|
99
|
-
const { plaintext: ns, valid: valid1 } = this.decryptAndHash(hs.ss, message.ns)
|
|
100
|
-
if (valid1 && isValidPublicKey(ns)) {
|
|
101
|
-
hs.rs = ns
|
|
102
|
-
}
|
|
103
|
-
if (!hs.e) {
|
|
104
|
-
throw new Error('Handshake state `e` param is missing.')
|
|
105
|
-
}
|
|
106
|
-
this.mixKey(hs.ss, this.dh(hs.e.privateKey, hs.rs))
|
|
107
|
-
|
|
108
|
-
const { plaintext, valid: valid2 } = this.decryptAndHash(hs.ss, message.ciphertext)
|
|
109
|
-
const { cs1, cs2 } = this.split(hs.ss)
|
|
110
|
-
|
|
111
|
-
return { h: hs.ss.h, plaintext, valid: (valid1 && valid2), cs1, cs2 }
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
public initSession (initiator: boolean, prologue: bytes32, s: KeyPair): NoiseSession {
|
|
115
|
-
const psk = this.createEmptyKey()
|
|
116
|
-
const rs = new Uint8Array(32) // no static key yet
|
|
117
|
-
let hs
|
|
118
|
-
|
|
119
|
-
if (initiator) {
|
|
120
|
-
hs = this.initializeInitiator(prologue, s, rs, psk)
|
|
121
|
-
} else {
|
|
122
|
-
hs = this.initializeResponder(prologue, s, rs, psk)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
hs,
|
|
127
|
-
i: initiator,
|
|
128
|
-
mc: 0
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
public sendMessage (session: NoiseSession, message: bytes, ephemeral?: KeyPair): MessageBuffer {
|
|
133
|
-
let messageBuffer: MessageBuffer
|
|
134
|
-
if (session.mc === 0) {
|
|
135
|
-
messageBuffer = this.writeMessageA(session.hs, message, ephemeral)
|
|
136
|
-
} else if (session.mc === 1) {
|
|
137
|
-
messageBuffer = this.writeMessageB(session.hs, message)
|
|
138
|
-
} else if (session.mc === 2) {
|
|
139
|
-
const { h, messageBuffer: resultingBuffer, cs1, cs2 } = this.writeMessageC(session.hs, message)
|
|
140
|
-
messageBuffer = resultingBuffer
|
|
141
|
-
session.h = h
|
|
142
|
-
session.cs1 = cs1
|
|
143
|
-
session.cs2 = cs2
|
|
144
|
-
} else if (session.mc > 2) {
|
|
145
|
-
if (session.i) {
|
|
146
|
-
if (!session.cs1) {
|
|
147
|
-
throw new Error('CS1 (cipher state) is not defined')
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
messageBuffer = this.writeMessageRegular(session.cs1, message)
|
|
151
|
-
} else {
|
|
152
|
-
if (!session.cs2) {
|
|
153
|
-
throw new Error('CS2 (cipher state) is not defined')
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
messageBuffer = this.writeMessageRegular(session.cs2, message)
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
159
|
-
throw new Error('Session invalid.')
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
session.mc++
|
|
163
|
-
return messageBuffer
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public recvMessage (session: NoiseSession, message: MessageBuffer): DecryptedResult {
|
|
167
|
-
let plaintext: bytes = new Uint8Array(0)
|
|
168
|
-
let valid = false
|
|
169
|
-
if (session.mc === 0) {
|
|
170
|
-
({ plaintext, valid } = this.readMessageA(session.hs, message))
|
|
171
|
-
} else if (session.mc === 1) {
|
|
172
|
-
({ plaintext, valid } = this.readMessageB(session.hs, message))
|
|
173
|
-
} else if (session.mc === 2) {
|
|
174
|
-
const { h, plaintext: resultingPlaintext, valid: resultingValid, cs1, cs2 } = this.readMessageC(session.hs, message)
|
|
175
|
-
plaintext = resultingPlaintext
|
|
176
|
-
valid = resultingValid
|
|
177
|
-
session.h = h
|
|
178
|
-
session.cs1 = cs1
|
|
179
|
-
session.cs2 = cs2
|
|
180
|
-
}
|
|
181
|
-
session.mc++
|
|
182
|
-
return { plaintext, valid }
|
|
183
|
-
}
|
|
184
|
-
}
|