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 async function generateKeypair(): Promise<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 let binary = "";
39 for (let i = 0; i < bytes.length; i++) {
40 binary += String.fromCharCode(bytes[i]);
41 }
42 return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
43}
44
45export async function createServiceJwt(
46 privateKey: Uint8Array,
47 issuerDid: string,
48 audienceDid: string,
49 lxm: string,
50): Promise<string> {
51 const header = {
52 alg: "ES256K",
53 typ: "JWT",
54 };
55
56 const now = Math.floor(Date.now() / 1000);
57 const payload = {
58 iss: issuerDid,
59 sub: issuerDid,
60 aud: audienceDid,
61 exp: now + 180,
62 iat: now,
63 lxm: lxm,
64 };
65
66 const headerEncoded = base64UrlEncode(JSON.stringify(header));
67 const payloadEncoded = base64UrlEncode(JSON.stringify(payload));
68 const message = `${headerEncoded}.${payloadEncoded}`;
69
70 const msgBytes = new TextEncoder().encode(message);
71 const hashBuffer = await crypto.subtle.digest("SHA-256", msgBytes);
72 const msgHash = new Uint8Array(hashBuffer);
73 const signature = await secp.signAsync(msgHash, privateKey);
74 const sigBytes = signature.toCompactRawBytes();
75 const signatureEncoded = base64UrlEncode(sigBytes);
76
77 return `${message}.${signatureEncoded}`;
78}
79
80export function generateDidDocument(
81 did: string,
82 publicKeyMultibase: string,
83 handle: string,
84 pdsEndpoint: string,
85): object {
86 return {
87 "@context": [
88 "https://www.w3.org/ns/did/v1",
89 "https://w3id.org/security/multikey/v1",
90 "https://w3id.org/security/suites/secp256k1-2019/v1",
91 ],
92 id: did,
93 alsoKnownAs: [`at://${handle}`],
94 verificationMethod: [
95 {
96 id: `${did}#atproto`,
97 type: "Multikey",
98 controller: did,
99 publicKeyMultibase: publicKeyMultibase,
100 },
101 ],
102 service: [
103 {
104 id: "#atproto_pds",
105 type: "AtprotoPersonalDataServer",
106 serviceEndpoint: pdsEndpoint,
107 },
108 ],
109 };
110}