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>
This commit is contained in:
Sasha 2023-11-28 01:02:12 +01:00 committed by GitHub
parent 7df21b7756
commit 7eb3375f50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 158 additions and 130 deletions

View File

@ -16,6 +16,10 @@
"./symmetric": { "./symmetric": {
"types": "./dist/symmetric.d.ts", "types": "./dist/symmetric.d.ts",
"import": "./dist/symmetric.js" "import": "./dist/symmetric.js"
},
"./crypto": {
"types": "./dist/crypto/index.d.ts",
"import": "./dist/crypto/index.js"
} }
}, },
"typesVersions": { "typesVersions": {

View File

@ -1,7 +1,7 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/secp256k1";
import { concat, hexToBytes } from "@waku/utils/bytes"; 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. * HKDF as implemented in go-ethereum.
*/ */

View File

@ -1,76 +1,3 @@
import nodeCrypto from "crypto"; export * from "./utils.js";
export * as ecies from "./ecies.js";
import * as secp from "@noble/secp256k1"; export * as symmetric from "./symmetric.js";
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));
}

View File

@ -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( export async function encrypt(
iv: Uint8Array, iv: Uint8Array,

View File

@ -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;
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));
}

View File

@ -1,33 +1,34 @@
import { DefaultPubsubTopic } from "@waku/core"; import { DefaultPubsubTopic } from "@waku/core";
import { Decoder as DecoderV0 } from "@waku/core/lib/message/version_0"; import { Decoder as DecoderV0 } from "@waku/core/lib/message/version_0";
import { IMetaSetter, PubsubTopic } from "@waku/interfaces";
import type { import type {
EncoderOptions as BaseEncoderOptions, EncoderOptions as BaseEncoderOptions,
IDecoder, IDecoder,
IEncoder, IEncoder,
IMessage, IMessage,
IProtoMessage IMetaSetter,
IProtoMessage,
PubsubTopic
} from "@waku/interfaces"; } from "@waku/interfaces";
import { WakuMessage } from "@waku/proto"; import { WakuMessage } from "@waku/proto";
import { Logger } from "@waku/utils"; import { Logger } from "@waku/utils";
import { generatePrivateKey } from "./crypto/utils.js";
import { DecodedMessage } from "./decoded_message.js"; import { DecodedMessage } from "./decoded_message.js";
import { import {
decryptAsymmetric, decryptAsymmetric,
encryptAsymmetric, encryptAsymmetric,
postCipher, postCipher,
preCipher preCipher
} from "./waku_payload.js"; } from "./encryption.js";
import { OneMillion, Version } from "./misc.js";
import { export {
generatePrivateKey, decryptAsymmetric,
getPublicKey, encryptAsymmetric,
OneMillion, postCipher,
Version preCipher,
} from "./index.js"; generatePrivateKey
};
export { generatePrivateKey, getPublicKey };
export type { Encoder, Decoder, DecodedMessage };
const log = new Logger("message-encryption:ecies"); const log = new Logger("message-encryption:ecies");

View File

