Skip to content

Commit

Permalink
feat!: export crypto primitives (#1728)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
weboko and danisharora099 authored Nov 28, 2023
1 parent 7df21b7 commit 7eb3375
Show file tree
Hide file tree
Showing 16 changed files with 158 additions and 130 deletions.
4 changes: 4 additions & 0 deletions packages/message-encryption/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion packages/message-encryption/src/crypto/ecies.ts
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand Down
79 changes: 3 additions & 76 deletions packages/message-encryption/src/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -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<string, any> | 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<Uint8Array> {
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";
4 changes: 2 additions & 2 deletions packages/message-encryption/src/crypto/symmetric.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
76 changes: 76 additions & 0 deletions packages/message-encryption/src/crypto/utils.ts
Original file line number Diff line number Diff line change
@@ -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<string, any> | undefined;

Check warning on line 9 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

Check warning on line 9 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / proto

Unexpected any. Specify a different type
const crypto: { node?: any; web?: any } = {

Check warning on line 10 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

Check warning on line 10 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

Check warning on line 10 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / proto

Unexpected any. Specify a different type

Check warning on line 10 in packages/message-encryption/src/crypto/utils.ts

View workflow job for this annotation

GitHub Actions / proto

Unexpected any. Specify a different type
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<Uint8Array> {
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));
}
25 changes: 13 additions & 12 deletions packages/message-encryption/src/ecies.ts
Original file line number Diff line number Diff line change
@@ -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");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -210,6 +212,11 @@ export async function preCipher(
return envelope;
}

type Signature = {
signature: Uint8Array;
publicKey: Uint8Array | undefined;
};

/**
* Decode a decrypted payload.
*
Expand Down
10 changes: 1 addition & 9 deletions packages/message-encryption/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export const Symmetric = {
export const Asymmetric = {
keySize: 32
};

export const OneMillion = BigInt(1_000_000);

export const Version = 1;
15 changes: 10 additions & 5 deletions packages/message-encryption/src/symmetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
8 changes: 5 additions & 3 deletions packages/tests/tests/ephemeral.node.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
14 changes: 10 additions & 4 deletions packages/tests/tests/filter/subscribe.node.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 7eb3375

Please sign in to comment.