diff --git a/package.json b/package.json index 34511f79..e4e7f5f3 100644 --- a/package.json +++ b/package.json @@ -75,9 +75,9 @@ "@libp2p/interface-peer-id": "^2.0.0", "@libp2p/logger": "^2.0.5", "@libp2p/peer-id": "^2.0.0", - "@stablelib/chacha20poly1305": "^1.0.1", - "@noble/hashes": "^1.3.0", - "@stablelib/x25519": "^1.0.3", + "@noble/curves": "^1.1.0", + "@noble/hashes": "^1.3.1", + "@noble/ciphers": "^0.1.4", "it-length-prefixed": "^9.0.1", "it-pair": "^2.0.2", "it-pb-stream": "^4.0.1", diff --git a/src/crypto/js.ts b/src/crypto/js.ts index 2a501021..203de3ff 100644 --- a/src/crypto/js.ts +++ b/src/crypto/js.ts @@ -1,7 +1,7 @@ -import { hkdf } from '@noble/hashes/hkdf' +import { chacha20_poly1305 } from '@noble/ciphers/chacha' +import { x25519 } from '@noble/curves/ed25519' +import { extract, expand } from '@noble/hashes/hkdf' import { sha256 } from '@noble/hashes/sha256' -import { ChaCha20Poly1305 } from '@stablelib/chacha20poly1305' -import * as x25519 from '@stablelib/x25519' import type { bytes, bytes32 } from '../@types/basic.js' import type { Hkdf } from '../@types/handshake.js' import type { KeyPair } from '../@types/libp2p.js' @@ -13,7 +13,9 @@ export const pureJsCrypto: ICryptoInterface = { }, getHKDF (ck: bytes32, ikm: Uint8Array): Hkdf { - const okm = hkdf(sha256, ikm, ck, undefined, 96) + const prk = extract(sha256, ikm, ck) + const okmU8Array = expand(sha256, prk, undefined, 96) + const okm = okmU8Array const k1 = okm.subarray(0, 32) const k2 = okm.subarray(32, 64) @@ -23,36 +25,38 @@ export const pureJsCrypto: ICryptoInterface = { }, generateX25519KeyPair (): KeyPair { - const keypair = x25519.generateKeyPair() + const secretKey = x25519.utils.randomPrivateKey() + const publicKey = x25519.getPublicKey(secretKey) return { - publicKey: keypair.publicKey, - privateKey: keypair.secretKey + publicKey, + privateKey: secretKey } }, generateX25519KeyPairFromSeed (seed: Uint8Array): KeyPair { - const keypair = x25519.generateKeyPairFromSeed(seed) + const publicKey = x25519.getPublicKey(seed) return { - publicKey: keypair.publicKey, - privateKey: keypair.secretKey + publicKey, + privateKey: seed } }, generateX25519SharedKey (privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array { - return x25519.sharedKey(privateKey, publicKey) + return x25519.getSharedSecret(privateKey, publicKey) }, chaCha20Poly1305Encrypt (plaintext: Uint8Array, nonce: Uint8Array, ad: Uint8Array, k: bytes32): bytes { - const ctx = new ChaCha20Poly1305(k) - - return ctx.seal(nonce, plaintext, ad) + return chacha20_poly1305(k, nonce, ad).encrypt(plaintext) }, chaCha20Poly1305Decrypt (ciphertext: Uint8Array, nonce: Uint8Array, ad: Uint8Array, k: bytes32, dst?: Uint8Array): bytes | null { - const ctx = new ChaCha20Poly1305(k) - - return ctx.open(nonce, ciphertext, ad, dst) + const result = chacha20_poly1305(k, nonce, ad).decrypt(ciphertext) + if (dst) { + dst.set(result) + return result + } + return result } } diff --git a/src/crypto/streaming.ts b/src/crypto/streaming.ts index e785a649..771204b2 100644 --- a/src/crypto/streaming.ts +++ b/src/crypto/streaming.ts @@ -1,4 +1,3 @@ -import { TAG_LENGTH } from '@stablelib/chacha20poly1305' import { NOISE_MSG_MAX_LENGTH_BYTES, NOISE_MSG_MAX_LENGTH_BYTES_WITHOUT_TAG } from '../constants.js' import { uint16BEEncode } from '../encoder.js' import type { IHandshake } from '../@types/handshake-interface.js' @@ -6,6 +5,8 @@ import type { MetricsRegistry } from '../metrics.js' import type { Transform } from 'it-stream-types' import type { Uint8ArrayList } from 'uint8arraylist' +const CHACHA_TAG_LENGTH = 16 + // Returns generator that encrypts payload from the user export function encryptStream (handshake: IHandshake, metrics?: MetricsRegistry): Transform> { return async function * (source) { @@ -36,7 +37,7 @@ export function decryptStream (handshake: IHandshake, metrics?: MetricsRegistry) end = chunk.length } - if (end - TAG_LENGTH < i) { + if (end - CHACHA_TAG_LENGTH < i) { throw new Error('Invalid chunk') } const encrypted = chunk.subarray(i, end) @@ -44,7 +45,7 @@ export function decryptStream (handshake: IHandshake, metrics?: MetricsRegistry) // see https://github.com/ChainSafe/js-libp2p-noise/pull/242#issue-1422126164 // this is ok because chacha20 reads bytes one by one and don't reread after that // it's also tested in https://github.com/ChainSafe/as-chacha20poly1305/pull/1/files#diff-25252846b58979dcaf4e41d47b3eadd7e4f335e7fb98da6c049b1f9cd011f381R48 - const dst = chunk.subarray(i, end - TAG_LENGTH) + const dst = chunk.subarray(i, end - CHACHA_TAG_LENGTH) const { plaintext: decrypted, valid } = handshake.decrypt(encrypted, handshake.session, dst) if (!valid) { metrics?.decryptErrors.increment()