Skip to content

Commit 5b4f4f2

Browse files
authored
fix: change hashing, curve and cipher libraries to @noble (#322)
* replace stablelib hashes with noble Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> * update benchmark code Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> * update noble ciphers Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com> --------- Signed-off-by: Marin Petrunic <marin.petrunic@gmail.com>
1 parent f03583b commit 5b4f4f2

File tree

3 files changed

+28
-23
lines changed

3 files changed

+28
-23
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@
7575
"@libp2p/interface-peer-id": "^2.0.0",
7676
"@libp2p/logger": "^2.0.5",
7777
"@libp2p/peer-id": "^2.0.0",
78-
"@stablelib/chacha20poly1305": "^1.0.1",
79-
"@noble/hashes": "^1.3.0",
80-
"@stablelib/x25519": "^1.0.3",
78+
"@noble/curves": "^1.1.0",
79+
"@noble/hashes": "^1.3.1",
80+
"@noble/ciphers": "^0.1.4",
8181
"it-length-prefixed": "^9.0.1",
8282
"it-pair": "^2.0.2",
8383
"it-pb-stream": "^4.0.1",

src/crypto/js.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { hkdf } from '@noble/hashes/hkdf'
1+
import { chacha20_poly1305 } from '@noble/ciphers/chacha'
2+
import { x25519 } from '@noble/curves/ed25519'
3+
import { extract, expand } from '@noble/hashes/hkdf'
24
import { sha256 } from '@noble/hashes/sha256'
3-
import { ChaCha20Poly1305 } from '@stablelib/chacha20poly1305'
4-
import * as x25519 from '@stablelib/x25519'
55
import type { bytes, bytes32 } from '../@types/basic.js'
66
import type { Hkdf } from '../@types/handshake.js'
77
import type { KeyPair } from '../@types/libp2p.js'
@@ -13,7 +13,9 @@ export const pureJsCrypto: ICryptoInterface = {
1313
},
1414

1515
getHKDF (ck: bytes32, ikm: Uint8Array): Hkdf {
16-
const okm = hkdf(sha256, ikm, ck, undefined, 96)
16+
const prk = extract(sha256, ikm, ck)
17+
const okmU8Array = expand(sha256, prk, undefined, 96)
18+
const okm = okmU8Array
1719

1820
const k1 = okm.subarray(0, 32)
1921
const k2 = okm.subarray(32, 64)
@@ -23,36 +25,38 @@ export const pureJsCrypto: ICryptoInterface = {
2325
},
2426

2527
generateX25519KeyPair (): KeyPair {
26-
const keypair = x25519.generateKeyPair()
28+
const secretKey = x25519.utils.randomPrivateKey()
29+
const publicKey = x25519.getPublicKey(secretKey)
2730

2831
return {
29-
publicKey: keypair.publicKey,
30-
privateKey: keypair.secretKey
32+
publicKey,
33+
privateKey: secretKey
3134
}
3235
},
3336

3437
generateX25519KeyPairFromSeed (seed: Uint8Array): KeyPair {
35-
const keypair = x25519.generateKeyPairFromSeed(seed)
38+
const publicKey = x25519.getPublicKey(seed)
3639

3740
return {
38-
publicKey: keypair.publicKey,
39-
privateKey: keypair.secretKey
41+
publicKey,
42+
privateKey: seed
4043
}
4144
},
4245

4346
generateX25519SharedKey (privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array {
44-
return x25519.sharedKey(privateKey, publicKey)
47+
return x25519.getSharedSecret(privateKey, publicKey)
4548
},
4649

4750
chaCha20Poly1305Encrypt (plaintext: Uint8Array, nonce: Uint8Array, ad: Uint8Array, k: bytes32): bytes {
48-
const ctx = new ChaCha20Poly1305(k)
49-
50-
return ctx.seal(nonce, plaintext, ad)
51+
return chacha20_poly1305(k, nonce, ad).encrypt(plaintext)
5152
},
5253

5354
chaCha20Poly1305Decrypt (ciphertext: Uint8Array, nonce: Uint8Array, ad: Uint8Array, k: bytes32, dst?: Uint8Array): bytes | null {
54-
const ctx = new ChaCha20Poly1305(k)
55-
56-
return ctx.open(nonce, ciphertext, ad, dst)
55+
const result = chacha20_poly1305(k, nonce, ad).decrypt(ciphertext)
56+
if (dst) {
57+
dst.set(result)
58+
return result
59+
}
60+
return result
5761
}
5862
}

src/crypto/streaming.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { TAG_LENGTH } from '@stablelib/chacha20poly1305'
21
import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from '../constants.js'
32
import { uint16BEEncode } from '../encoder.js'
43
import type { IHandshake } from '../@types/handshake-interface.js'
54
import type { MetricsRegistry } from '../metrics.js'
65
import type { Transform } from 'it-stream-types'
76
import type { Uint8ArrayList } from 'uint8arraylist'
87

8+
const CHACHA_TAG_LENGTH = 16
9+
910
// Returns generator that encrypts payload from the user
1011
export function encryptStream (handshake: IHandshake, metrics?: MetricsRegistry): Transform<AsyncIterable<Uint8Array>> {
1112
return async function * (source) {
@@ -36,15 +37,15 @@ export function decryptStream (handshake: IHandshake, metrics?: MetricsRegistry)
3637
end = chunk.length
3738
}
3839

39-
if (end - TAG_LENGTH < i) {
40+
if (end - CHACHA_TAG_LENGTH < i) {
4041
throw new Error('Invalid chunk')
4142
}
4243
const encrypted = chunk.subarray(i, end)
4344
// memory allocation is not cheap so reuse the encrypted Uint8Array
4445
// see https://github.com/ChainSafe/js-libp2p-noise/pull/242#issue-1422126164
4546
// this is ok because chacha20 reads bytes one by one and don't reread after that
4647
// 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 dst = chunk.subarray(i, end - CHACHA_TAG_LENGTH)
4849
const { plaintext: decrypted, valid } = handshake.decrypt(encrypted, handshake.session, dst)
4950
if (!valid) {
5051
metrics?.decryptErrors.increment()

0 commit comments

Comments
 (0)