Add prettier, formatted files. Use bytes/hex conversions from noble.
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Matches 64-character lowercase hexadecimal strings.
|
||||
* Used for validating event IDs and cryptographic keys.
|
||||
*/
|
||||
export const HEX_64_PATTERN = /^[a-f0-9]{64}$/;
|
||||
|
||||
/**
|
||||
* Matches 128-character lowercase hexadecimal strings.
|
||||
* Used for validating signatures.
|
||||
*/
|
||||
export const HEX_128_PATTERN = /^[a-f0-9]{128}$/;
|
||||
@@ -1,43 +1,3 @@
|
||||
/**
|
||||
* Public key is not 64 lowercase hex characters.
|
||||
*/
|
||||
export class MalformedPubKeyError extends Error {
|
||||
constructor() {
|
||||
super("public key must be 64 lowercase hex characters");
|
||||
this.name = "MalformedPubKeyError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private key is not 64 lowercase hex characters.
|
||||
*/
|
||||
export class MalformedPrivKeyError extends Error {
|
||||
constructor() {
|
||||
super("private key must be 64 lowercase hex characters");
|
||||
this.name = "MalformedPrivKeyError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event ID is not 64 hex characters.
|
||||
*/
|
||||
export class MalformedIDError extends Error {
|
||||
constructor() {
|
||||
super("event id must be 64 hex characters");
|
||||
this.name = "MalformedIDError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event signature is not 128 hex characters.
|
||||
*/
|
||||
export class MalformedSigError extends Error {
|
||||
constructor() {
|
||||
super("event signature must be 128 hex characters");
|
||||
this.name = "MalformedSigError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event tag contains fewer than two elements.
|
||||
*/
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import { test } from "vitest";
|
||||
|
||||
test("placeholder", () => {});
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import { test } from "vitest";
|
||||
|
||||
test("placeholder", () => {});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { describe, test, expect } from "vitest";
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { EventID } from "./id";
|
||||
import { testEvent, testPK } from "./util.test";
|
||||
import type { EventData } from "./types";
|
||||
import { testEvent, testPK } from "./util.test";
|
||||
|
||||
interface IDTestCase {
|
||||
name: string;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { sha256 } from "@noble/hashes/sha2.js";
|
||||
import { bytesToHex } from "@noble/hashes/utils.js";
|
||||
|
||||
import type { EventData } from "./types";
|
||||
|
||||
/**
|
||||
@@ -27,7 +29,7 @@ function serialize(event: EventData): string {
|
||||
function getID(event: EventData): string {
|
||||
const serialized = serialize(event);
|
||||
const hash = sha256(new TextEncoder().encode(serialized));
|
||||
return Buffer.from(hash).toString("hex");
|
||||
return bytesToHex(hash);
|
||||
}
|
||||
|
||||
export const EventID = {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from "./types";
|
||||
export * from "./constants";
|
||||
export * from "./errors";
|
||||
export { Event } from "./event";
|
||||
export { Keys } from "./keys";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { describe, test, expect } from "vitest";
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { Keys } from "./keys";
|
||||
import { HEX_64_PATTERN } from "./constants";
|
||||
import { testSK, testPK } from "./util.test";
|
||||
import { testPK, testSK } from "./util.test";
|
||||
|
||||
const HEX_64_PATTERN = /^[a-f0-9]{64}$/;
|
||||
|
||||
describe("Keys.generatePrivate", () => {
|
||||
test("returns 64 hex characters", () => {
|
||||
@@ -24,7 +26,7 @@ describe("Keys.getPublic", () => {
|
||||
|
||||
test("throws on invalid private key - too short", () => {
|
||||
expect(() => Keys.getPublicKey("abc123")).toThrow(
|
||||
"private key must be 64 lowercase hex characters",
|
||||
/"secret key" expected.*/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -33,14 +35,6 @@ describe("Keys.getPublic", () => {
|
||||
Keys.getPublicKey(
|
||||
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
|
||||
),
|
||||
).toThrow("private key must be 64 lowercase hex characters");
|
||||
});
|
||||
|
||||
test("throws on invalid private key - uppercase", () => {
|
||||
expect(() =>
|
||||
Keys.getPublicKey(
|
||||
"F43A0435F69529F310BBD1D6263D2FBF0977F54BFE2310CC37AE5904B83BB167",
|
||||
),
|
||||
).toThrow("private key must be 64 lowercase hex characters");
|
||||
).toThrow(/hex string expected,.*/);
|
||||
});
|
||||
});
|
||||
|
||||
13
src/keys.ts
13
src/keys.ts
@@ -1,6 +1,5 @@
|
||||
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
||||
import { schnorr } from "@noble/secp256k1";
|
||||
import { HEX_64_PATTERN } from "./constants";
|
||||
import { MalformedPrivKeyError } from "./errors";
|
||||
|
||||
/**
|
||||
* Generates a new random secp256k1 private key.
|
||||
@@ -8,7 +7,7 @@ import { MalformedPrivKeyError } from "./errors";
|
||||
*/
|
||||
function generatePrivateKey(): string {
|
||||
const { secretKey } = schnorr.keygen();
|
||||
return Buffer.from(secretKey).toString("hex");
|
||||
return bytesToHex(secretKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -18,14 +17,10 @@ function generatePrivateKey(): string {
|
||||
* @throws {MalformedPrivKeyError} If private key is not 64 lowercase hex characters
|
||||
*/
|
||||
function getPublicKey(privateKey: string): string {
|
||||
if (!HEX_64_PATTERN.test(privateKey)) {
|
||||
throw new MalformedPrivKeyError();
|
||||
}
|
||||
|
||||
const privateKeyBytes = Buffer.from(privateKey, "hex");
|
||||
const privateKeyBytes = hexToBytes(privateKey);
|
||||
const publicKeyBytes = schnorr.getPublicKey(privateKeyBytes);
|
||||
|
||||
return Buffer.from(publicKeyBytes).toString("hex");
|
||||
return bytesToHex(publicKeyBytes);
|
||||
}
|
||||
|
||||
export const Keys = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, test, expect } from "vitest";
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { Sign } from "./sign";
|
||||
import { testSK, testEvent } from "./util.test";
|
||||
import { testEvent, testSK } from "./util.test";
|
||||
|
||||
describe("Sign.sign", () => {
|
||||
test("produces correct signature", () => {
|
||||
@@ -10,13 +11,13 @@ describe("Sign.sign", () => {
|
||||
|
||||
test("throws on invalid event ID", () => {
|
||||
expect(() => Sign.sign("thisisabadeventid", testSK)).toThrow(
|
||||
"event id must be 64 hex characters",
|
||||
/hex string expected,.*/,
|
||||
);
|
||||
});
|
||||
|
||||
test("throws on invalid private key", () => {
|
||||
expect(() => Sign.sign(testEvent.id, "thisisabadsecretkey")).toThrow(
|
||||
"private key must be 64 lowercase hex characters",
|
||||
/hex string expected,.*/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
19
src/sign.ts
19
src/sign.ts
@@ -1,8 +1,8 @@
|
||||
import { hashes as secp_hashes } from "@noble/secp256k1";
|
||||
import { schnorr } from "@noble/secp256k1";
|
||||
import { hmac } from "@noble/hashes/hmac.js";
|
||||
import { sha256 } from "@noble/hashes/sha2.js";
|
||||
import { MalformedIDError, MalformedPrivKeyError } from "./errors";
|
||||
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
||||
import { hashes as secp_hashes } from "@noble/secp256k1";
|
||||
import { schnorr } from "@noble/secp256k1";
|
||||
|
||||
secp_hashes.hmacSha256 = (key, msg) => hmac(sha256, key, msg);
|
||||
secp_hashes.sha256 = sha256;
|
||||
@@ -16,19 +16,12 @@ secp_hashes.sha256 = sha256;
|
||||
* @throws {MalformedPrivKeyError} If private key is not 64 lowercase hex characters
|
||||
*/
|
||||
function sign(eventID: string, privateKey: string): string {
|
||||
const privateKeyBytes = Buffer.from(privateKey, "hex");
|
||||
if (privateKeyBytes.length !== 32) {
|
||||
throw new MalformedPrivKeyError();
|
||||
}
|
||||
|
||||
const idBytes = Buffer.from(eventID, "hex");
|
||||
if (idBytes.length !== 32) {
|
||||
throw new MalformedIDError();
|
||||
}
|
||||
const privateKeyBytes = hexToBytes(privateKey);
|
||||
const idBytes = hexToBytes(eventID);
|
||||
|
||||
const auxRand = sha256(privateKeyBytes);
|
||||
const signature = schnorr.sign(idBytes, privateKeyBytes, auxRand);
|
||||
return Buffer.from(signature).toString("hex");
|
||||
return bytesToHex(signature);
|
||||
}
|
||||
|
||||
export const Sign = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { test } from "vitest";
|
||||
|
||||
import type { EventData } from "./types";
|
||||
|
||||
test("placeholder", () => {});
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import { test } from "vitest";
|
||||
|
||||
test("placeholder", () => {});
|
||||
|
||||
Reference in New Issue
Block a user