-
-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: selectively use nodejs crypto for noise (#5900)
* chore: add noise perf test * feat: use nodejs crypto for noise * feat: selectively use nodejs or as implementation
- Loading branch information
1 parent
37f404f
commit 8794025
Showing
4 changed files
with
126 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,85 @@ | ||
import crypto from "node:crypto"; | ||
import type {ConnectionEncrypter} from "@libp2p/interface/connection-encrypter"; | ||
import {newInstance, ChaCha20Poly1305} from "@chainsafe/as-chacha20poly1305"; | ||
import {ICryptoInterface, noise, pureJsCrypto} from "@chainsafe/libp2p-noise"; | ||
import {digest} from "@chainsafe/as-sha256"; | ||
|
||
type Bytes = Uint8Array; | ||
type Bytes32 = Uint8Array; | ||
import {newInstance, ChaCha20Poly1305} from "@chainsafe/as-chacha20poly1305"; | ||
|
||
const ctx = newInstance(); | ||
const asImpl = new ChaCha20Poly1305(ctx); | ||
|
||
// same to stablelib but we use as-chacha20poly1305 and as-sha256 | ||
const lodestarCrypto: ICryptoInterface = { | ||
...pureJsCrypto, | ||
hashSHA256(data: Uint8Array): Uint8Array { | ||
return digest(data); | ||
const CHACHA_POLY1305 = "chacha20-poly1305"; | ||
|
||
const nodeCrypto: Pick<ICryptoInterface, "hashSHA256" | "chaCha20Poly1305Encrypt" | "chaCha20Poly1305Decrypt"> = { | ||
hashSHA256(data) { | ||
return crypto.createHash("sha256").update(data).digest(); | ||
}, | ||
|
||
chaCha20Poly1305Encrypt(plaintext: Uint8Array, nonce: Uint8Array, ad: Uint8Array, k: Bytes32): Bytes { | ||
return asImpl.seal(k, nonce, plaintext, ad); | ||
chaCha20Poly1305Encrypt(plaintext, nonce, ad, k) { | ||
const cipher = crypto.createCipheriv(CHACHA_POLY1305, k, nonce, { | ||
authTagLength: 16, | ||
}); | ||
cipher.setAAD(ad, {plaintextLength: plaintext.byteLength}); | ||
const updated = cipher.update(plaintext); | ||
const final = cipher.final(); | ||
const tag = cipher.getAuthTag(); | ||
|
||
const encrypted = Buffer.concat([updated, tag, final]); | ||
return encrypted; | ||
}, | ||
|
||
chaCha20Poly1305Decrypt( | ||
ciphertext: Uint8Array, | ||
nonce: Uint8Array, | ||
ad: Uint8Array, | ||
k: Bytes32, | ||
dst?: Uint8Array | ||
): Bytes | null { | ||
chaCha20Poly1305Decrypt(ciphertext, nonce, ad, k, _dst) { | ||
const authTag = ciphertext.slice(ciphertext.length - 16); | ||
const text = ciphertext.slice(0, ciphertext.length - 16); | ||
const decipher = crypto.createDecipheriv(CHACHA_POLY1305, k, nonce, { | ||
authTagLength: 16, | ||
}); | ||
decipher.setAAD(ad, { | ||
plaintextLength: text.byteLength, | ||
}); | ||
decipher.setAuthTag(authTag); | ||
const updated = decipher.update(text); | ||
const final = decipher.final(); | ||
if (final.byteLength > 0) { | ||
return Buffer.concat([updated, final]); | ||
} | ||
return updated; | ||
}, | ||
}; | ||
|
||
const asCrypto: Pick<ICryptoInterface, "hashSHA256" | "chaCha20Poly1305Encrypt" | "chaCha20Poly1305Decrypt"> = { | ||
hashSHA256(data) { | ||
return digest(data); | ||
}, | ||
chaCha20Poly1305Encrypt(plaintext, nonce, ad, k) { | ||
return asImpl.seal(k, nonce, plaintext, ad); | ||
}, | ||
chaCha20Poly1305Decrypt(ciphertext, nonce, ad, k, dst) { | ||
return asImpl.open(k, nonce, ciphertext, ad, dst); | ||
}, | ||
}; | ||
|
||
// benchmarks show that for chacha20poly1305 | ||
// the as implementation is faster for smaller payloads(<1200) | ||
// and the node implementation is faster for larger payloads | ||
const lodestarCrypto: ICryptoInterface = { | ||
...pureJsCrypto, | ||
hashSHA256(data) { | ||
return nodeCrypto.hashSHA256(data); | ||
}, | ||
chaCha20Poly1305Encrypt(plaintext, nonce, ad, k) { | ||
if (plaintext.length < 1200) { | ||
return asCrypto.chaCha20Poly1305Encrypt(plaintext, nonce, ad, k); | ||
} | ||
return nodeCrypto.chaCha20Poly1305Encrypt(plaintext, nonce, ad, k); | ||
}, | ||
chaCha20Poly1305Decrypt(ciphertext, nonce, ad, k, dst) { | ||
if (ciphertext.length < 1200) { | ||
return asCrypto.chaCha20Poly1305Decrypt(ciphertext, nonce, ad, k, dst); | ||
} | ||
return nodeCrypto.chaCha20Poly1305Decrypt(ciphertext, nonce, ad, k, dst); | ||
}, | ||
}; | ||
|
||
export function createNoise(): () => ConnectionEncrypter { | ||
return noise({crypto: lodestarCrypto}); | ||
} |
52 changes: 52 additions & 0 deletions
52
packages/beacon-node/test/perf/network/noise/sendData.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import {itBench} from "@dapplion/benchmark"; | ||
import {duplexPair} from "it-pair/duplex"; | ||
import {createSecp256k1PeerId} from "@libp2p/peer-id-factory"; | ||
import {pipe} from "it-pipe"; | ||
import drain from "it-drain"; | ||
import {createNoise} from "../../../../src/network/libp2p/noise.js"; | ||
|
||
describe("network / noise / sendData", () => { | ||
const numberOfMessages = 1000; | ||
|
||
for (const messageLength of [ | ||
// | ||
2 ** 8, | ||
2 ** 9, | ||
2 ** 10, | ||
1200, | ||
2 ** 11, | ||
2 ** 12, | ||
2 ** 14, | ||
2 ** 16, | ||
]) { | ||
itBench({ | ||
id: `send data - ${numberOfMessages} ${messageLength}B messages`, | ||
beforeEach: async () => { | ||
const peerA = await createSecp256k1PeerId(); | ||
const peerB = await createSecp256k1PeerId(); | ||
const noiseA = createNoise()(); | ||
const noiseB = createNoise()(); | ||
|
||
const [inboundConnection, outboundConnection] = duplexPair<Uint8Array>(); | ||
const [outbound, inbound] = await Promise.all([ | ||
noiseA.secureOutbound(peerA, outboundConnection, peerB), | ||
noiseB.secureInbound(peerB, inboundConnection, peerA), | ||
]); | ||
|
||
return {connA: outbound.conn, connB: inbound.conn, data: new Uint8Array(messageLength)}; | ||
}, | ||
fn: async ({connA, connB, data}) => { | ||
await Promise.all([ | ||
// | ||
pipe(connB.source, connB.sink), | ||
pipe(function* () { | ||
for (let i = 0; i < numberOfMessages; i++) { | ||
yield data; | ||
} | ||
}, connA.sink), | ||
pipe(connB.source, drain), | ||
]); | ||
}, | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters