Add prettier, formatted files. Use bytes/hex conversions from noble.

This commit is contained in:
Jay
2025-10-24 12:58:28 -04:00
parent b23e56b2e6
commit 268f411633
16 changed files with 299 additions and 94 deletions

View File

@@ -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}$/;

View File

@@ -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.
*/

View File

@@ -1,2 +1,3 @@
import { test } from "vitest";
test("placeholder", () => {});

View File

@@ -1,2 +1,3 @@
import { test } from "vitest";
test("placeholder", () => {});

View File

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

View File

@@ -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 = {

View File

@@ -1,5 +1,4 @@
export * from "./types";
export * from "./constants";
export * from "./errors";
export { Event } from "./event";
export { Keys } from "./keys";

View File

@@ -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,.*/);
});
});

View File

@@ -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 = {

View File

@@ -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,.*/,
);
});
});

View File

@@ -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 = {

View File

@@ -1,4 +1,5 @@
import { test } from "vitest";
import type { EventData } from "./types";
test("placeholder", () => {});

View File

@@ -1,2 +1,3 @@
import { test } from "vitest";
test("placeholder", () => {});