passkey-kit 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +399 -227
  2. package/{types → dist}/base.d.ts +2 -1
  3. package/dist/base.d.ts.map +1 -0
  4. package/{src/base.ts → dist/base.js} +8 -10
  5. package/dist/base.js.map +1 -0
  6. package/dist/base64url.d.ts +18 -0
  7. package/dist/base64url.d.ts.map +1 -0
  8. package/dist/base64url.js +18 -0
  9. package/dist/base64url.js.map +1 -0
  10. package/dist/constants.d.ts +87 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +111 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/contract-errors.d.ts +69 -0
  15. package/dist/contract-errors.d.ts.map +1 -0
  16. package/dist/contract-errors.js +129 -0
  17. package/dist/contract-errors.js.map +1 -0
  18. package/dist/errors.d.ts +163 -0
  19. package/dist/errors.d.ts.map +1 -0
  20. package/dist/errors.js +244 -0
  21. package/dist/errors.js.map +1 -0
  22. package/dist/events.d.ts +94 -0
  23. package/dist/events.d.ts.map +1 -0
  24. package/dist/events.js +92 -0
  25. package/dist/events.js.map +1 -0
  26. package/dist/index.d.ts +22 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +36 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/indexer/codec.d.ts +18 -0
  31. package/dist/indexer/codec.d.ts.map +1 -0
  32. package/dist/indexer/codec.js +32 -0
  33. package/dist/indexer/codec.js.map +1 -0
  34. package/dist/indexer/index.d.ts +25 -0
  35. package/dist/indexer/index.d.ts.map +1 -0
  36. package/dist/indexer/index.js +34 -0
  37. package/dist/indexer/index.js.map +1 -0
  38. package/dist/indexer/mercury.d.ts +88 -0
  39. package/dist/indexer/mercury.d.ts.map +1 -0
  40. package/dist/indexer/mercury.js +237 -0
  41. package/dist/indexer/mercury.js.map +1 -0
  42. package/dist/indexer/stellar-indexer.d.ts +115 -0
  43. package/dist/indexer/stellar-indexer.d.ts.map +1 -0
  44. package/dist/indexer/stellar-indexer.js +279 -0
  45. package/dist/indexer/stellar-indexer.js.map +1 -0
  46. package/dist/indexer/types.d.ts +73 -0
  47. package/dist/indexer/types.d.ts.map +1 -0
  48. package/dist/indexer/types.js +9 -0
  49. package/dist/indexer/types.js.map +1 -0
  50. package/dist/kit/auth-payload.d.ts +89 -0
  51. package/dist/kit/auth-payload.d.ts.map +1 -0
  52. package/dist/kit/auth-payload.js +203 -0
  53. package/dist/kit/auth-payload.js.map +1 -0
  54. package/dist/kit/deploy-ops.d.ts +45 -0
  55. package/dist/kit/deploy-ops.d.ts.map +1 -0
  56. package/dist/kit/deploy-ops.js +69 -0
  57. package/dist/kit/deploy-ops.js.map +1 -0
  58. package/dist/kit/tx-ops.d.ts +92 -0
  59. package/dist/kit/tx-ops.d.ts.map +1 -0
  60. package/dist/kit/tx-ops.js +113 -0
  61. package/dist/kit/tx-ops.js.map +1 -0
  62. package/dist/kit/wallet-ops.d.ts +59 -0
  63. package/dist/kit/wallet-ops.d.ts.map +1 -0
  64. package/dist/kit/wallet-ops.js +122 -0
  65. package/dist/kit/wallet-ops.js.map +1 -0
  66. package/dist/kit/webauthn-ops.d.ts +55 -0
  67. package/dist/kit/webauthn-ops.d.ts.map +1 -0
  68. package/dist/kit/webauthn-ops.js +79 -0
  69. package/dist/kit/webauthn-ops.js.map +1 -0
  70. package/dist/kit.d.ts +128 -0
  71. package/dist/kit.d.ts.map +1 -0
  72. package/dist/kit.js +276 -0
  73. package/dist/kit.js.map +1 -0
  74. package/dist/managers/credential-manager.d.ts +39 -0
  75. package/dist/managers/credential-manager.d.ts.map +1 -0
  76. package/dist/managers/credential-manager.js +47 -0
  77. package/dist/managers/credential-manager.js.map +1 -0
  78. package/dist/managers/index.d.ts +7 -0
  79. package/dist/managers/index.d.ts.map +1 -0
  80. package/dist/managers/index.js +7 -0
  81. package/dist/managers/index.js.map +1 -0
  82. package/dist/managers/signer-manager.d.ts +51 -0
  83. package/dist/managers/signer-manager.d.ts.map +1 -0
  84. package/dist/managers/signer-manager.js +92 -0
  85. package/dist/managers/signer-manager.js.map +1 -0
  86. package/dist/managers/submission-manager.d.ts +52 -0
  87. package/dist/managers/submission-manager.d.ts.map +1 -0
  88. package/dist/managers/submission-manager.js +77 -0
  89. package/dist/managers/submission-manager.js.map +1 -0
  90. package/dist/relayer.d.ts +61 -0
  91. package/dist/relayer.d.ts.map +1 -0
  92. package/dist/relayer.js +119 -0
  93. package/dist/relayer.js.map +1 -0
  94. package/dist/rpc-data.d.ts +35 -0
  95. package/dist/rpc-data.d.ts.map +1 -0
  96. package/dist/rpc-data.js +49 -0
  97. package/dist/rpc-data.js.map +1 -0
  98. package/dist/sac.d.ts +31 -0
  99. package/dist/sac.d.ts.map +1 -0
  100. package/dist/sac.js +47 -0
  101. package/dist/sac.js.map +1 -0
  102. package/dist/server.d.ts +79 -0
  103. package/dist/server.d.ts.map +1 -0
  104. package/dist/server.js +152 -0
  105. package/dist/server.js.map +1 -0
  106. package/dist/signers.d.ts +83 -0
  107. package/dist/signers.d.ts.map +1 -0
  108. package/dist/signers.js +124 -0
  109. package/dist/signers.js.map +1 -0
  110. package/dist/storage/index.d.ts +13 -0
  111. package/dist/storage/index.d.ts.map +1 -0
  112. package/dist/storage/index.js +12 -0
  113. package/dist/storage/index.js.map +1 -0
  114. package/dist/storage/indexeddb.d.ts +26 -0
  115. package/dist/storage/indexeddb.d.ts.map +1 -0
  116. package/dist/storage/indexeddb.js +100 -0
  117. package/dist/storage/indexeddb.js.map +1 -0
  118. package/dist/storage/localStorage.d.ts +22 -0
  119. package/dist/storage/localStorage.d.ts.map +1 -0
  120. package/dist/storage/localStorage.js +86 -0
  121. package/dist/storage/localStorage.js.map +1 -0
  122. package/dist/storage/memory.d.ts +18 -0
  123. package/dist/storage/memory.d.ts.map +1 -0
  124. package/dist/storage/memory.js +41 -0
  125. package/dist/storage/memory.js.map +1 -0
  126. package/dist/types.d.ts +136 -0
  127. package/dist/types.d.ts.map +1 -0
  128. package/dist/types.js +39 -0
  129. package/dist/types.js.map +1 -0
  130. package/dist/utils.d.ts +52 -0
  131. package/dist/utils.d.ts.map +1 -0
  132. package/dist/utils.js +153 -0
  133. package/dist/utils.js.map +1 -0
  134. package/dist/validation.d.ts +40 -0
  135. package/dist/validation.d.ts.map +1 -0
  136. package/dist/validation.js +73 -0
  137. package/dist/validation.js.map +1 -0
  138. package/dist/version.d.ts +7 -0
  139. package/dist/version.d.ts.map +1 -0
  140. package/dist/version.js +9 -0
  141. package/dist/version.js.map +1 -0
  142. package/package.json +48 -20
  143. package/.claude/settings.local.json +0 -20
  144. package/.cursorrules +0 -170
  145. package/.gitattributes +0 -2
  146. package/.github/workflows/release.yml +0 -21
  147. package/.vscode/settings.json +0 -6
  148. package/PROPOSAL.md +0 -397
  149. package/cheatsheet.txt +0 -18
  150. package/clone-js-sdk.sh +0 -21
  151. package/packages/passkey-kit-sdk/README.md +0 -54
  152. package/packages/passkey-kit-sdk/package.json +0 -18
  153. package/packages/passkey-kit-sdk/src/index.ts +0 -171
  154. package/packages/passkey-kit-sdk/tsconfig.json +0 -98
  155. package/packages/passkey-kit-sdk/types/index.d.ts +0 -190
  156. package/packages/sac-sdk/README.md +0 -54
  157. package/packages/sac-sdk/package.json +0 -18
  158. package/packages/sac-sdk/src/index.ts +0 -586
  159. package/packages/sac-sdk/tsconfig.json +0 -98
  160. package/packages/sac-sdk/types/index.d.ts +0 -532
  161. package/pnpm-workspace.yaml +0 -2
  162. package/src/index.ts +0 -5
  163. package/src/kit.ts +0 -698
  164. package/src/sac.ts +0 -29
  165. package/src/server.ts +0 -179
  166. package/src/types.ts +0 -32
  167. package/tsconfig.json +0 -28
  168. package/types/index.d.ts +0 -5
  169. package/types/kit.d.ts +0 -89
  170. package/types/sac.d.ts +0 -13
  171. package/types/server.d.ts +0 -29
  172. package/types/types.d.ts +0 -22
