···11import "@atcute/ozone/lexicons";
22-import { fromBytes } from "@atcute/cbor";
32import { XRPCError } from "@atcute/client";
43import type {
54 At,
···109import fastify, { type FastifyInstance, type FastifyRequest } from "fastify";
1110import Database, { type Database as SQLiteDatabase } from "libsql";
1211import type { WebSocket } from "ws";
1313-import { verifyJwt } from "./util/crypto.js";
1212+import { parsePrivateKey, verifyJwt } from "./util/crypto.js";
1413import { formatLabel, labelIsSigned, signLabel } from "./util/labels.js";
1514import type {
1615 CreateLabelData,
···83828483 try {
8584 if (options.signingKey.startsWith("did:key:")) throw 0;
8686- this.#signingKey = fromBytes({ $bytes: options.signingKey });
8585+ this.#signingKey = parsePrivateKey(options.signingKey);
8786 if (this.#signingKey.byteLength !== 32) throw 0;
8888- } catch (e) {
8787+ } catch {
8988 throw new Error(INVALID_SIGNING_KEY_ERROR);
9089 }
9190
+7-4
src/bin.ts
···4141 }, {
4242 type: "text",
4343 name: "privateKey",
4444- message:
4545- "Enter a hex-encoded signing key to use, or leave blank to generate a new one:",
4444+ message: "Enter a signing key to use, or leave blank to generate a new one:",
46454747- validate: (value) =>
4848- !value || /^[0-9a-f]*$/.test(value) || "Must be a hex-encoded string.",
4646+ validate: (value) => {
4747+ if (!value) return true;
4848+ if (/^[0-9a-f]*$/.test(value)) return true;
4949+ if (/^[A-Za-z0-9+/=]+$/.test(value)) return true;
5050+ return "Must be a hex or base64-encoded string.";
5151+ },
4952 }], { onCancel: () => process.exit(1) });
50535154 const operation = await plcSetupLabeler({
+5-5
src/scripts/plc.ts
···11-import { fromBytes, toBytes } from "@atcute/cbor";
21import type { ComAtprotoIdentitySignPlcOperation } from "@atcute/client/lexicons";
32import { secp256k1 as k256 } from "@noble/curves/secp256k1";
44-import { formatDidKey, SECP256K1_JWT_ALG } from "../util/crypto.js";
33+import { toString as ui8ToString } from "uint8arrays/to-string";
44+import { formatDidKey, parsePrivateKey, SECP256K1_JWT_ALG } from "../util/crypto.js";
55import { loginAgent, LoginCredentials } from "./util.js";
6677/** Options for the {@link plcSetupLabeler} function. */
···1616 plcToken: string;
17171818 /**
1919- * You may choose to provide your own hex-encoded secp256k1 signing key to use for the labeler.
1919+ * You may choose to provide your own secp256k1 signing key to use for the labeler.
2020 * Leave this empty to generate a new keypair.
2121 */
2222 privateKey?: string | Uint8Array;
···5252 const privateKey = options.privateKey
5353 ? options.privateKey instanceof Uint8Array
5454 ? options.privateKey
5555- : fromBytes({ $bytes: options.privateKey })
5555+ : parsePrivateKey(options.privateKey)
5656 : k256.utils.randomPrivateKey();
57575858 const publicKey = k256.getPublicKey(privateKey);
···104104 });
105105106106 if (!options.privateKey && operation.verificationMethods) {
107107- const privateKeyString = toBytes(privateKey).$bytes;
107107+ const privateKeyString = ui8ToString(privateKey, "hex");
108108 console.log(
109109 "This is your labeler's signing key. It will be needed to sign any labels you create.",
110110 "You will not be able to retrieve this key again, so make sure to save it somewhere safe.",
+21
src/util/crypto.ts
···193193};
194194195195/**
196196+ * Parses a hex- or base64-encoded private key to a Uint8Array.
197197+ * @param privateKey The private key to parse.
198198+ */
199199+export const parsePrivateKey = (privateKey: string): Uint8Array => {
200200+ let keyBytes: Uint8Array | undefined;
201201+ try {
202202+ keyBytes = ui8.fromString(privateKey, "hex");
203203+ if (keyBytes.byteLength !== 32) throw 0;
204204+ } catch {
205205+ try {
206206+ keyBytes = ui8.fromString(privateKey, "base64url");
207207+ } catch {}
208208+ } finally {
209209+ if (!keyBytes) {
210210+ throw new Error("Invalid private key. Must be hex or base64url, and 32 bytes long.");
211211+ }
212212+ return keyBytes;
213213+ }
214214+};
215215+216216+/**
196217 * Formats a pubkey in did:key format.
197218 * @param jwtAlg The JWT algorithm used by the signing key.
198219 * @param keyBytes The bytes of the pubkey.