@zerodev/wallet-core 0.0.1-alpha.17 → 0.0.1-alpha.19

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 (155) hide show
  1. package/dist/_cjs/actions/auth/getOAuthLoginUrl.js +18 -0
  2. package/dist/_cjs/actions/auth/getOAuthLoginUrl.js.map +1 -0
  3. package/dist/_cjs/actions/auth/getWhoami.js +2 -2
  4. package/dist/_cjs/actions/auth/getWhoami.js.map +1 -1
  5. package/dist/_cjs/actions/auth/index.js +3 -1
  6. package/dist/_cjs/actions/auth/index.js.map +1 -1
  7. package/dist/_cjs/actions/auth/loginWithStamp.js +5 -5
  8. package/dist/_cjs/actions/auth/loginWithStamp.js.map +1 -1
  9. package/dist/_cjs/actions/auth/registerWithOTP.js.map +1 -1
  10. package/dist/_cjs/actions/index.js +2 -1
  11. package/dist/_cjs/actions/index.js.map +1 -1
  12. package/dist/_cjs/actions/wallet/signingUtils.js +2 -2
  13. package/dist/_cjs/actions/wallet/signingUtils.js.map +1 -1
  14. package/dist/_cjs/client/authProxy.js +1 -1
  15. package/dist/_cjs/client/authProxy.js.map +1 -1
  16. package/dist/_cjs/client/createClient.js +5 -5
  17. package/dist/_cjs/client/createClient.js.map +1 -1
  18. package/dist/_cjs/client/decorators/client.js +1 -0
  19. package/dist/_cjs/client/decorators/client.js.map +1 -1
  20. package/dist/_cjs/client/transports/createTransport.js +5 -5
  21. package/dist/_cjs/client/transports/createTransport.js.map +1 -1
  22. package/dist/_cjs/client/transports/rest.js +5 -5
  23. package/dist/_cjs/client/transports/rest.js.map +1 -1
  24. package/dist/_cjs/constants.js +2 -1
  25. package/dist/_cjs/constants.js.map +1 -1
  26. package/dist/_cjs/core/createZeroDevWallet.js +38 -64
  27. package/dist/_cjs/core/createZeroDevWallet.js.map +1 -1
  28. package/dist/_cjs/index.js.map +1 -1
  29. package/dist/_cjs/stampers/indexedDbStamper.js +17 -2
  30. package/dist/_cjs/stampers/indexedDbStamper.js.map +1 -1
  31. package/dist/_cjs/stampers/webauthnStamper.js +23 -3
  32. package/dist/_cjs/stampers/webauthnStamper.js.map +1 -1
  33. package/dist/_cjs/utils/encryptOtpAttempt.js +57 -0
  34. package/dist/_cjs/utils/encryptOtpAttempt.js.map +1 -0
  35. package/dist/_cjs/utils/exportPrivateKey.js +1 -1
  36. package/dist/_cjs/utils/exportPrivateKey.js.map +1 -1
  37. package/dist/_cjs/utils/exportWallet.js +2 -6
  38. package/dist/_cjs/utils/exportWallet.js.map +1 -1
  39. package/dist/_cjs/utils/hpke.js +78 -0
  40. package/dist/_cjs/utils/hpke.js.map +1 -0
  41. package/dist/_cjs/utils/utils.js +5 -6
  42. package/dist/_cjs/utils/utils.js.map +1 -1
  43. package/dist/_esm/actions/auth/getOAuthLoginUrl.js +23 -0
  44. package/dist/_esm/actions/auth/getOAuthLoginUrl.js.map +1 -0
  45. package/dist/_esm/actions/auth/getWhoami.js +2 -2
  46. package/dist/_esm/actions/auth/getWhoami.js.map +1 -1
  47. package/dist/_esm/actions/auth/index.js +1 -0
  48. package/dist/_esm/actions/auth/index.js.map +1 -1
  49. package/dist/_esm/actions/auth/loginWithStamp.js +5 -5
  50. package/dist/_esm/actions/auth/loginWithStamp.js.map +1 -1
  51. package/dist/_esm/actions/auth/registerWithOTP.js.map +1 -1
  52. package/dist/_esm/actions/index.js +1 -1
  53. package/dist/_esm/actions/index.js.map +1 -1
  54. package/dist/_esm/actions/wallet/signingUtils.js +2 -2
  55. package/dist/_esm/actions/wallet/signingUtils.js.map +1 -1
  56. package/dist/_esm/client/authProxy.js +9 -4
  57. package/dist/_esm/client/authProxy.js.map +1 -1
  58. package/dist/_esm/client/createClient.js +5 -5
  59. package/dist/_esm/client/createClient.js.map +1 -1
  60. package/dist/_esm/client/decorators/client.js +2 -1
  61. package/dist/_esm/client/decorators/client.js.map +1 -1
  62. package/dist/_esm/client/transports/createTransport.js +5 -5
  63. package/dist/_esm/client/transports/createTransport.js.map +1 -1
  64. package/dist/_esm/client/transports/rest.js +5 -5
  65. package/dist/_esm/client/transports/rest.js.map +1 -1
  66. package/dist/_esm/constants.js +6 -0
  67. package/dist/_esm/constants.js.map +1 -1
  68. package/dist/_esm/core/createZeroDevWallet.js +42 -66
  69. package/dist/_esm/core/createZeroDevWallet.js.map +1 -1
  70. package/dist/_esm/index.js.map +1 -1
  71. package/dist/_esm/stampers/indexedDbStamper.js +17 -2
  72. package/dist/_esm/stampers/indexedDbStamper.js.map +1 -1
  73. package/dist/_esm/stampers/webauthnStamper.js +23 -4
  74. package/dist/_esm/stampers/webauthnStamper.js.map +1 -1
  75. package/dist/_esm/utils/encryptOtpAttempt.js +81 -0
  76. package/dist/_esm/utils/encryptOtpAttempt.js.map +1 -0
  77. package/dist/_esm/utils/exportPrivateKey.js +1 -1
  78. package/dist/_esm/utils/exportPrivateKey.js.map +1 -1
  79. package/dist/_esm/utils/exportWallet.js +2 -6
  80. package/dist/_esm/utils/exportWallet.js.map +1 -1
  81. package/dist/_esm/utils/hpke.js +119 -0
  82. package/dist/_esm/utils/hpke.js.map +1 -0
  83. package/dist/_esm/utils/utils.js +5 -6
  84. package/dist/_esm/utils/utils.js.map +1 -1
  85. package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts +30 -0
  86. package/dist/_types/actions/auth/getOAuthLoginUrl.d.ts.map +1 -0
  87. package/dist/_types/actions/auth/index.d.ts +1 -0
  88. package/dist/_types/actions/auth/index.d.ts.map +1 -1
  89. package/dist/_types/actions/auth/loginWithStamp.d.ts +2 -1
  90. package/dist/_types/actions/auth/loginWithStamp.d.ts.map +1 -1
  91. package/dist/_types/actions/auth/registerWithOTP.d.ts +6 -0
  92. package/dist/_types/actions/auth/registerWithOTP.d.ts.map +1 -1
  93. package/dist/_types/actions/index.d.ts +1 -1
  94. package/dist/_types/actions/index.d.ts.map +1 -1
  95. package/dist/_types/client/authProxy.d.ts +13 -7
  96. package/dist/_types/client/authProxy.d.ts.map +1 -1
  97. package/dist/_types/client/decorators/client.d.ts +7 -1
  98. package/dist/_types/client/decorators/client.d.ts.map +1 -1
  99. package/dist/_types/client/transports/rest.d.ts +5 -4
  100. package/dist/_types/client/transports/rest.d.ts.map +1 -1
  101. package/dist/_types/client/types.d.ts +9 -9
  102. package/dist/_types/client/types.d.ts.map +1 -1
  103. package/dist/_types/constants.d.ts +1 -0
  104. package/dist/_types/constants.d.ts.map +1 -1
  105. package/dist/_types/core/createZeroDevWallet.d.ts +13 -0
  106. package/dist/_types/core/createZeroDevWallet.d.ts.map +1 -1
  107. package/dist/_types/index.d.ts +1 -1
  108. package/dist/_types/index.d.ts.map +1 -1
  109. package/dist/_types/stampers/index.d.ts +1 -1
  110. package/dist/_types/stampers/index.d.ts.map +1 -1
  111. package/dist/_types/stampers/indexedDbStamper.d.ts +2 -2
  112. package/dist/_types/stampers/indexedDbStamper.d.ts.map +1 -1
  113. package/dist/_types/stampers/types.d.ts +31 -5
  114. package/dist/_types/stampers/types.d.ts.map +1 -1
  115. package/dist/_types/stampers/webauthnStamper.d.ts +2 -2
  116. package/dist/_types/stampers/webauthnStamper.d.ts.map +1 -1
  117. package/dist/_types/types/session.d.ts +2 -3
  118. package/dist/_types/types/session.d.ts.map +1 -1
  119. package/dist/_types/utils/buildClientSignature.d.ts +3 -3
  120. package/dist/_types/utils/buildClientSignature.d.ts.map +1 -1
  121. package/dist/_types/utils/encryptOtpAttempt.d.ts +40 -0
  122. package/dist/_types/utils/encryptOtpAttempt.d.ts.map +1 -0
  123. package/dist/_types/utils/exportWallet.d.ts.map +1 -1
  124. package/dist/_types/utils/hpke.d.ts +38 -0
  125. package/dist/_types/utils/hpke.d.ts.map +1 -0
  126. package/dist/_types/utils/utils.d.ts.map +1 -1
  127. package/dist/tsconfig.build.tsbuildinfo +1 -1
  128. package/package.json +5 -1
  129. package/src/actions/auth/getOAuthLoginUrl.ts +48 -0
  130. package/src/actions/auth/getWhoami.ts +2 -2
  131. package/src/actions/auth/index.ts +5 -0
  132. package/src/actions/auth/loginWithStamp.ts +7 -6
  133. package/src/actions/auth/registerWithOTP.ts +6 -0
  134. package/src/actions/index.ts +3 -0
  135. package/src/actions/wallet/signingUtils.ts +2 -2
  136. package/src/client/authProxy.ts +14 -8
  137. package/src/client/createClient.ts +6 -6
  138. package/src/client/decorators/client.ts +13 -0
  139. package/src/client/transports/createTransport.ts +5 -5
  140. package/src/client/transports/rest.ts +11 -10
  141. package/src/client/types.ts +9 -9
  142. package/src/constants.ts +8 -0
  143. package/src/core/createZeroDevWallet.ts +58 -81
  144. package/src/index.ts +5 -2
  145. package/src/stampers/index.ts +2 -2
  146. package/src/stampers/indexedDbStamper.ts +24 -4
  147. package/src/stampers/types.ts +33 -5
  148. package/src/stampers/webauthnStamper.ts +27 -6
  149. package/src/types/session.ts +2 -3
  150. package/src/utils/buildClientSignature.ts +3 -3
  151. package/src/utils/encryptOtpAttempt.ts +142 -0
  152. package/src/utils/exportPrivateKey.ts +1 -1
  153. package/src/utils/exportWallet.ts +2 -6
  154. package/src/utils/hpke.ts +219 -0
  155. package/src/utils/utils.ts +5 -6