@ -9,9 +9,9 @@ import {
encryptSymmetric, encryptSymmetric,
postCipher, postCipher,
preCipher preCipher
} from "./waku_payload.js"; } from "./encryption.js";
describe("Waku Payload", function () { describe("Waku Encryption", function () {
this.timeout(20000); this.timeout(20000);
it("Asymmetric encrypt & decrypt", async function () { it("Asymmetric encrypt & decrypt", async function () {
await fc.assert( await fc.assert(

View File

@ -1,12 +1,14 @@
import * as secp from "@noble/secp256k1"; import * as secp from "@noble/secp256k1";
import { concat, hexToBytes } from "@waku/utils/bytes"; import { concat, hexToBytes } from "@waku/utils/bytes";
import { Symmetric } from "./constants.js"; import {
import * as ecies from "./crypto/ecies.js"; ecies,
import { keccak256, randomBytes, sign } from "./crypto/index.js"; keccak256,
import * as symmetric from "./crypto/symmetric.js"; randomBytes,
sign,
import { Signature } from "./index.js"; symmetric
} from "./crypto/index.js";
import { Symmetric } from "./misc.js";
const FlagsLength = 1; const FlagsLength = 1;
const FlagMask = 3; // 0011 const FlagMask = 3; // 0011
@ -210,6 +212,11 @@ export async function preCipher(
return envelope; return envelope;
} }
type Signature = {
signature: Uint8Array;
publicKey: Uint8Array | undefined;
};
/** /**
* Decode a decrypted payload. * Decode a decrypted payload.
* *

View File

@ -5,17 +5,9 @@ import {
} from "./crypto/index.js"; } from "./crypto/index.js";
import { DecodedMessage } from "./decoded_message.js"; import { DecodedMessage } from "./decoded_message.js";
export const OneMillion = BigInt(1_000_000);
export { generatePrivateKey, generateSymmetricKey, getPublicKey }; export { generatePrivateKey, generateSymmetricKey, getPublicKey };
export type { DecodedMessage }; export type { DecodedMessage };
export * as ecies from "./ecies.js"; export * as ecies from "./ecies.js";
export * as symmetric from "./symmetric.js"; export * as symmetric from "./symmetric.js";
export * as crypto from "./crypto";
export const Version = 1;
export type Signature = {
signature: Uint8Array;
publicKey: Uint8Array | undefined;
};

View File

@ -8,3 +8,7 @@ export const Symmetric = {
export const Asymmetric = { export const Asymmetric = {
keySize: 32 keySize: 32
}; };
export const OneMillion = BigInt(1_000_000);
export const Version = 1;

View File

@ -12,18 +12,23 @@ import type {
import { WakuMessage } from "@waku/proto"; import { WakuMessage } from "@waku/proto";
import { Logger } from "@waku/utils"; import { Logger } from "@waku/utils";
import { generateSymmetricKey } from "./crypto/utils.js";
import { DecodedMessage } from "./decoded_message.js"; import { DecodedMessage } from "./decoded_message.js";
import { import {
decryptSymmetric, decryptSymmetric,
encryptSymmetric, encryptSymmetric,
postCipher, postCipher,
preCipher preCipher
} from "./waku_payload.js"; } from "./encryption.js";
import { OneMillion, Version } from "./misc.js";
import { generateSymmetricKey, OneMillion, Version } from "./index.js"; export {
decryptSymmetric,
export { generateSymmetricKey }; encryptSymmetric,
export type { DecodedMessage, Encoder, Decoder }; postCipher,
preCipher,
generateSymmetricKey
};
const log = new Logger("message-encryption:symmetric"); const log = new Logger("message-encryption:symmetric");

View File

@ -7,13 +7,15 @@ import {
import { IFilterSubscription, Protocols } from "@waku/interfaces"; import { IFilterSubscription, Protocols } from "@waku/interfaces";
import type { LightNode } from "@waku/interfaces"; import type { LightNode } from "@waku/interfaces";
import { import {
createDecoder as eciesDecoder,
createEncoder as eciesEncoder,
generatePrivateKey, generatePrivateKey,
generateSymmetricKey,
getPublicKey getPublicKey
} from "@waku/message-encryption";
import {
createDecoder as eciesDecoder,
createEncoder as eciesEncoder
} from "@waku/message-encryption/ecies"; } from "@waku/message-encryption/ecies";
import { import {
generateSymmetricKey,
createDecoder as symDecoder, createDecoder as symDecoder,
createEncoder as symEncoder createEncoder as symEncoder
} from "@waku/message-encryption/symmetric"; } from "@waku/message-encryption/symmetric";

View File

@ -6,7 +6,13 @@ import {
} from "@waku/core"; } from "@waku/core";
import type { IFilterSubscription, LightNode } from "@waku/interfaces"; import type { IFilterSubscription, LightNode } from "@waku/interfaces";
import { Protocols } 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 { utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai"; 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 () { it("Subscribe and receive ecies encrypted messages via lightPush", async function () {
const privateKey = ecies.generatePrivateKey(); const privateKey = generatePrivateKey();
const publicKey = ecies.getPublicKey(privateKey); const publicKey = getPublicKey(privateKey);
const encoder = ecies.createEncoder({ const encoder = ecies.createEncoder({
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
publicKey publicKey
@ -89,7 +95,7 @@ describe("Waku Filter V2: Subscribe", function () {
}); });
it("Subscribe and receive symmetrically encrypted messages via lightPush", async function () { it("Subscribe and receive symmetrically encrypted messages via lightPush", async function () {
const symKey = symmetric.generateSymmetricKey(); const symKey = generateSymmetricKey();
const encoder = symmetric.createEncoder({ const encoder = symmetric.createEncoder({
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
symKey symKey

View File

@ -1,15 +1,17 @@
import { createDecoder, createEncoder, DecodedMessage } from "@waku/core"; import { createDecoder, createEncoder, DecodedMessage } from "@waku/core";
import { RelayNode } from "@waku/interfaces"; import { RelayNode } from "@waku/interfaces";
import { import {
createDecoder as createEciesDecoder,
createEncoder as createEciesEncoder,
generatePrivateKey, generatePrivateKey,
generateSymmetricKey,
getPublicKey getPublicKey
} from "@waku/message-encryption";
import {
createDecoder as createEciesDecoder,
createEncoder as createEciesEncoder
} from "@waku/message-encryption/ecies"; } from "@waku/message-encryption/ecies";
import { import {
createDecoder as createSymDecoder, createDecoder as createSymDecoder,
createEncoder as createSymEncoder, createEncoder as createSymEncoder
generateSymmetricKey
} from "@waku/message-encryption/symmetric"; } from "@waku/message-encryption/symmetric";
import { createRelayNode } from "@waku/sdk"; import { createRelayNode } from "@waku/sdk";
import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes"; import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes";

View File

@ -7,15 +7,17 @@ import {
import type { IMessage, LightNode } from "@waku/interfaces"; import type { IMessage, LightNode } from "@waku/interfaces";
import { Protocols } from "@waku/interfaces"; import { Protocols } from "@waku/interfaces";
import { import {
createDecoder as createEciesDecoder,
createEncoder as createEciesEncoder,
generatePrivateKey, generatePrivateKey,
generateSymmetricKey,
getPublicKey getPublicKey
} from "@waku/message-encryption";
import {
createDecoder as createEciesDecoder,
createEncoder as createEciesEncoder
} from "@waku/message-encryption/ecies"; } from "@waku/message-encryption/ecies";
import { import {
createDecoder as createSymDecoder, createDecoder as createSymDecoder,
createEncoder as createSymEncoder, createEncoder as createSymEncoder
generateSymmetricKey
} from "@waku/message-encryption/symmetric"; } from "@waku/message-encryption/symmetric";
import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes"; import { bytesToUtf8, utf8ToBytes } from "@waku/utils/bytes";
import { expect } from "chai"; import { expect } from "chai";

View File

@ -7,10 +7,10 @@ import {
} from "@waku/core"; } from "@waku/core";
import type { LightNode, RelayNode, Waku } from "@waku/interfaces"; import type { LightNode, RelayNode, Waku } from "@waku/interfaces";
import { Protocols } from "@waku/interfaces"; import { Protocols } from "@waku/interfaces";
import { generateSymmetricKey } from "@waku/message-encryption";
import { import {
createDecoder, createDecoder,
createEncoder, createEncoder
generateSymmetricKey
} from "@waku/message-encryption/symmetric"; } from "@waku/message-encryption/symmetric";
import { import {
createLightNode, createLightNode,