this repo has no description
1import * as secp from "@noble/secp256k1";
2import { base58btc } from "multiformats/bases/base58";
3
4const SECP256K1_MULTICODEC_PREFIX = new Uint8Array([0xe7, 0x01]);
5
6export interface Keypair {
7 privateKey: Uint8Array;
8 publicKey: Uint8Array;
9 publicKeyMultibase: string;
10 publicKeyDidKey: string;
11}
12
13export function generateKeypair(): Keypair {
14 const privateKey = secp.utils.randomPrivateKey();
15 const publicKey = secp.getPublicKey(privateKey, true);
16
17 const multicodecKey = new Uint8Array(
18 SECP256K1_MULTICODEC_PREFIX.length + publicKey.length,
19 );
20 multicodecKey.set(SECP256K1_MULTICODEC_PREFIX, 0);
21 multicodecKey.set(publicKey, SECP256K1_MULTICODEC_PREFIX.length);
22
23 const publicKeyMultibase = base58btc.encode(multicodecKey);
24 const publicKeyDidKey = `did:key:${publicKeyMultibase}`;
25
26 return {
27 privateKey,
28 publicKey,
29 publicKeyMultibase,
30 publicKeyDidKey,
31 };
32}
33
34function base64UrlEncode(data: Uint8Array | string): string {
35 const bytes = typeof data === "string"
36 ? new TextEncoder().encode(data)
37 : data;
38 const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('')
39 return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
40}
41
42export async function createServiceJwt(
43 privateKey: Uint8Array,
44 issuerDid: string,
45 audienceDid: string,
46 lxm: string,
47): Promise<string> {
48 const header = {
49 alg: "ES256K",
50 typ: "JWT",
51 };
52
53 const now = Math.floor(Date.now() / 1000);
54 const payload = {
55 iss: issuerDid,
56 sub: issuerDid,
57 aud: audienceDid,
58 exp: now + 180,
59 iat: now,
60 lxm: lxm,
61 };
62
63 const headerEncoded = base64UrlEncode(JSON.stringify(header));
64 const payloadEncoded = base64UrlEncode(JSON.stringify(payload));
65 const message = `${headerEncoded}.${payloadEncoded}`;
66
67 const msgBytes = new TextEncoder().encode(message);
68 const hashBuffer = await crypto.subtle.digest("SHA-256", msgBytes);
69 const msgHash = new Uint8Array(hashBuffer);
70 const signature = await secp.signAsync(msgHash, privateKey);
71 const sigBytes = signature.toCompactRawBytes();
72 const signatureEncoded = base64UrlEncode(sigBytes);
73
74 return `${message}.${signatureEncoded}`;
75}
76
77export function generateDidDocument(
78 did: string,
79 publicKeyMultibase: string,
80 handle: string,
81 pdsEndpoint: string,
82): object {
83 return {
84 "@context": [
85 "https://www.w3.org/ns/did/v1",
86 "https://w3id.org/security/multikey/v1",
87 "https://w3id.org/security/suites/secp256k1-2019/v1",
88 ],
89 id: did,
90 alsoKnownAs: [`at://${handle}`],
91 verificationMethod: [
92 {
93 id: `${did}#atproto`,
94 type: "Multikey",
95 controller: did,
96 publicKeyMultibase: publicKeyMultibase,
97 },
98 ],
99 service: [
100 {
101 id: "#atproto_pds",
102 type: "AtprotoPersonalDataServer",
103 serviceEndpoint: pdsEndpoint,
104 },
105 ],
106 };
107}