@@ -72,7 +72,7 @@ export async function exportPrivateKey(
72
72
  })
73
73
 
74
74
  const stamperKey =
75
- session.stamperType === 'indexedDb' ? 'indexedDbStamper' : 'webauthnStamper'
75
+ session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
76
76
  const stamper = wallet.client[stamperKey]
77
77
  if (!stamper) {
78
78
  throw new Error(`Stamper '${stamperKey}' not found on wallet.client`)
@@ -56,9 +56,7 @@ export async function exportWallet(
56
56
 
57
57
  const listWalletsStamp =
58
58
  await wallet.client[
59
- session.stamperType === 'indexedDb'
60
- ? 'indexedDbStamper'
61
- : 'webauthnStamper'
59
+ session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
62
60
  ].stamp(listWalletsBody)
63
61
  if (!listWalletsStamp) {
64
62
  throw new Error('Failed to stamp list wallets body')
@@ -93,9 +91,7 @@ export async function exportWallet(
93
91
  })
94
92
  const exportWalletStamp =
95
93
  await wallet.client[
96
- session.stamperType === 'indexedDb'
97
- ? 'indexedDbStamper'
98
- : 'webauthnStamper'
94
+ session.stamperType === 'apiKey' ? 'apiKeyStamper' : 'passkeyStamper'
99
95
  ].stamp(exportWalletBody)
100
96
  if (!exportWalletStamp) {
101
97
  throw new Error('Failed to stamp export wallet body')
@@ -0,0 +1,219 @@
1
+ /**
2
+ * HPKE (RFC 9180) seal for Turnkey enclave-encrypted requests.
3
+ *
4
+ * Suite: DHKEM(P-256, HKDF-SHA256) / HKDF-SHA256 / AES-256-GCM
5
+ * - KEM ID = 0x0010 (DHKEM-P256-HKDF-SHA256)
6
+ * - KDF ID = 0x0001 (HKDF-SHA256)
7
+ * - AEAD ID = 0x0002 (AES-256-GCM)
8
+ *
9
+ * Wire format and AAD construction match Turnkey's enclave_encrypt Go package:
10
+ * info = "turnkey_hpke"
11
+ * aad = enc || pkR (both 65-byte uncompressed P-256 points)
12
+ *
13
+ * References:
14
+ * - RFC 9180 §4 / §5
15
+ * - tkhq/go-sdk/pkg/enclave_encrypt
16
+ */
17
+
18
+ import { gcm } from '@noble/ciphers/aes.js'
19
+ import { p256 } from '@noble/curves/nist.js'
20
+ import { expand, extract } from '@noble/hashes/hkdf.js'
21
+ import { sha256 } from '@noble/hashes/sha2.js'
22
+
23
+ const KEM_ID = 0x0010
24
+ const KDF_ID = 0x0001
25
+ const AEAD_ID = 0x0002
26
+
27
+ // Output sizes for the chosen primitives.
28
+ const NH = 32 // SHA-256 output
29
+ const NK = 32 // AES-256 key
30
+ const NN = 12 // AES-GCM nonce
31
+ const NPK = 65 // uncompressed P-256 point: 0x04 || X || Y
32
+
33
+ const TURNKEY_HPKE_INFO = new TextEncoder().encode('turnkey_hpke')
34
+
35
+ const HPKE_VERSION = new TextEncoder().encode('HPKE-v1')
36
+
37
+ // suite_id for the HPKE context: "HPKE" || I2OSP(KEM,2) || I2OSP(KDF,2) || I2OSP(AEAD,2)
38
+ const HPKE_SUITE_ID = concat(
39
+ new TextEncoder().encode('HPKE'),
40
+ i2osp(KEM_ID, 2),
41
+ i2osp(KDF_ID, 2),
42
+ i2osp(AEAD_ID, 2),
43
+ )
44
+
45
+ // suite_id for the KEM scope: "KEM" || I2OSP(KEM,2)
46
+ const KEM_SUITE_ID = concat(new TextEncoder().encode('KEM'), i2osp(KEM_ID, 2))
47
+
48
+ function concat(...parts: Uint8Array[]): Uint8Array {
49
+ const total = parts.reduce((sum, p) => sum + p.length, 0)
50
+ const out = new Uint8Array(total)
51
+ let offset = 0
52
+ for (const p of parts) {
53
+ out.set(p, offset)
54
+ offset += p.length
55
+ }
56
+ return out
57
+ }
58
+
59
+ function i2osp(n: number, len: number): Uint8Array {
60
+ const out = new Uint8Array(len)
61
+ for (let i = len - 1; i >= 0; i--) {
62
+ out[i] = n & 0xff
63
+ n >>>= 8
64
+ }
65
+ return out
66
+ }
67
+
68
+ // LabeledExtract(salt, label, ikm, suite_id) =
69
+ // HKDF-Extract(salt, "HPKE-v1" || suite_id || label || ikm)
70
+ function labeledExtract(
71
+ salt: Uint8Array,
72
+ label: string,
73
+ ikm: Uint8Array,
74
+ suiteId: Uint8Array,
75
+ ): Uint8Array {
76
+ const labeledIkm = concat(
77
+ HPKE_VERSION,
78
+ suiteId,
79
+ new TextEncoder().encode(label),
80
+ ikm,
81
+ )
82
+ return extract(sha256, labeledIkm, salt)
83
+ }
84
+
85
+ // LabeledExpand(prk, label, info, L, suite_id) =
86
+ // HKDF-Expand(prk, I2OSP(L,2) || "HPKE-v1" || suite_id || label || info, L)
87
+ function labeledExpand(
88
+ prk: Uint8Array,
89
+ label: string,
90
+ info: Uint8Array,
91
+ length: number,
92
+ suiteId: Uint8Array,
93
+ ): Uint8Array {
94
+ const labeledInfo = concat(
95
+ i2osp(length, 2),
96
+ HPKE_VERSION,
97
+ suiteId,
98
+ new TextEncoder().encode(label),
99
+ info,
100
+ )
101
+ return expand(sha256, prk, labeledInfo, length)
102
+ }
103
+
104
+ // DHKEM Encap: returns (sharedSecret, enc)
105
+ // sharedSecret is 32 bytes; enc is the serialized ephemeral pubkey (65 bytes uncompressed).
106
+ function encap(receiverPublicKey: Uint8Array): {
107
+ sharedSecret: Uint8Array
108
+ enc: Uint8Array
109
+ } {
110
+ const ephSk = p256.utils.randomSecretKey()
111
+ const ephPkUncompressed = p256.getPublicKey(ephSk, false)
112
+
113
+ // ECDH: returns the serialized shared point. Pass isCompressed=true so the
114
+ // first byte is the SEC1 prefix and bytes [1, 33) are the x-coordinate.
115
+ const sharedPoint = p256.getSharedSecret(
116
+ ephSk,
117
+ receiverPublicKey,
118
+ /* isCompressed */ true,
119
+ )
120
+ const dh = sharedPoint.slice(1, 33)
121
+
122
+ const kemContext = concat(ephPkUncompressed, receiverPublicKey)
123
+
124
+ const eaePrk = labeledExtract(new Uint8Array(0), 'eae_prk', dh, KEM_SUITE_ID)
125
+ const sharedSecret = labeledExpand(
126
+ eaePrk,
127
+ 'shared_secret',
128
+ kemContext,
129
+ NH,
130
+ KEM_SUITE_ID,
131
+ )
132
+
133
+ return { sharedSecret, enc: ephPkUncompressed }
134
+ }
135
+
136
+ // KeySchedule for mode_base: returns (key, base_nonce).
137
+ function keySchedule(
138
+ sharedSecret: Uint8Array,
139
+ info: Uint8Array,
140
+ ): { key: Uint8Array; baseNonce: Uint8Array } {
141
+ const empty = new Uint8Array(0)
142
+
143
+ const pskIdHash = labeledExtract(empty, 'psk_id_hash', empty, HPKE_SUITE_ID)
144
+ const infoHash = labeledExtract(empty, 'info_hash', info, HPKE_SUITE_ID)
145
+
146
+ // mode_base = 0x00 prepended to (psk_id_hash || info_hash)
147
+ const keyScheduleContext = concat(new Uint8Array([0]), pskIdHash, infoHash)
148
+
149
+ const secret = labeledExtract(sharedSecret, 'secret', empty, HPKE_SUITE_ID)
150
+
151
+ const key = labeledExpand(
152
+ secret,
153
+ 'key',
154
+ keyScheduleContext,
155
+ NK,
156
+ HPKE_SUITE_ID,
157
+ )
158
+ const baseNonce = labeledExpand(
159
+ secret,
160
+ 'base_nonce',
161
+ keyScheduleContext,
162
+ NN,
163
+ HPKE_SUITE_ID,
164
+ )
165
+
166
+ return { key, baseNonce }
167
+ }
168
+
169
+ function aesGcmSeal(
170
+ key: Uint8Array,
171
+ nonce: Uint8Array,
172
+ aad: Uint8Array,
173
+ plaintext: Uint8Array,
174
+ ): Uint8Array {
175
+ // Returns ciphertext || tag (16 bytes appended) — matches the single-blob
176
+ // format Turnkey's `Sealer.Seal` and Web Crypto's AES-GCM produce.
177
+ return gcm(key, nonce, aad).encrypt(plaintext)
178
+ }
179
+
180
+ export type HpkeSealResult = {
181
+ /** Ephemeral sender public key (uncompressed P-256, 65 bytes). */
182
+ encappedPublic: Uint8Array
183
+ /** AES-256-GCM ciphertext with a 16-byte authentication tag appended. */
184
+ ciphertext: Uint8Array
185
+ }
186
+
187
+ /**
188
+ * Single-shot HPKE seal in mode_base for Turnkey's TLS Fetcher enclave.
189
+ *
190
+ * Uses the fixed Turnkey `info = "turnkey_hpke"` and the AAD shape
191
+ * `enc || receiverPublicKey` so the resulting bundle is decryptable by
192
+ * `enclave_encrypt.EnclaveEncryptServer.Decrypt`.
193
+ *
194
+ * @param receiverPublicKey - The enclave's ephemeral target public key
195
+ * (uncompressed P-256, 65 bytes), extracted from the encryption target bundle.
196
+ * @param plaintext - The bytes to encrypt (e.g. the JSON-encoded OTP attempt).
197
+ */
198
+ export async function hpkeSealP256({
199
+ receiverPublicKey,
200
+ plaintext,
201
+ }: {
202
+ receiverPublicKey: Uint8Array
203
+ plaintext: Uint8Array
204
+ }): Promise<HpkeSealResult> {
205
+ if (receiverPublicKey.length !== NPK) {
206
+ throw new Error(
207
+ `hpkeSealP256: receiverPublicKey must be ${NPK} bytes (uncompressed P-256), got ${receiverPublicKey.length}`,
208
+ )
209
+ }
210
+
211
+ const { sharedSecret, enc } = encap(receiverPublicKey)
212
+ const { key, baseNonce } = keySchedule(sharedSecret, TURNKEY_HPKE_INFO)
213
+
214
+ // First message of the context, sequence 0 → nonce = base_nonce.
215
+ const aad = concat(enc, receiverPublicKey)
216
+ const ciphertext = aesGcmSeal(key, baseNonce, aad, plaintext)
217
+
218
+ return { encappedPublic: enc, ciphertext }
219
+ }
@@ -18,7 +18,8 @@ export function parseSession(
18
18
  throw new Error('Invalid JWT: Missing payload')
19
19
  }
20
20
 
21
- const decoded = JSON.parse(Buffer.from(payload, 'base64').toString())
21
+ const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')
22
+ const decoded = JSON.parse(atob(base64))
22
23
  const {
23
24
  exp,
24
25
  public_key: publicKey,
@@ -68,11 +69,9 @@ export const generateRandomBuffer = (): ArrayBuffer => {
68
69
  * @returns {string} - The encoded challenge.
69
70
  */
70
71
  export const base64UrlEncode = (challenge: ArrayBuffer): string => {
71
- return Buffer.from(challenge)
72
- .toString('base64')
73
- .replace(/\+/g, '-')
74
- .replace(/\//g, '_')
75
- .replace(/=/g, '')
72
+ const bytes = new Uint8Array(challenge)
73
+ const binary = String.fromCharCode(...bytes)
74
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
76
75
  }
77
76
 
78
77
  /**