passkey-kit 0.4.3 → 0.4.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passkey-kit",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "A helper library for creating and using passkey accounts on the Stellar blockchain.",
5
5
  "author": "Tyler van der Hoeven",
6
6
  "license": "MIT",
@@ -12,7 +12,6 @@
12
12
  "@stellar/stellar-sdk": "12.1.0",
13
13
  "base64url": "^3.0.1",
14
14
  "buffer": "^6.0.3",
15
- "cbor-x": "^1.5.9",
16
15
  "passkey-factory-sdk": "0.2.1",
17
16
  "passkey-kit-sdk": "0.2.1"
18
17
  },
package/src/base.ts CHANGED
@@ -2,8 +2,8 @@ import { SorobanRpc, xdr } from "@stellar/stellar-sdk"
2
2
  import base64url from "base64url"
3
3
 
4
4
  export class PasskeyBase {
5
- public rpc: SorobanRpc.Server
6
- public rpcUrl: string
5
+ public rpc: SorobanRpc.Server | undefined
6
+ public rpcUrl: string | undefined
7
7
  public launchtubeUrl: string | undefined
8
8
  public launchtubeJwt: string | undefined
9
9
  public mercuryUrl: string | undefined
@@ -12,9 +12,7 @@ export class PasskeyBase {
12
12
  public mercuryPassword: string | undefined
13
13
 
14
14
  constructor(options: {
15
- rpcUrl: string,
16
- networkPassphrase: string,
17
- factoryContractId: string,
15
+ rpcUrl?: string,
18
16
  launchtubeUrl?: string,
19
17
  launchtubeJwt?: string,
20
18
  mercuryUrl?: string,
@@ -53,8 +51,10 @@ export class PasskeyBase {
53
51
  if (!mercuryJwt && mercuryUrl && mercuryEmail && mercuryPassword)
54
52
  this.setMercuryJwt()
55
53
 
56
- this.rpcUrl = rpcUrl
57
- this.rpc = new SorobanRpc.Server(rpcUrl)
54
+ if (rpcUrl) {
55
+ this.rpcUrl = rpcUrl
56
+ this.rpc = new SorobanRpc.Server(rpcUrl)
57
+ }
58
58
  }
59
59
 
60
60
  public async setMercuryJwt() {
@@ -89,7 +89,7 @@ export class PasskeyBase {
89
89
  }
90
90
 
91
91
  public async getSigners(contractId: string) {
92
- if (!this.mercuryUrl || !this.mercuryJwt)
92
+ if (!this.rpc || !this.mercuryUrl || !this.mercuryJwt)
93
93
  throw new Error('Mercury service not configured')
94
94
 
95
95
  const signers = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
package/src/kit.ts CHANGED
@@ -4,7 +4,6 @@ import { Address, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, Transac
4
4
  import base64url from 'base64url'
5
5
  import { startRegistration, startAuthentication } from "@simplewebauthn/browser"
6
6
  import type { AuthenticatorAttestationResponseJSON } from "@simplewebauthn/types"
7
- import { decode } from 'cbor-x/decode'
8
7
  import { Buffer } from 'buffer'
9
8
  import { PasskeyBase } from './base'
10
9
 
@@ -29,6 +28,12 @@ export class PasskeyKit extends PasskeyBase {
29
28
 
30
29
  super(options)
31
30
 
31
+ if (!this.rpcUrl)
32
+ this.rpcUrl = rpcUrl
33
+
34
+ if (!this.rpc)
35
+ this.rpc = new SorobanRpc.Server(rpcUrl)
36
+
32
37
  this.networkPassphrase = networkPassphrase
33
38
  this.factory = new FactoryClient({
34
39
  contractId: factoryContractId,
@@ -82,7 +87,7 @@ export class PasskeyKit extends PasskeyBase {
82
87
  },
83
88
  pubKeyCredParams: [{ alg: -7, type: "public-key" }],
84
89
  attestation: "none",
85
- timeout: 120_000
90
+ timeout: 120_000,
86
91
  });
87
92
 
88
93
  if (!this.keyId)
@@ -90,7 +95,7 @@ export class PasskeyKit extends PasskeyBase {
90
95
 
91
96
  return {
92
97
  keyId: base64url.toBuffer(id),
93
- publicKey: this.getPublicKey(response)
98
+ publicKey: await this.getPublicKey(response)
94
99
  }
95
100
  }
96
101
 
@@ -109,7 +114,7 @@ export class PasskeyKit extends PasskeyBase {
109
114
  timeout: 120_000
110
115
  });
111
116
 
112
- console.log(response);
117
+ // console.log(response);
113
118
 
114
119
  keyId = response.id
115
120
  }
@@ -140,7 +145,7 @@ export class PasskeyKit extends PasskeyBase {
140
145
  // attempt passkey id derivation
141
146
  try {
142
147
  // TODO what is the error if the entry exists but is archived?
143
- await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
148
+ await this.rpc!.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
144
149
  }
145
150
  // if that fails look up from the `getContractId` function
146
151
  catch {
@@ -172,7 +177,7 @@ export class PasskeyKit extends PasskeyBase {
172
177
  // Default mirrors DEFAULT_TIMEOUT (currently 5 minutes) https://github.com/stellar/js-stellar-sdk/blob/master/src/contract/utils.ts#L7
173
178
  let { keyId, ledgersToLive = 60 } = options || {}
174
179
 
175
- const lastLedger = await this.rpc.getLatestLedger().then(({ sequence }) => sequence)
180
+ const lastLedger = await this.rpc!.getLatestLedger().then(({ sequence }) => sequence)
176
181
  const credentials = entry.credentials().address();
177
182
  const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
178
183
  new xdr.HashIdPreimageSorobanAuthorization({
@@ -295,7 +300,7 @@ export class PasskeyKit extends PasskeyBase {
295
300
  await this.signAuthEntries(entries, options)
296
301
  }
297
302
 
298
- const sim = await this.rpc.simulateTransaction(txn)
303
+ const sim = await this.rpc!.simulateTransaction(txn)
299
304
 
300
305
  if (
301
306
  SorobanRpc.Api.isSimulationError(sim)
@@ -311,30 +316,51 @@ export class PasskeyKit extends PasskeyBase {
311
316
  @Later
312
317
  */
313
318
 
314
- private getPublicKey(response: AuthenticatorAttestationResponseJSON) {
319
+ private async getPublicKey(response: AuthenticatorAttestationResponseJSON) {
315
320
  let publicKey: Buffer | undefined
316
321
 
317
- if (response.publicKey)
318
- publicKey = base64url.toBuffer(response.publicKey).slice(response.publicKey.length - 65)
322
+ if (response.publicKey) {
323
+ publicKey = base64url.toBuffer(response.publicKey)
324
+ publicKey = publicKey!.slice(publicKey!.length - 65)
325
+ }
319
326
 
320
327
  if (
321
328
  !publicKey
322
329
  || publicKey[0] !== 0x04
323
330
  || publicKey.length !== 65
324
331
  ) {
325
- // Extract and decode attestation object (CBOR), slice authenticator data to get COSE-encoded public key, and decode COSE to obtain key components
326
- const { authData } = response.authenticatorData ? { authData: base64url.toBuffer(response.authenticatorData) } : decode(base64url.toBuffer(response.attestationObject));
327
- const credentialIdLength = (authData[53] << 8) + authData[54];
328
- const publicKeyBytes = authData.slice(55 + credentialIdLength);
329
- const publicKeyCose = decode(publicKeyBytes);
332
+ let x: Buffer
333
+ let y: Buffer
334
+
335
+ if (response.authenticatorData) {
336
+ const authenticatorData = base64url.toBuffer(response.authenticatorData)
337
+ const credentialIdLength = (authenticatorData[53] << 8) | authenticatorData[54]
338
+
339
+ x = authenticatorData.slice(65 + credentialIdLength, 97 + credentialIdLength)
340
+ y = authenticatorData.slice(100 + credentialIdLength, 132 + credentialIdLength)
341
+ } else {
342
+ const attestationData = base64url.toBuffer(response.attestationObject)
343
+
344
+ let startIndex = attestationData.indexOf(0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20)
345
+ startIndex = startIndex + startIndex.length
346
+
347
+ x = attestationData.slice(startIndex, 32 + startIndex)
348
+ y = attestationData.slice(35 + startIndex, 67 + startIndex)
349
+ }
330
350
 
331
351
  publicKey = Buffer.from([
332
352
  0x04, // (0x04 prefix) https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
333
- ...publicKeyCose['-2'],
334
- ...publicKeyCose['-3']
353
+ ...x,
354
+ ...y
335
355
  ])
336
356
  }
337
357
 
358
+ /* TODO
359
+ - 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
360
+ Given that `startRegistration` doesn't produce a signature, verifying we've got the correct public key isn't really possible
361
+ @Later
362
+ */
363
+
338
364
  return publicKey
339
365
  }
340
366
 
package/types/base.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SorobanRpc } from "@stellar/stellar-sdk";
2
2
  export declare class PasskeyBase {
3
- rpc: SorobanRpc.Server;
4
- rpcUrl: string;
3
+ rpc: SorobanRpc.Server | undefined;
4
+ rpcUrl: string | undefined;
5
5
  launchtubeUrl: string | undefined;
6
6
  launchtubeJwt: string | undefined;
7
7
  mercuryUrl: string | undefined;
@@ -9,9 +9,7 @@ export declare class PasskeyBase {
9
9
  mercuryEmail: string | undefined;
10
10
  mercuryPassword: string | undefined;
11
11
  constructor(options: {
12
- rpcUrl: string;
13
- networkPassphrase: string;
14
- factoryContractId: string;
12
+ rpcUrl?: string;
15
13
  launchtubeUrl?: string;
16
14
  launchtubeJwt?: string;
17
15
  mercuryUrl?: string;