package/src/kit.ts DELETED
@@ -1,698 +0,0 @@
1
- import { Client as PasskeyClient, type Signature, type SignerKey as SDKSignerKey, type SignerLimits as SDKSignerLimits } from 'passkey-kit-sdk'
2
- import { StrKey, hash, xdr, Keypair, Address, TransactionBuilder, Operation } from '@stellar/stellar-sdk/minimal'
3
- import { startRegistration, startAuthentication, type AuthenticationResponseJSON, type AuthenticatorAttestationResponseJSON, type AuthenticatorSelectionCriteria } from "@simplewebauthn/browser"
4
- import { Buffer } from 'buffer'
5
- import base64url from 'base64url'
6
- import type { SignerKey, SignerLimits, SignerStore } from './types'
7
- import { PasskeyBase } from './base'
8
- import { AssembledTransaction, basicNodeSigner, type AssembledTransactionOptions, type Tx } from '@stellar/stellar-sdk/minimal/contract'
9
- import type { Server } from '@stellar/stellar-sdk/minimal/rpc'
10
-
11
- // TODO we should allow setting the rpId in the constructor so we don't have to keep setting it for every call
12
- // e.g. if you want to sign with a smol.xyz key on next.smol.xyz key you have to set rpId over and over again
13
-
14
- export class PasskeyKit extends PasskeyBase {
15
- declare rpc: Server
16
- declare rpcUrl: string
17
-
18
- private walletKeypair: Keypair
19
- private walletPublicKey: string
20
- private walletWasmHash: string
21
- private timeoutInSeconds: number
22
- private WebAuthn: {
23
- startRegistration: typeof startRegistration,
24
- startAuthentication: typeof startAuthentication
25
- }
26
-
27
- public keyId: string | undefined
28
- public networkPassphrase: string
29
- public wallet: PasskeyClient | undefined
30
-
31
- constructor(options: {
32
- rpcUrl: string,
33
- networkPassphrase: string,
34
- walletWasmHash: string,
35
- timeoutInSeconds?: number,
36
- WebAuthn?: {
37
- startRegistration: typeof startRegistration,
38
- startAuthentication: typeof startAuthentication
39
- }
40
- }) {
41
- const { rpcUrl, networkPassphrase, walletWasmHash, WebAuthn } = options
42
-
43
- super(rpcUrl)
44
-
45
- this.networkPassphrase = networkPassphrase
46
- // this account exists as the seed source for deploying new wallets
47
- // not using the genesis wallet as on mainnet the account has no usable signers
48
- // there's a chance this isn't the best move and should instead be a constructor variable
49
- // alternatively when we create a new wallet we shouldn't inherit the source as the auth entry signer
50
- // Keypair.fromRawEd25519Seed(hash(Buffer.from(this.networkPassphrase)))
51
- this.walletKeypair = Keypair.fromRawEd25519Seed(hash(Buffer.from('kalepail')));
52
- this.walletPublicKey = this.walletKeypair.publicKey()
53
- this.walletWasmHash = walletWasmHash
54
- this.timeoutInSeconds = options.timeoutInSeconds || 30 // OpenZeppelin Relayer requires <= 30 second timeout so let's default to that
55
- this.WebAuthn = WebAuthn || { startRegistration, startAuthentication }
56
- }
57
-
58
- public async createWallet(
59
- app: string,
60
- user: string,
61
- settings?: {
62
- rpId?: string
63
- authenticatorSelection?: AuthenticatorSelectionCriteria
64
- }
65
- ) {
66
- const { rawResponse, keyId, keyIdBase64, publicKey } = await this.createKey(app, user, settings)
67
-
68
- const at = await PasskeyClient.deploy(
69
- {
70
- signer: {
71
- tag: 'Secp256r1',
72
- values: [
73
- keyId,
74
- publicKey,
75
- [undefined],
76
- [undefined],
77
- { tag: 'Persistent', values: undefined },
78
- ]
79
- }
80
- },
81
- {
82
- rpcUrl: this.rpcUrl,
83
- wasmHash: this.walletWasmHash,
84
- networkPassphrase: this.networkPassphrase,
85
- publicKey: this.walletPublicKey,
86
- salt: hash(keyId),
87
- timeoutInSeconds: this.timeoutInSeconds,
88
- }
89
- )
90
-
91
- const contractId = at.result.options.contractId
92
-
93
- this.wallet = new PasskeyClient({
94
- contractId,
95
- networkPassphrase: this.networkPassphrase,
96
- rpcUrl: this.rpcUrl
97
- })
98
-
99
- await at.sign({
100
- signTransaction: basicNodeSigner(this.walletKeypair, this.networkPassphrase).signTransaction
101
- })
102
-
103
- return {
104
- rawResponse,
105
- keyId,
106
- keyIdBase64,
107
- contractId,
108
- signedTx: at.signed!
109
- }
110
- }
111
-
112
- public async createKey(app: string, user: string, settings?: {
113
- rpId?: string
114
- authenticatorSelection?: AuthenticatorSelectionCriteria
115
- }) {
116
- const now = new Date()
117
- const displayName = `${user} — ${now.toLocaleString()}`
118
- const { rpId, authenticatorSelection = {
119
- residentKey: "preferred",
120
- userVerification: "preferred",
121
- } } = settings || {}
122
-
123
- // TODO discover the contract id before creating the key so we can use it in the key name
124
- // TODO it's possible for the creation to fail in which case we've created a passkey but it's not onchain.
125
- // In this case we should save the passkey info and retry uploading it async vs asking the user to create another passkey
126
- // This does introduce a storage dependency though so it likely needs to be a function with some logic for choosing how to store the passkey data
127
-
128
- const rawResponse = await this.WebAuthn.startRegistration({
129
- optionsJSON: {
130
- challenge: base64url("stellaristhebetterblockchain"),
131
- rp: {
132
- id: rpId,
133
- name: app,
134
- },
135
- user: {
136
- id: base64url(`${user}:${now.getTime()}:${Math.random()}`),
137
- name: displayName,
138
- displayName
139
- },
140
- authenticatorSelection,
141
- pubKeyCredParams: [{ alg: -7, type: "public-key" }],
142
- }
143
- });
144
- const { id, response } = rawResponse
145
-
146
- if (!this.keyId)
147
- this.keyId = id;
148
-
149
- return {
150
- rawResponse,
151
- keyId: base64url.toBuffer(id),
152
- keyIdBase64: id,
153
- publicKey: await this.getPublicKey(response),
154
- }
155
- }
156
-
157
- public async connectWallet(opts?: {
158
- rpId?: string,
159
- keyId?: string | Uint8Array,
160
- getContractId?: (keyId: string) => Promise<string | undefined>,
161
- // TEMP for backwards compatibility for when we seeded wallets from a factory address
162
- // Consider putting this somewhere else??
163
- walletPublicKey?: string
164
- }) {
165
- let { rpId, keyId, getContractId, walletPublicKey } = opts || {}
166
- let keyIdBuffer: Buffer
167
- let rawResponse: AuthenticationResponseJSON | undefined;
168
-
169
- if (!keyId) {
170
- rawResponse = await this.WebAuthn.startAuthentication({
171
- optionsJSON: {
172
- challenge: base64url("stellaristhebetterblockchain"),
173
- rpId,
174
- userVerification: "preferred",
175
- }
176
- });
177
-
178
- keyId = rawResponse.id
179
- }
180
-
181
- if (keyId instanceof Uint8Array) {
182
- keyIdBuffer = Buffer.from(keyId)
183
- keyId = base64url(keyIdBuffer)
184
- } else {
185
- keyIdBuffer = base64url.toBuffer(keyId)
186
- }
187
-
188
- if (!this.keyId)
189
- this.keyId = keyId
190
-
191
- // Check for the contractId on-chain as a derivation from the keyId. This is the easiest and "cheapest" check however it will only work for the initially deployed passkey if it was used as derivation
192
- let contractId: string | undefined = this.encodeContract(this.walletPublicKey, keyIdBuffer);
193
-
194
- // attempt passkey id derivation
195
- try {
196
- // TODO what is the error if the entry exists but is archived?
197
- await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
198
- }
199
- // if that fails look up from the `getContractId` function
200
- catch {
201
- contractId = getContractId && await getContractId(keyId)
202
- }
203
-
204
- ////
205
- // TEMP for backwards compatibility for when we seeded wallets from a factory address
206
- // Consider putting this in the constructor
207
- if (!contractId && walletPublicKey) {
208
- contractId = this.encodeContract(walletPublicKey, keyIdBuffer);
209
-
210
- try {
211
- await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
212
- } catch {
213
- contractId = undefined
214
- }
215
- }
216
- ////
217
-
218
- if (!contractId)
219
- throw new Error('Failed to connect wallet')
220
-
221
- this.wallet = new PasskeyClient({
222
- contractId,
223
- rpcUrl: this.rpcUrl,
224
- networkPassphrase: this.networkPassphrase,
225
- })
226
-
227
- return {
228
- rawResponse,
229
- keyId: keyIdBuffer,
230
- keyIdBase64: keyId,
231
- contractId
232
- }
233
- }
234
-
235
- public async signAuthEntry(
236
- entry: xdr.SorobanAuthorizationEntry,
237
- options?: {
238
- rpId?: string,
239
- keyId?: 'any' | string | Uint8Array
240
- keypair?: Keypair,
241
- policy?: string,
242
- expiration?: number
243
- }
244
- ) {
245
- let { rpId, keyId, keypair, policy, expiration } = options || {}
246
-
247
- if ([keyId, keypair, policy].filter((arg) => !!arg).length > 1)
248
- throw new Error('Exactly one of `options.keyId`, `options.keypair`, or `options.policy` must be provided.');
249
-
250
- const credentials = entry.credentials().address();
251
-
252
- if (!expiration) {
253
- expiration = credentials.signatureExpirationLedger()
254
-
255
- if (!expiration) {
256
- const { sequence } = await this.rpc.getLatestLedger()
257
- expiration = sequence + this.timeoutInSeconds / 5; // assumes 5 second ledger time
258
- }
259
- }
260
-
261
- credentials.signatureExpirationLedger(expiration)
262
-
263
- const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
264
- new xdr.HashIdPreimageSorobanAuthorization({
265
- networkId: hash(Buffer.from(this.networkPassphrase)),
266
- nonce: credentials.nonce(),
267
- signatureExpirationLedger: credentials.signatureExpirationLedger(),
268
- invocation: entry.rootInvocation()
269
- })
270
- )
271
-
272
- const payload = hash(preimage.toXDR())
273
-
274
- // let signatures: Signatures
275
- let key: SDKSignerKey
276
- let val: Signature | undefined
277
-
278
- // const scSpecTypeDefSignatures = xdr.ScSpecTypeDef.scSpecTypeUdt(
279
- // new xdr.ScSpecTypeUdt({ name: "Signatures" }),
280
- // );
281
-
282
- // switch (credentials.signature().switch()) {
283
- // case xdr.ScValType.scvVoid():
284
- // signatures = [new Map()]
285
- // break;
286
- // default: {
287
- // signatures = this.wallet!.spec.scValToNative(credentials.signature(), scSpecTypeDefSignatures)
288
- // }
289
- // }
290
-
291
- // Sign with a policy
292
- if (policy) {
293
- key = {
294
- tag: "Policy",
295
- values: [policy]
296
- }
297
- val = {
298
- tag: "Policy",
299
- values: undefined,
300
- }
301
- }
302
-
303
- // Sign with the keypair as an ed25519 signer
304
- else if (keypair) {
305
- const signature = keypair.sign(payload);
306
-
307
- key = {
308
- tag: "Ed25519",
309
- values: [keypair.rawPublicKey()]
310
- }
311
- val = {
312
- tag: "Ed25519",
313
- values: [signature],
314
- }
315
- }
316
-
317
- // Default, use passkey
318
- else {
319
- const authenticationResponse = await this.WebAuthn.startAuthentication({
320
- optionsJSON: keyId === 'any'
321
- || (!keyId && !this.keyId)
322
- ? {
323
- challenge: base64url(payload),
324
- rpId,
325
- userVerification: "preferred",
326
- }
327
- : {
328
- challenge: base64url(payload),
329
- rpId,
330
- allowCredentials: [
331
- {
332
- id: keyId instanceof Uint8Array
333
- ? base64url(Buffer.from(keyId))
334
- : keyId || this.keyId!,
335
- type: "public-key",
336
- },
337
- ],
338
- userVerification: "preferred",
339
- }
340
- });
341
-
342
- key = {
343
- tag: "Secp256r1",
344
- values: [base64url.toBuffer(authenticationResponse.id)]
345
- }
346
- val = {
347
- tag: "Secp256r1",
348
- values: [
349
- {
350
- authenticator_data: base64url.toBuffer(
351
- authenticationResponse.response.authenticatorData,
352
- ),
353
- client_data_json: base64url.toBuffer(
354
- authenticationResponse.response.clientDataJSON,
355
- ),
356
- signature: this.compactSignature(
357
- base64url.toBuffer(authenticationResponse.response.signature)
358
- ),
359
- },
360
- ],
361
- }
362
- }
363
-
364
- const scKeyType = xdr.ScSpecTypeDef.scSpecTypeUdt(
365
- new xdr.ScSpecTypeUdt({ name: "SignerKey" }),
366
- );
367
- const scValType = xdr.ScSpecTypeDef.scSpecTypeUdt(
368
- new xdr.ScSpecTypeUdt({ name: "Signature" }),
369
- );
370
- const scKey = this.wallet!.spec.nativeToScVal(key, scKeyType);
371
- const scVal = val ? this.wallet!.spec.nativeToScVal(val, scValType) : xdr.ScVal.scvVoid();
372
- const scEntry = new xdr.ScMapEntry({
373
- key: scKey,
374
- val: scVal,
375
- })
376
-
377
- switch (credentials.signature().switch().name) {
378
- case 'scvVoid':
379
- credentials.signature(xdr.ScVal.scvVec([
380
- xdr.ScVal.scvMap([scEntry])
381
- ]))
382
- break;
383
- case 'scvVec':
384
- // Add the new signature to the existing map
385
- credentials.signature().vec()?.[0].map()?.push(scEntry)
386
-
387
- // Order the map by key
388
- // Not using Buffer.compare because Symbols are 9 bytes and unused bytes _append_ 0s vs prepending them, which is too bad
389
- credentials.signature().vec()?.[0].map()?.sort((a, b) => {
390
- return (
391
- a.key().vec()![0].sym() +
392
- a.key().vec()![1].toXDR().join('')
393
- ).localeCompare(
394
- b.key().vec()![0].sym() +
395
- b.key().vec()![1].toXDR().join('')
396
- )
397
- })
398
- break;
399
- default:
400
- throw new Error('Unsupported signature')
401
- }
402
-
403
- // Insert the new signature into the signatures Map
404
- // signatures[0].set(key, val)
405
-
406
- // Insert the new signatures Map into the credentials
407
- // credentials.signature(
408
- // this.wallet!.spec.nativeToScVal(signatures, scSpecTypeDefSignatures)
409
- // )
410
-
411
- // Order the signatures map
412
- // credentials.signature().vec()?.[0].map()?.sort((a, b) => {
413
- // return (
414
- // a.key().vec()![0].sym() +
415
- // a.key().vec()![1].toXDR().join('')
416
- // ).localeCompare(
417
- // b.key().vec()![0].sym() +
418
- // b.key().vec()![1].toXDR().join('')
419
- // )
420
- // })
421
-
422
- return entry
423
- }
424
-
425
- public async sign<T>(
426
- txn: AssembledTransaction<T> | Tx | string,
427
- options?: {
428
- rpId?: string,
429
- keyId?: 'any' | string | Uint8Array
430
- keypair?: Keypair,
431
- policy?: string,
432
- expiration?: number
433
- }
434
- ) {
435
- if (!(txn instanceof AssembledTransaction)) {
436
- try {
437
- txn = AssembledTransaction.fromXDR(this.wallet!.options, typeof txn === 'string' ? txn : txn.toXDR(), this.wallet!.spec);
438
- } catch {
439
- if (!(txn instanceof AssembledTransaction)) {
440
- const built = TransactionBuilder.fromXDR(typeof txn === 'string' ? txn : txn.toXDR(), this.networkPassphrase);
441
- const operation = built.operations[0] as Operation.InvokeHostFunction;
442
-
443
- txn = await AssembledTransaction.buildWithOp<T>(
444
- Operation.invokeHostFunction({ func: operation.func }),
445
- this.wallet!.options as AssembledTransactionOptions<T>
446
- );
447
- }
448
- }
449
- }
450
-
451
- await txn.signAuthEntries({
452
- address: this.wallet!.options.contractId,
453
- authorizeEntry: (entry) => {
454
- const clone = xdr.SorobanAuthorizationEntry.fromXDR(entry.toXDR())
455
- return this.signAuthEntry(clone, options)
456
- },
457
- })
458
-
459
- return txn
460
- }
461
-
462
- public addSecp256r1(keyId: string | Uint8Array, publicKey: string | Uint8Array, limits: SignerLimits, store: SignerStore, expiration?: number) {
463
- return this.secp256r1(keyId, publicKey, limits, store, 'add_signer', expiration)
464
- }
465
- public addEd25519(publicKey: string, limits: SignerLimits, store: SignerStore, expiration?: number) {
466
- return this.ed25519(publicKey, limits, store, 'add_signer', expiration)
467
- }
468
- public addPolicy(policy: string, limits: SignerLimits, store: SignerStore, expiration?: number) {
469
- return this.policy(policy, limits, store, 'add_signer', expiration)
470
- }
471
-
472
- public updateSecp256r1(keyId: string | Uint8Array, publicKey: string | Uint8Array, limits: SignerLimits, store: SignerStore, expiration?: number) {
473
- return this.secp256r1(keyId, publicKey, limits, store, 'update_signer', expiration)
474
- }
475
- public updateEd25519(publicKey: string, limits: SignerLimits, store: SignerStore, expiration?: number) {
476
- return this.ed25519(publicKey, limits, store, 'update_signer', expiration)
477
- }
478
- public updatePolicy(policy: string, limits: SignerLimits, store: SignerStore, expiration?: number) {
479
- return this.policy(policy, limits, store, 'update_signer', expiration)
480
- }
481
-
482
- public remove(signer: SignerKey) {
483
- return this.wallet!.remove_signer({
484
- signer_key: this.getSignerKey(signer)
485
- }, {
486
- timeoutInSeconds: this.timeoutInSeconds,
487
- });
488
- }
489
-
490
- private secp256r1(keyId: string | Uint8Array, publicKey: string | Uint8Array, limits: SignerLimits, store: SignerStore, fn: 'add_signer' | 'update_signer', expiration?: number) {
491
- keyId = typeof keyId === 'string' ? base64url.toBuffer(keyId) : keyId
492
- publicKey = typeof publicKey === 'string' ? base64url.toBuffer(publicKey) : publicKey
493
-
494
- return this.wallet![fn]({
495
- signer: {
496
- tag: "Secp256r1",
497
- values: [
498
- Buffer.from(keyId),
499
- Buffer.from(publicKey),
500
- [expiration],
501
- this.getSignerLimits(limits),
502
- { tag: store, values: undefined },
503
- ],
504
- },
505
- }, {
506
- timeoutInSeconds: this.timeoutInSeconds,
507
- });
508
- }
509
- private ed25519(publicKey: string, limits: SignerLimits, store: SignerStore, fn: 'add_signer' | 'update_signer', expiration?: number) {
510
- return this.wallet![fn]({
511
- signer: {
512
- tag: "Ed25519",
513
- values: [
514
- Keypair.fromPublicKey(publicKey).rawPublicKey(),
515
- [expiration],
516
- this.getSignerLimits(limits),
517
- { tag: store, values: undefined },
518
- ],
519
- },
520
- }, {
521
- timeoutInSeconds: this.timeoutInSeconds,
522
- });
523
- }
524
- private policy(policy: string, limits: SignerLimits, store: SignerStore, fn: 'add_signer' | 'update_signer', expiration?: number) {
525
- return this.wallet![fn]({
526
- signer: {
527
- tag: "Policy",
528
- values: [
529
- policy,
530
- [expiration],
531
- this.getSignerLimits(limits),
532
- { tag: store, values: undefined },
533
- ],
534
- },
535
- }, {
536
- timeoutInSeconds: this.timeoutInSeconds,
537
- });
538
- }
539
-
540
- /* LATER
541
- - Add a getKeyInfo action to get info about a specific passkey
542
- Specifically looking for name, type, etc. data so a user could grok what signer mapped to what passkey
543
- */
544
-
545
- private getSignerLimits(limits: SignerLimits) {
546
- if (!limits)
547
- return [undefined] as SDKSignerLimits
548
-
549
- const sdk_limits: SDKSignerLimits = [new Map()]
550
-
551
- for (const [contract, signer_keys] of limits.entries()) {
552
- let sdk_signer_keys: SDKSignerKey[] | undefined
553
-
554
- if (signer_keys?.length) {
555
- sdk_signer_keys = []
556
-
557
- for (const signer_key of signer_keys) {
558
- sdk_signer_keys.push(
559
- this.getSignerKey(signer_key)
560
- )
561
- }
562
- }
563
-
564
- sdk_limits[0]?.set(contract, sdk_signer_keys)
565
- }
566
-
567
- return sdk_limits
568
- }
569
-
570
- private getSignerKey({ key: tag, value }: SignerKey) {
571
- let signer_key: SDKSignerKey
572
-
573
- switch (tag) {
574
- case 'Policy':
575
- signer_key = {
576
- tag,
577
- values: [value]
578
- }
579
- break;
580
- case 'Ed25519':
581
- signer_key = {
582
- tag,
583
- values: [Keypair.fromPublicKey(value).rawPublicKey()]
584
- }
585
- break;
586
- case 'Secp256r1':
587
- signer_key = {
588
- tag,
589
- values: [base64url.toBuffer(value)]
590
- }
591
- break;
592
- }
593
-
594
- return signer_key
595
- }
596
-
597
- private encodeContract(walletPublicKey: string, keyIdBuffer: Buffer) {
598
- let contractId: string | undefined = StrKey.encodeContract(hash(xdr.HashIdPreimage.envelopeTypeContractId(
599
- new xdr.HashIdPreimageContractId({
600
- networkId: hash(Buffer.from(this.networkPassphrase)),
601
- contractIdPreimage: xdr.ContractIdPreimage.contractIdPreimageFromAddress(
602
- new xdr.ContractIdPreimageFromAddress({
603
- address: Address.fromString(walletPublicKey).toScAddress(),
604
- salt: hash(keyIdBuffer),
605
- })
606
- )
607
- })
608
- ).toXDR()));
609
-
610
- return contractId
611
- }
612
-
613
- private async getPublicKey(response: AuthenticatorAttestationResponseJSON) {
614
- let publicKey: Buffer | undefined
615
-
616
- if (response.publicKey) {
617
- publicKey = base64url.toBuffer(response.publicKey)
618
- publicKey = publicKey?.slice(publicKey.length - 65)
619
- }
620
-
621
- if (
622
- !publicKey
623
- || publicKey[0] !== 0x04
624
- || publicKey.length !== 65
625
- ) {
626
- let x: Buffer
627
- let y: Buffer
628
-
629
- if (response.authenticatorData) {
630
- const authenticatorData = base64url.toBuffer(response.authenticatorData)
631
- const credentialIdLength = (authenticatorData[53] << 8) | authenticatorData[54]
632
-
633
- x = authenticatorData.slice(65 + credentialIdLength, 97 + credentialIdLength)
634
- y = authenticatorData.slice(100 + credentialIdLength, 132 + credentialIdLength)
635
- } else {
636
- const attestationObject = base64url.toBuffer(response.attestationObject)
637
-
638
- let publicKeykPrefixSlice = Buffer.from([0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20])
639
- let startIndex = attestationObject.indexOf(publicKeykPrefixSlice)
640
- startIndex = startIndex + publicKeykPrefixSlice.length
641
-
642
- x = attestationObject.slice(startIndex, 32 + startIndex)
643
- y = attestationObject.slice(35 + startIndex, 67 + startIndex)
644
- }
645
-
646
- publicKey = Buffer.from([
647
- 0x04, // (0x04 prefix) https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
648
- ...x,
649
- ...y
650
- ])
651
- }
652
-
653
- /* TODO
654
- - We're doing some pretty "smart" public key decoding stuff so we should verify the signature against this final public key before assuming it's safe to use and save on-chain
655
- Hmm...Given that `startRegistration` doesn't produce a signature, verifying we've got the correct public key isn't really possible
656
- - This probably needs to be an onchain check, even if just a simulation, just to ensure everything looks good before we get too far adding value etc.
657
- */
658
-
659
- return publicKey
660
- }
661
-
662
- private compactSignature(signature: Buffer) {
663
- // Decode the DER signature
664
- let offset = 2;
665
-
666
- const rLength = signature[offset + 1];
667
- const r = signature.slice(offset + 2, offset + 2 + rLength);
668
-
669
- offset += 2 + rLength;
670
-
671
- const sLength = signature[offset + 1];
672
- const s = signature.slice(offset + 2, offset + 2 + sLength);
673
-
674
- // Convert r and s to BigInt
675
- const rBigInt = BigInt('0x' + r.toString('hex'));
676
- let sBigInt = BigInt('0x' + s.toString('hex'));
677
-
678
- // Ensure s is in the low-S form
679
- // https://github.com/stellar/stellar-protocol/discussions/1435#discussioncomment-8809175
680
- // https://discord.com/channels/897514728459468821/1233048618571927693
681
- // Define the order of the curve secp256r1
682
- // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/lib.rs#L72
683
- const n = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551');
684
- const halfN = n / 2n;
685
-
686
- if (sBigInt > halfN)
687
- sBigInt = n - sBigInt;
688
-
689
- // Convert back to buffers and ensure they are 32 bytes
690
- const rPadded = Buffer.from(rBigInt.toString(16).padStart(64, '0'), 'hex');
691
- const sLowS = Buffer.from(sBigInt.toString(16).padStart(64, '0'), 'hex');
692
-
693
- // Concatenate r and low-s
694
- const concatSignature = Buffer.concat([rPadded, sLowS]);
695
-
696
- return concatSignature;
697
- }
698
- }