From 7eb3375f50265240096d70bd2814549c8156a0eb Mon Sep 17 00:00:00 2001 From: Sasha <118575614+weboko@users.noreply.github.com> Date: Tue, 28 Nov 2023 01:02:12 +0100 Subject: [PATCH] feat!: export crypto primitives (#1728) * export crypto primitives * export crypto * update imports * fix size limit * rename crypto.js * move Signature type * fix path * fix: size-limit (#1734) * fix paths, revert change to config --------- Co-authored-by: Danish Arora <35004822+danisharora099@users.noreply.github.com> --- packages/message-encryption/package.json | 4 + .../message-encryption/src/crypto/ecies.ts | 2 +- .../message-encryption/src/crypto/index.ts | 79 +------------------ .../src/crypto/symmetric.ts | 4 +- .../message-encryption/src/crypto/utils.ts | 76 ++++++++++++++++++ packages/message-encryption/src/ecies.ts | 25 +++--- ...aku_payload.spec.ts => encryption.spec.ts} | 4 +- .../src/{waku_payload.ts => encryption.ts} | 19 +++-- packages/message-encryption/src/index.ts | 10 +-- .../src/{constants.ts => misc.ts} | 4 + packages/message-encryption/src/symmetric.ts | 15 ++-- packages/tests/tests/ephemeral.node.spec.ts | 8 +- .../tests/tests/filter/subscribe.node.spec.ts | 14 +++- packages/tests/tests/relay/index.node.spec.ts | 10 ++- packages/tests/tests/store/index.node.spec.ts | 10 ++- packages/tests/tests/waku.node.spec.ts | 4 +- 16 files changed, 158 insertions(+), 130 deletions(-) create mode 100644 packages/message-encryption/src/crypto/utils.ts rename packages/message-encryption/src/{waku_payload.spec.ts => encryption.spec.ts} (97%) rename packages/message-encryption/src/{waku_payload.ts => encryption.ts} (96%) rename packages/message-encryption/src/{constants.ts => misc.ts} (69%) diff --git a/packages/message-encryption/package.json b/packages/message-encryption/package.json index 08b1585d8e..1cef02145d 100644 --- a/packages/message-encryption/package.json +++ b/packages/message-encryption/package.json @@ -16,6 +16,10 @@ "./symmetric": { "types": "./dist/symmetric.d.ts", "import": "./dist/symmetric.js" + }, + "./crypto": { + "types": "./dist/crypto/index.d.ts", + "import": "./dist/crypto/index.js" } }, "typesVersions": { diff --git a/packages/message-encryption/src/crypto/ecies.ts b/packages/message-encryption/src/crypto/ecies.ts index d8f16557b2..a53d3ef558 100644 --- a/packages/message-encryption/src/crypto/ecies.ts +++ b/packages/message-encryption/src/crypto/ecies.ts @@ -1,7 +1,7 @@ import * as secp from "@noble/secp256k1"; import { concat, hexToBytes } from "@waku/utils/bytes"; -import { getSubtle, randomBytes, sha256 } from "./index.js"; +import { getSubtle, randomBytes, sha256 } from "./utils.js"; /** * HKDF as implemented in go-ethereum. */ diff --git a/packages/message-encryption/src/crypto/index.ts b/packages/message-encryption/src/crypto/index.ts index 78e23d7774..7c247f255f 100644 --- a/packages/message-encryption/src/crypto/index.ts +++ b/packages/message-encryption/src/crypto/index.ts @@ -1,76 +1,3 @@ -import nodeCrypto from "crypto"; - -import * as secp from "@noble/secp256k1"; -import { concat } from "@waku/utils/bytes"; -import sha3 from "js-sha3"; - -import { Asymmetric, Symmetric } from "../constants.js"; - -declare const self: Record | undefined; -const crypto: { node?: any; web?: any } = { - node: nodeCrypto, - web: typeof self === "object" && "crypto" in self ? self.crypto : undefined -}; - -export function getSubtle(): SubtleCrypto { - if (crypto.web) { - return crypto.web.subtle; - } else if (crypto.node) { - return crypto.node.webcrypto.subtle; - } else { - throw new Error( - "The environment doesn't have Crypto Subtle API (if in the browser, be sure to use to be in a secure context, ie, https)" - ); - } -} - -export const randomBytes = secp.utils.randomBytes; -export const sha256 = secp.utils.sha256; - -/** - * Generate a new private key to be used for asymmetric encryption. - * - * Use {@link getPublicKey} to get the corresponding Public Key. - */ -export function generatePrivateKey(): Uint8Array { - return randomBytes(Asymmetric.keySize); -} - -/** - * Generate a new symmetric key to be used for symmetric encryption. - */ -export function generateSymmetricKey(): Uint8Array { - return randomBytes(Symmetric.keySize); -} - -/** - * Return the public key for the given private key, to be used for asymmetric - * encryption. - */ -export const getPublicKey = secp.getPublicKey; - -/** - * ECDSA Sign a message with the given private key. - * - * @param message The message to sign, usually a hash. - * @param privateKey The ECDSA private key to use to sign the message. - * - * @returns The signature and the recovery id concatenated. - */ -export async function sign( - message: Uint8Array, - privateKey: Uint8Array -): Promise { - const [signature, recoveryId] = await secp.sign(message, privateKey, { - recovered: true, - der: false - }); - return concat( - [signature, new Uint8Array([recoveryId])], - signature.length + 1 - ); -} - -export function keccak256(input: Uint8Array): Uint8Array { - return new Uint8Array(sha3.keccak256.arrayBuffer(input)); -} +export * from "./utils.js"; +export * as ecies from "./ecies.js"; +export * as symmetric from "./symmetric.js"; diff --git a/packages/message-encryption/src/crypto/symmetric.ts b/packages/message-encryption/src/crypto/symmetric.ts index 35f69f4558..faaeb4acb0 100644 --- a/packages/message-encryption/src/crypto/symmetric.ts +++ b/packages/message-encryption/src/crypto/symmetric.ts @@ -1,6 +1,6 @@ -import { Symmetric } from "../constants.js"; +import { Symmetric } from "../misc.js"; -import { getSubtle, randomBytes } from "./index.js"; +import { getSubtle, randomBytes } from "./utils.js"; export async function encrypt( iv: Uint8Array, diff --git a/packages/message-encryption/src/crypto/utils.ts b/packages/message-encryption/src/crypto/utils.ts new file mode 100644 index 0000000000..fd57811bd4 --- /dev/null +++ b/packages/message-encryption/src/crypto/utils.ts @@ -0,0 +1,76 @@ +import nodeCrypto from "crypto"; + +import * as secp from "@noble/secp256k1"; +import { concat } from "@waku/utils/bytes"; +import sha3 from "js-sha3"; + +import { Asymmetric, Symmetric } from "../misc.js"; + +declare const self: Record | undefined; +const crypto: { node?: any; web?: any } = { + node: nodeCrypto, + web: typeof self === "object" && "crypto" in self ? self.crypto : undefined +}; + +export function getSubtle(): SubtleCrypto { + if (crypto.web) { + return crypto.web.subtle; + } else if (crypto.node) { + return crypto.node.webcrypto.subtle; + } else { + throw new Error( + "The environment doesn't have Crypto Subtle API (if in the browser, be sure to use to be in a secure context, ie, https)" + ); + } +} + +export const randomBytes = secp.utils.randomBytes; +export const sha256 = secp.utils.sha256; + +/** + * Generate a new private key to be used for asymmetric encryption. + * + * Use {@link getPublicKey} to get the corresponding Public Key. + */ +export function generatePrivateKey(): Uint8Array { + return randomBytes(Asymmetric.keySize); +} + +/** + * Generate a new symmetric key to be used for symmetric encryption. + */ +export function generateSymmetricKey(): Uint8Array { + return randomBytes(Symmetric.keySize); +} + +/** + * Return the public key for the given private key, to be used for asymmetric + * encryption. + */ +export const getPublicKey = secp.getPublicKey; + +/** + * ECDSA Sign a message with the given private key. + * + * @param message The message to sign, usually a hash. + * @param privateKey The ECDSA private key to use to sign the message. + * + * @returns The signature and the recovery id concatenated. + */ +export async function sign( + message: Uint8Array, + privateKey: Uint8Array +): Promise { + const [signature, recoveryId] = await secp.sign(message, privateKey, { + recovered: true, + der: false + }); + return concat( + [signature, new Uint8Array([recoveryId])], + signature.length + 1 + ); +} + +export function keccak256(input: Uint8Array): Uint8Array { + return new Uint8Array(sha3.keccak256.arrayBuffer(input)); +} diff --git a/packages/message-encryption/src/ecies.ts b/packages/message-encryption/src/ecies.ts index 5a35573d0a..d9c56fea0b 100644 --- a/packages/message-encryption/src/ecies.ts +++ b/packages/message-encryption/src/ecies.ts @@ -1,33 +1,34 @@ import { DefaultPubsubTopic } from "@waku/core"; import { Decoder as DecoderV0 } from "@waku/core/lib/message/version_0"; -import { IMetaSetter, PubsubTopic } from "@waku/interfaces"; import type { EncoderOptions as BaseEncoderOptions, IDecoder, IEncoder, IMessage, - IProtoMessage + IMetaSetter, + IProtoMessage, + PubsubTopic } from "@waku/interfaces"; import { WakuMessage } from "@waku/proto"; import { Logger } from "@waku/utils"; +import { generatePrivateKey } from "./crypto/utils.js"; import { DecodedMessage } from "./decoded_message.js"; import { decryptAsymmetric, encryptAsymmetric, postCipher, preCipher -} from "./waku_payload.js"; +} from "./encryption.js"; +import { OneMillion, Version } from "./misc.js"; -import { - generatePrivateKey, - getPublicKey, - OneMillion, - Version -} from "./index.js"; - -export { generatePrivateKey, getPublicKey }; -export type { Encoder, Decoder, DecodedMessage }; +export { + decryptAsymmetric, + encryptAsymmetric, + postCipher, + preCipher, + generatePrivateKey +}; const log = new Logger("message-encryption:ecies"); diff --git a/packages/message-encryption/src/waku_payload.spec.ts b/packages/message-encryption/src/encryption.spec.ts similarity index 97% rename from packages/message-encryption/src/waku_payload.spec.ts rename to packages/message-encryption/src/encryption.spec.ts index 379b068197..67e0ca13d8 100644 --- a/packages/message-encryption/src/waku_payload.spec.ts +++ b/packages/message-encryption/src/encryption.spec.ts @@ -9,9 +9,9 @@ import { encryptSymmetric, postCipher, preCipher -} from "./waku_payload.js"; +} from "./encryption.js"; -describe("Waku Payload", function () { +describe("Waku Encryption", function () { this.timeout(20000); it("Asymmetric encrypt & decrypt", async function () { await fc.assert( diff --git a/packages/message-encryption/src/waku_payload.ts b/packages/message-encryption/src/encryption.ts similarity index 96% rename from packages/message-encryption/src/waku_payload.ts rename to packages/message-encryption/src/encryption.ts index 8da80da4be..de0b2ac0bb 100644 --- a/packages/message-encryption/src/waku_payload.ts +++ b/packages/message-encryption/src/encryption.ts @@ -1,12 +1,14 @@ import * as secp from "@noble/secp256k1"; import { concat, hexToBytes } from "@waku/utils/bytes"; -import { Symmetric } from "./constants.js"; -import * as ecies from "./crypto/ecies.js"; -import { keccak256, randomBytes, sign } from "./crypto/index.js"; -import * as symmetric from "./crypto/symmetric.js"; - -import { Signature } from "./index.js"; +import { + ecies, + keccak256, + randomBytes, + sign, + symmetric +} from "./crypto/index.js"; +import { Symmetric } from "./misc.js"; const FlagsLength = 1; const FlagMask = 3; // 0011 @@ -210,6 +212,11 @@ export async function preCipher( return envelope; } +type Signature = { + signature: Uint8Array; + publicKey: Uint8Array | undefined; +}; + /** * Decode a decrypted payload. * diff --git a/packages/message-encryption/src/index.ts b/packages/message-encryption/src/index.ts index 1bcc3934d7..765c85dfc3 100644 --- a/packages/message-encryption/src/index.ts +++ b/packages/message-encryption/src/index.ts @@ -5,17 +5,9 @@ import { } from "./crypto/index.js"; import { DecodedMessage } from "./decoded_message.js"; -export const OneMillion = BigInt(1_000_000); - export { generatePrivateKey, generateSymmetricKey, getPublicKey }; export type { DecodedMessage }; export * as ecies from "./ecies.js"; export * as symmetric from "./symmetric.js"; - -export const Version = 1; - -export type Signature = { - signature: Uint8Array; - publicKey: Uint8Array | undefined; -}; +export * as crypto from "./crypto"; diff --git a/packages/message-encryption/src/constants.ts b/packages/message-encryption/src/misc.ts similarity index 69% rename from packages/message-encryption/src/constants.ts rename to packages/message-encryption/src/misc.ts index 586a792fe5..9491453b7a 100644 --- a/packages/message-encryption/src/constants.ts +++ b/packages/message-encryption/src/misc.ts @@ -8,3 +8,7 @@ export const Symmetric = { export const Asymmetric = { keySize: 32 }; + +export const OneMillion = BigInt(1_000_000); + +export const Version = 1; diff --git a/packages/message-encryption/src/symmetric.ts b/packages/message-encryption/src/symmetric.ts index ff49b29d38..2d2ccabbab 100644 --- a/packages/message-encryption/src/symmetric.ts +++ b/packages/message-encryption/src/symmetric.ts @@ -12,18 +12,23 @@ import type { import { WakuMessage } from "@waku/proto"; import { Logger } from "@waku/utils"; +import { generateSymmetricKey } from "./crypto/utils.js"; import { DecodedMessage } from "./decoded_message.js"; import { decryptSymmetric, encryptSymmetric, postCipher, preCipher -} from "./waku_payload.js"; +} from "./encryption.js"; +import { OneMillion, Version } from "./misc.js"; -import { generateSymmetricKey, OneMillion, Version } from "./index.js"; - -export { generateSymmetricKey }; -export type { DecodedMessage, Encoder, Decoder }; +export { + decryptSymmetric, + encryptSymmetric, + postCipher, + preCipher, + generateSymmetricKey +}; const log = new Logger("message-encryption:symmetric"); diff --git a/packages/tests/tests/ephemeral.node.spec.ts b/packages/tests/tests/ephemeral.node.spec.ts index 3f301b9b53..b8c0115e3d 100644 --- a/packages/tests/tests/ephemeral.node.spec.ts +++ b/packages/tests/tests/ephemeral.node.spec.ts @@ -7,13 +7,15 @@ import { import { IFilterSubscription, Protocols } from "@waku/interfaces"; import type { LightNode } from "@waku/interfaces"; import { - createDecoder as eciesDecoder, - createEncoder as eciesEncoder, generatePrivateKey, + generateSymmetricKey, getPublicKey +} from "@waku/message-encryption"; +import { + createDecoder as eciesDecoder, + createEncoder as eciesEncoder } from "@waku/message-encryption/ecies"; import { - generateSymmetricKey, createDecoder as symDecoder, createEncoder as symEncoder } from "@waku/message-encryption/symmetric"; diff --git a/packages/tests/tests/filter/subscribe.node.spec.ts b/packages/tests/tests/filter/subscribe.node.spec.ts index a3aa765b53..a191264347 100644 --- a/packages/tests/tests/filter/subscribe.node.spec.ts +++ b/packages/tests/tests/filter/subscribe.node.spec.ts @@ -6,7 +6,13 @@ import { } from "@waku/core"; import type { IFilterSubscription, LightNode } from "@waku/interfaces"; import { Protocols } from "@waku/interfaces"; -import { ecies, symmetric } from "@waku/message-encryption"; +import { + ecies, + generatePrivateKey, + generateSymmetricKey, + getPublicKey, + symmetric +} from "@waku/message-encryption"; import { utf8ToBytes } from "@waku/utils/bytes"; import { expect } from "chai"; @@ -67,8 +73,8 @@ describe("Waku Filter V2: Subscribe", function () { }); it("Subscribe and receive ecies encrypted messages via lightPush", async function () { - const privateKey = ecies.generatePrivateKey(); - const publicKey = ecies.getPublicKey(privateKey); + const privateKey = generatePrivateKey(); + const publicKey = getPublicKey(privateKey); const encoder = ecies.createEncoder({ contentTopic: TestContentTopic, publicKey @@ -89,7 +95,7 @@ describe("Waku Filter V2: Subscribe", function () { }); it("Subscribe and receive symmetrically encrypted messages via lightPush", async function () { - const symKey = symmetric.generateSymmetricKey(); + const symKey = generateSymmetricKey(); const encoder = symmetric.createEncoder({ contentTopic: TestContentTopic, symKey diff --git a/packages/tests/tests/relay/index.node.spec.ts b/packages/tests/tests/relay/index.node.spec.ts index 31f955ab7c..d6bb19fcff 100644 --- a/packages/tests/tests/relay/index.node.spec.ts +++ b/packages/tests/tests/relay/index.node.spec.ts @@ -1,15 +1,17 @@ import { createDecoder, createEncoder, DecodedMessage } from "@waku/core"; import { RelayNode } from "@waku/interfaces"; import { - createDecoder as createEciesDecoder, - createEncoder as createEciesEncoder, generatePrivateKey, + generateSymmetricKey, getPublicKey +} from "@waku/message-encryption"; +import { + createDecoder as createEciesDecoder, + createEncoder as createEciesEncoder } from "@waku/message-encryption/ecies"; import { createDecoder as createSymDecoder, - createEncoder as createSymEncoder, - generateSymmetricKey + createEncoder as createSymEncoder } from "@waku/message-encryption/symmetric"; import { createRelayNode } from "@waku/sdk"; import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes"; diff --git a/packages/tests/tests/store/index.node.spec.ts b/packages/tests/tests/store/index.node.spec.ts index ee1edd5605..b446bd7829 100644 --- a/packages/tests/tests/store/index.node.spec.ts +++ b/packages/tests/tests/store/index.node.spec.ts @@ -7,15 +7,17 @@ import { import type { IMessage, LightNode } from "@waku/interfaces"; import { Protocols } from "@waku/interfaces"; import { - createDecoder as createEciesDecoder, - createEncoder as createEciesEncoder, generatePrivateKey, + generateSymmetricKey, getPublicKey +} from "@waku/message-encryption"; +import { + createDecoder as createEciesDecoder, + createEncoder as createEciesEncoder } from "@waku/message-encryption/ecies"; import { createDecoder as createSymDecoder, - createEncoder as createSymEncoder, - generateSymmetricKey + createEncoder as createSymEncoder } from "@waku/message-encryption/symmetric"; import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes"; import { expect } from "chai"; diff --git a/packages/tests/tests/waku.node.spec.ts b/packages/tests/tests/waku.node.spec.ts index 6b399718fc..e7aa69f176 100644 --- a/packages/tests/tests/waku.node.spec.ts +++ b/packages/tests/tests/waku.node.spec.ts @@ -7,10 +7,10 @@ import { } from "@waku/core"; import type { LightNode, RelayNode, Waku } from "@waku/interfaces"; import { Protocols } from "@waku/interfaces"; +import { generateSymmetricKey } from "@waku/message-encryption"; import { createDecoder, - createEncoder, - generateSymmetricKey + createEncoder } from "@waku/message-encryption/symmetric"; import { createLightNode,