mirror of
https://github.com/waku-org/js-waku.git
synced 2025-02-19 15:54:28 +00:00
moar refactoring
This commit is contained in:
parent
bf988e5871
commit
4e1843e71b
@ -1,27 +1,21 @@
|
||||
import * as secp from "@noble/secp256k1";
|
||||
import {
|
||||
sign as nobleSign,
|
||||
ProjectivePoint as Point,
|
||||
Signature,
|
||||
verify
|
||||
} from "@noble/secp256k1";
|
||||
import { concat } from "@waku/utils/bytes";
|
||||
import sha3 from "js-sha3";
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
const signature = nobleSign(message, privateKey);
|
||||
return concat([
|
||||
signature.toCompactRawBytes(),
|
||||
new Uint8Array([signature.recovery || 0])
|
||||
]);
|
||||
}
|
||||
|
||||
export function keccak256(input: Uint8Array): Uint8Array {
|
||||
@ -32,21 +26,18 @@ export function compressPublicKey(publicKey: Uint8Array): Uint8Array {
|
||||
if (publicKey.length === 64) {
|
||||
publicKey = concat([new Uint8Array([4]), publicKey], 65);
|
||||
}
|
||||
const point = secp.Point.fromHex(publicKey);
|
||||
const point = Point.fromHex(publicKey);
|
||||
return point.toRawBytes(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify an ECDSA signature.
|
||||
*/
|
||||
export function verifySignature(
|
||||
signature: Uint8Array,
|
||||
message: Uint8Array | string,
|
||||
publicKey: Uint8Array
|
||||
): boolean {
|
||||
try {
|
||||
const _signature = secp.Signature.fromCompact(signature.slice(0, 64));
|
||||
return secp.verify(_signature, message, publicKey);
|
||||
const _signature = Signature.fromCompact(signature.slice(0, 64));
|
||||
return verify(_signature, message, publicKey);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
import * as secp from "@noble/secp256k1";
|
||||
import { sign as nobleSign, ProjectivePoint as Point } from "@noble/secp256k1";
|
||||
import type { NodeId } from "@waku/interfaces";
|
||||
import { bytesToHex } from "@waku/utils/bytes";
|
||||
|
||||
import { keccak256 } from "./crypto.js";
|
||||
|
||||
export async function sign(
|
||||
privKey: Uint8Array,
|
||||
msg: Uint8Array
|
||||
): Promise<Uint8Array> {
|
||||
return secp.sign(keccak256(msg), privKey, {
|
||||
der: false
|
||||
});
|
||||
return nobleSign(keccak256(msg), privKey).toCompactRawBytes();
|
||||
}
|
||||
|
||||
export function nodeId(pubKey: Uint8Array): NodeId {
|
||||
const publicKey = secp.Point.fromHex(pubKey);
|
||||
const publicKey = Point.fromHex(pubKey);
|
||||
const uncompressedPubkey = publicKey.toRawBytes(false);
|
||||
|
||||
return bytesToHex(keccak256(uncompressedPubkey.slice(1)));
|
||||
|
@ -1,7 +1,11 @@
|
||||
import * as secp from "@noble/secp256k1";
|
||||
import { concat, hexToBytes } from "@waku/utils/bytes";
|
||||
import {
|
||||
getPublicKey,
|
||||
getSharedSecret,
|
||||
etc as secpUtils
|
||||
} from "@noble/secp256k1";
|
||||
import { concat } from "@waku/utils/bytes";
|
||||
|
||||
import { getSubtle, randomBytes, sha256 } from "./index.js";
|
||||
import { getSubtle } from "./index.js";
|
||||
/**
|
||||
* HKDF as implemented in go-ethereum.
|
||||
*/
|
||||
@ -15,7 +19,7 @@ function kdf(secret: Uint8Array, outputLength: number): Promise<Uint8Array> {
|
||||
[counters, secret],
|
||||
counters.length + secret.length
|
||||
);
|
||||
const willBeHashResult = sha256(countersSecret);
|
||||
const willBeHashResult = secpUtils.hmacSha256Async(countersSecret);
|
||||
willBeResult = willBeResult.then((result) =>
|
||||
willBeHashResult.then((hashResult) => {
|
||||
const _hashResult = new Uint8Array(hashResult);
|
||||
@ -31,61 +35,59 @@ function kdf(secret: Uint8Array, outputLength: number): Promise<Uint8Array> {
|
||||
return willBeResult;
|
||||
}
|
||||
|
||||
function aesCtrEncrypt(
|
||||
async function aesCtrEncrypt(
|
||||
counter: Uint8Array,
|
||||
key: ArrayBufferLike,
|
||||
data: ArrayBufferLike
|
||||
): Promise<Uint8Array> {
|
||||
return getSubtle()
|
||||
.importKey("raw", key, "AES-CTR", false, ["encrypt"])
|
||||
.then((cryptoKey) =>
|
||||
getSubtle().encrypt(
|
||||
{ name: "AES-CTR", counter: counter, length: 128 },
|
||||
cryptoKey,
|
||||
data
|
||||
)
|
||||
)
|
||||
.then((bytes) => new Uint8Array(bytes));
|
||||
const cryptoKey = await getSubtle().importKey("raw", key, "AES-CTR", false, [
|
||||
"encrypt"
|
||||
]);
|
||||
const bytes = await getSubtle().encrypt(
|
||||
{ name: "AES-CTR", counter: counter, length: 128 },
|
||||
cryptoKey,
|
||||
data
|
||||
);
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
function aesCtrDecrypt(
|
||||
async function aesCtrDecrypt(
|
||||
counter: Uint8Array,
|
||||
key: ArrayBufferLike,
|
||||
data: ArrayBufferLike
|
||||
): Promise<Uint8Array> {
|
||||
return getSubtle()
|
||||
.importKey("raw", key, "AES-CTR", false, ["decrypt"])
|
||||
.then((cryptoKey) =>
|
||||
getSubtle().decrypt(
|
||||
{ name: "AES-CTR", counter: counter, length: 128 },
|
||||
cryptoKey,
|
||||
data
|
||||
)
|
||||
)
|
||||
.then((bytes) => new Uint8Array(bytes));
|
||||
const cryptoKey = await getSubtle().importKey("raw", key, "AES-CTR", false, [
|
||||
"decrypt"
|
||||
]);
|
||||
const bytes = await getSubtle().decrypt(
|
||||
{ name: "AES-CTR", counter: counter, length: 128 },
|
||||
cryptoKey,
|
||||
data
|
||||
);
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
function hmacSha256Sign(
|
||||
async function hmacSha256Sign(
|
||||
key: ArrayBufferLike,
|
||||
msg: ArrayBufferLike
|
||||
): PromiseLike<Uint8Array> {
|
||||
): Promise<Uint8Array> {
|
||||
const algorithm = { name: "HMAC", hash: { name: "SHA-256" } };
|
||||
return getSubtle()
|
||||
.importKey("raw", key, algorithm, false, ["sign"])
|
||||
.then((cryptoKey) => getSubtle().sign(algorithm, cryptoKey, msg))
|
||||
.then((bytes) => new Uint8Array(bytes));
|
||||
const cryptoKey = await getSubtle().importKey("raw", key, algorithm, false, [
|
||||
"sign"
|
||||
]);
|
||||
const bytes = await getSubtle().sign(algorithm, cryptoKey, msg);
|
||||
return new Uint8Array(bytes);
|
||||
}
|
||||
|
||||
function hmacSha256Verify(
|
||||
async function hmacSha256Verify(
|
||||
key: ArrayBufferLike,
|
||||
msg: ArrayBufferLike,
|
||||
sig: ArrayBufferLike
|
||||
): Promise<boolean> {
|
||||
const algorithm = { name: "HMAC", hash: { name: "SHA-256" } };
|
||||
const _key = getSubtle().importKey("raw", key, algorithm, false, ["verify"]);
|
||||
return _key.then((cryptoKey) =>
|
||||
getSubtle().verify(algorithm, cryptoKey, sig, msg)
|
||||
);
|
||||
const cryptoKey = await _key;
|
||||
return await getSubtle().verify(algorithm, cryptoKey, sig, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,9 +110,11 @@ function derive(privateKeyA: Uint8Array, publicKeyB: Uint8Array): Uint8Array {
|
||||
} else if (publicKeyB[0] !== 4) {
|
||||
throw new Error("Bad public key, a valid public key would begin with 4");
|
||||
} else {
|
||||
const px = secp.getSharedSecret(privateKeyA, publicKeyB, true);
|
||||
const privateKeyAHex = secpUtils.bytesToHex(privateKeyA);
|
||||
const publicKeyBHex = secpUtils.bytesToHex(publicKeyB);
|
||||
const px = getSharedSecret(privateKeyAHex, publicKeyBHex, true);
|
||||
// Remove the compression prefix
|
||||
return new Uint8Array(hexToBytes(px).slice(1));
|
||||
return new Uint8Array(px.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,21 +129,21 @@ export async function encrypt(
|
||||
publicKeyTo: Uint8Array,
|
||||
msg: Uint8Array
|
||||
): Promise<Uint8Array> {
|
||||
const ephemPrivateKey = randomBytes(32);
|
||||
const ephemPrivateKey = secpUtils.randomBytes(32);
|
||||
|
||||
const sharedPx = derive(ephemPrivateKey, publicKeyTo);
|
||||
|
||||
const hash = await kdf(sharedPx, 32);
|
||||
|
||||
const iv = randomBytes(16);
|
||||
const iv = secpUtils.randomBytes(16);
|
||||
const encryptionKey = hash.slice(0, 16);
|
||||
const cipherText = await aesCtrEncrypt(iv, encryptionKey, msg);
|
||||
|
||||
const ivCipherText = concat([iv, cipherText], iv.length + cipherText.length);
|
||||
|
||||
const macKey = await sha256(hash.slice(16));
|
||||
const macKey = await secpUtils.hmacSha256Async(hash.slice(16));
|
||||
const hmac = await hmacSha256Sign(macKey, ivCipherText);
|
||||
const ephemPublicKey = secp.getPublicKey(ephemPrivateKey, false);
|
||||
const ephemPublicKey = getPublicKey(ephemPrivateKey, false);
|
||||
|
||||
return concat(
|
||||
[ephemPublicKey, ivCipherText, hmac],
|
||||
@ -181,9 +185,9 @@ export async function decrypt(
|
||||
// check HMAC
|
||||
const px = derive(privateKey, ephemPublicKey);
|
||||
const hash = await kdf(px, 32);
|
||||
const [encryptionKey, macKey] = await sha256(hash.slice(16)).then(
|
||||
(macKey) => [hash.slice(0, 16), macKey]
|
||||
);
|
||||
const [encryptionKey, macKey] = await secpUtils
|
||||
.hmacSha256Async(hash.slice(16))
|
||||
.then((macKey) => [hash.slice(0, 16), macKey]);
|
||||
|
||||
if (!(await hmacSha256Verify(macKey, cipherAndIv, msgMac))) {
|
||||
throw new Error("Incorrect MAC");
|
||||
|
@ -1,7 +1,10 @@
|
||||
import nodeCrypto from "crypto";
|
||||
|
||||
import * as secp from "@noble/secp256k1";
|
||||
import { concat } from "@waku/utils/bytes";
|
||||
import {
|
||||
getPublicKey,
|
||||
sign as secpSign,
|
||||
etc as secpUtils
|
||||
} from "@noble/secp256k1";
|
||||
import sha3 from "js-sha3";
|
||||
|
||||
import { Asymmetric, Symmetric } from "../constants.js";
|
||||
@ -24,30 +27,28 @@ export function getSubtle(): SubtleCrypto {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return secpUtils.randomBytes(Asymmetric.keySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new symmetric key to be used for symmetric encryption.
|
||||
*/
|
||||
|
||||
export function generateSymmetricKey(): Uint8Array {
|
||||
return randomBytes(Symmetric.keySize);
|
||||
return secpUtils.randomBytes(Symmetric.keySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the public key for the given private key, to be used for asymmetric
|
||||
* encryption.
|
||||
*/
|
||||
export const getPublicKey = secp.getPublicKey;
|
||||
export { getPublicKey };
|
||||
|
||||
/**
|
||||
* ECDSA Sign a message with the given private key.
|
||||
@ -61,14 +62,15 @@ 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
|
||||
);
|
||||
const signatureObj = secpSign(message, privateKey);
|
||||
const signature = signatureObj.toCompactRawBytes();
|
||||
const recoveryId = signatureObj.recovery;
|
||||
|
||||
if (recoveryId === undefined) {
|
||||
throw new Error("Recovery ID is undefined");
|
||||
}
|
||||
|
||||
return new Uint8Array([...signature, recoveryId]);
|
||||
}
|
||||
|
||||
export function keccak256(input: Uint8Array): Uint8Array {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { etc as secpUtils } from "@noble/secp256k1";
|
||||
|
||||
import { Symmetric } from "../constants.js";
|
||||
|
||||
import { getSubtle, randomBytes } from "./index.js";
|
||||
import { getSubtle } from "./index.js";
|
||||
|
||||
export async function encrypt(
|
||||
iv: Uint8Array,
|
||||
@ -29,5 +31,5 @@ export async function decrypt(
|
||||
}
|
||||
|
||||
export function generateIv(): Uint8Array {
|
||||
return randomBytes(Symmetric.ivSize);
|
||||
return secpUtils.randomBytes(Symmetric.ivSize);
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { sign, Signature } from "@noble/secp256k1";
|
||||
import { etc as secpUtils } 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 } from "./crypto/index.js";
|
||||
import { keccak256 } from "./crypto/index.js";
|
||||
import * as symmetric from "./crypto/symmetric.js";
|
||||
|
||||
import { Signature as WakuSignature } from "./index.js";
|
||||
@ -190,7 +191,7 @@ export async function preCipher(
|
||||
|
||||
const remainder = rawSize % PaddingTarget;
|
||||
const paddingSize = PaddingTarget - remainder;
|
||||
const pad = randomBytes(paddingSize);
|
||||
const pad = secpUtils.randomBytes(paddingSize);
|
||||
|
||||
if (!validateDataIntegrity(pad, paddingSize)) {
|
||||
throw new Error("failed to generate random padding of size " + paddingSize);
|
||||
|
Loading…
x
Reference in New Issue
Block a user