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