2022-03-07 13:33:20 +11:00
|
|
|
import * as secp from "@noble/secp256k1";
|
2021-10-26 16:58:26 +11:00
|
|
|
|
2022-05-20 10:54:48 +10:00
|
|
|
import { keccak256, randomBytes } from "../crypto";
|
2022-03-07 13:33:20 +11:00
|
|
|
import { bytesToHex } from "../utils";
|
2022-03-07 07:51:36 +11:00
|
|
|
|
2022-02-04 14:12:00 +11:00
|
|
|
import { createNodeId } from "./create";
|
|
|
|
|
import { NodeId } from "./types";
|
2021-10-26 16:58:26 +11:00
|
|
|
|
2022-03-07 07:51:36 +11:00
|
|
|
export function createPrivateKey(): Uint8Array {
|
2022-02-16 14:08:48 +11:00
|
|
|
return randomBytes(32);
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
|
2022-02-16 12:11:54 +11:00
|
|
|
export function publicKey(privKey: Uint8Array): Uint8Array {
|
2022-03-07 13:33:20 +11:00
|
|
|
return secp.getPublicKey(privKey, true);
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 13:33:20 +11:00
|
|
|
export function compressPublicKey(publicKey: Uint8Array): Uint8Array {
|
|
|
|
|
const point = secp.Point.fromHex(bytesToHex(publicKey));
|
|
|
|
|
return point.toRawBytes(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function sign(
|
|
|
|
|
privKey: Uint8Array,
|
|
|
|
|
msg: Uint8Array
|
|
|
|
|
): Promise<Uint8Array> {
|
2022-05-20 10:54:48 +10:00
|
|
|
return secp.sign(keccak256(msg), privKey, {
|
2022-03-07 13:33:20 +11:00
|
|
|
der: false,
|
|
|
|
|
});
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
|
2022-02-16 12:11:54 +11:00
|
|
|
export function verify(
|
|
|
|
|
pubKey: Uint8Array,
|
|
|
|
|
msg: Uint8Array,
|
|
|
|
|
sig: Uint8Array
|
|
|
|
|
): boolean {
|
2022-03-07 13:33:20 +11:00
|
|
|
try {
|
|
|
|
|
const _sig = secp.Signature.fromCompact(sig.slice(0, 64));
|
2022-05-20 10:54:48 +10:00
|
|
|
return secp.verify(_sig, keccak256(msg), pubKey);
|
2022-03-07 13:33:20 +11:00
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function nodeId(pubKey: Uint8Array): NodeId {
|
2022-03-07 13:33:20 +11:00
|
|
|
const publicKey = secp.Point.fromHex(pubKey);
|
|
|
|
|
const uncompressedPubkey = publicKey.toRawBytes(false);
|
2021-10-26 16:58:26 +11:00
|
|
|
|
2022-05-20 10:54:48 +10:00
|
|
|
return createNodeId(keccak256(uncompressedPubkey.slice(1)));
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ENRKeyPair {
|
|
|
|
|
public constructor(
|
|
|
|
|
public readonly nodeId: NodeId,
|
2022-02-16 12:11:54 +11:00
|
|
|
public readonly privateKey: Uint8Array,
|
|
|
|
|
public readonly publicKey: Uint8Array
|
2021-10-26 16:58:26 +11:00
|
|
|
) {}
|
|
|
|
|
|
2022-03-07 07:51:36 +11:00
|
|
|
public static create(privateKey?: Uint8Array): ENRKeyPair {
|
2021-10-26 16:58:26 +11:00
|
|
|
if (privateKey) {
|
2022-03-07 13:33:20 +11:00
|
|
|
if (!secp.utils.isValidPrivateKey(privateKey)) {
|
2022-02-04 14:12:00 +11:00
|
|
|
throw new Error("Invalid private key");
|
2021-10-26 16:58:26 +11:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-07 07:51:36 +11:00
|
|
|
const _privateKey = privateKey || createPrivateKey();
|
2021-10-26 16:58:26 +11:00
|
|
|
const _publicKey = publicKey(_privateKey);
|
|
|
|
|
const _nodeId = nodeId(_publicKey);
|
|
|
|
|
|
|
|
|
|
return new ENRKeyPair(_nodeId, _privateKey, _publicKey);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 13:33:20 +11:00
|
|
|
public async sign(msg: Uint8Array): Promise<Uint8Array> {
|
2021-10-26 16:58:26 +11:00
|
|
|
return sign(this.privateKey, msg);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-16 12:11:54 +11:00
|
|
|
public verify(msg: Uint8Array, sig: Uint8Array): boolean {
|
2021-10-26 16:58:26 +11:00
|
|
|
return verify(this.publicKey, msg, sig);
|
|
|
|
|
}
|
|
|
|
|
}
|