@0xsequence/wallet-primitives 0.0.0-20250520201059
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/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +202 -0
- package/dist/address.d.ts +5 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +7 -0
- package/dist/address.js.map +1 -0
- package/dist/attestation.d.ts +24 -0
- package/dist/attestation.d.ts.map +1 -0
- package/dist/attestation.js +77 -0
- package/dist/attestation.js.map +1 -0
- package/dist/config.d.ts +85 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +381 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +173 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +31 -0
- package/dist/constants.js.map +1 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +8 -0
- package/dist/context.js.map +1 -0
- package/dist/erc-6492.d.ts +19 -0
- package/dist/erc-6492.d.ts.map +1 -0
- package/dist/erc-6492.js +64 -0
- package/dist/erc-6492.js.map +1 -0
- package/dist/extensions/index.d.ts +9 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +7 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/passkeys.d.ts +31 -0
- package/dist/extensions/passkeys.d.ts.map +1 -0
- package/dist/extensions/passkeys.js +224 -0
- package/dist/extensions/passkeys.js.map +1 -0
- package/dist/extensions/recovery.d.ts +310 -0
- package/dist/extensions/recovery.d.ts.map +1 -0
- package/dist/extensions/recovery.js +444 -0
- package/dist/extensions/recovery.js.map +1 -0
- package/dist/generic-tree.d.ts +14 -0
- package/dist/generic-tree.d.ts.map +1 -0
- package/dist/generic-tree.js +34 -0
- package/dist/generic-tree.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +15 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +24 -0
- package/dist/network.js.map +1 -0
- package/dist/payload.d.ts +108 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +627 -0
- package/dist/payload.js.map +1 -0
- package/dist/permission.d.ts +73 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +188 -0
- package/dist/permission.js.map +1 -0
- package/dist/session-config.d.ts +113 -0
- package/dist/session-config.d.ts.map +1 -0
- package/dist/session-config.js +554 -0
- package/dist/session-config.js.map +1 -0
- package/dist/session-signature.d.ts +24 -0
- package/dist/session-signature.d.ts.map +1 -0
- package/dist/session-signature.js +141 -0
- package/dist/session-signature.js.map +1 -0
- package/dist/signature.d.ts +108 -0
- package/dist/signature.d.ts.map +1 -0
- package/dist/signature.js +1079 -0
- package/dist/signature.js.map +1 -0
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +100 -0
- package/dist/utils.js.map +1 -0
- package/eslint.config.mjs +4 -0
- package/package.json +27 -0
- package/src/address.ts +19 -0
- package/src/attestation.ts +114 -0
- package/src/config.ts +521 -0
- package/src/constants.ts +39 -0
- package/src/context.ts +16 -0
- package/src/erc-6492.ts +97 -0
- package/src/extensions/index.ts +14 -0
- package/src/extensions/passkeys.ts +283 -0
- package/src/extensions/recovery.ts +542 -0
- package/src/generic-tree.ts +55 -0
- package/src/index.ts +15 -0
- package/src/network.ts +37 -0
- package/src/payload.ts +825 -0
- package/src/permission.ts +252 -0
- package/src/session-config.ts +681 -0
- package/src/session-signature.ts +197 -0
- package/src/signature.ts +1398 -0
- package/src/utils.ts +114 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Address } from 'ox'
|
|
2
|
+
|
|
3
|
+
export type Extensions = {
|
|
4
|
+
passkeys: Address.Address
|
|
5
|
+
recovery: Address.Address
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Dev1: Extensions = {
|
|
9
|
+
passkeys: '0x8f26281dB84C18aAeEa8a53F94c835393229d296',
|
|
10
|
+
recovery: '0xd98da48C4FF9c19742eA5856A277424557C863a6',
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export * as Passkeys from './passkeys.js'
|
|
14
|
+
export * as Recovery from './recovery.js'
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { Bytes, Hex, WebAuthnP256 } from 'ox'
|
|
2
|
+
import * as GenericTree from '../generic-tree.js'
|
|
3
|
+
|
|
4
|
+
export type PasskeyMetadata = {
|
|
5
|
+
credentialId: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type PublicKey = {
|
|
9
|
+
requireUserVerification: boolean
|
|
10
|
+
x: Hex.Hex
|
|
11
|
+
y: Hex.Hex
|
|
12
|
+
metadata?: PasskeyMetadata | Hex.Hex
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function metadataTree(metadata: Required<PublicKey>['metadata']): GenericTree.Tree {
|
|
16
|
+
if (typeof metadata === 'object') {
|
|
17
|
+
return {
|
|
18
|
+
type: 'leaf',
|
|
19
|
+
value: Bytes.fromString(metadata.credentialId),
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
return metadata
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function metadataNode(metadata: Required<PublicKey>['metadata']): GenericTree.Node {
|
|
27
|
+
return GenericTree.hash(metadataTree(metadata))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function toTree(publicKey: PublicKey): GenericTree.Tree {
|
|
31
|
+
const a = Hex.padLeft(publicKey.x, 32)
|
|
32
|
+
const b = Hex.padLeft(publicKey.y, 32)
|
|
33
|
+
const c = Hex.padLeft(publicKey.requireUserVerification ? '0x01' : '0x00', 32)
|
|
34
|
+
|
|
35
|
+
if (publicKey.metadata) {
|
|
36
|
+
return [
|
|
37
|
+
[a, b],
|
|
38
|
+
[c, metadataTree(publicKey.metadata)],
|
|
39
|
+
]
|
|
40
|
+
} else {
|
|
41
|
+
return [
|
|
42
|
+
[a, b],
|
|
43
|
+
[c, Hex.padLeft('0x00', 32)],
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function fromTree(tree: GenericTree.Tree): PublicKey {
|
|
49
|
+
if (!GenericTree.isBranch(tree) || tree.length !== 2) {
|
|
50
|
+
throw new Error('Invalid tree')
|
|
51
|
+
}
|
|
52
|
+
const [p1, p2] = tree
|
|
53
|
+
if (!GenericTree.isBranch(p1) || p1.length !== 2) {
|
|
54
|
+
throw new Error('Invalid tree for x,y')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const [x, y] = p1
|
|
58
|
+
if (!GenericTree.isNode(x)) {
|
|
59
|
+
throw new Error('Invalid x bytes')
|
|
60
|
+
}
|
|
61
|
+
if (!GenericTree.isNode(y)) {
|
|
62
|
+
throw new Error('Invalid y bytes')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let requireUserVerification = false
|
|
66
|
+
let metadata: PublicKey['metadata']
|
|
67
|
+
|
|
68
|
+
if (GenericTree.isBranch(p2)) {
|
|
69
|
+
if (p2.length !== 2) {
|
|
70
|
+
throw new Error('Invalid tree for c,metadata')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [c, meta] = p2
|
|
74
|
+
if (!GenericTree.isNode(c) || c.length !== 32) {
|
|
75
|
+
throw new Error('Invalid c bytes')
|
|
76
|
+
}
|
|
77
|
+
const cBytes = Hex.toBytes(c)
|
|
78
|
+
requireUserVerification = cBytes[31] === 1
|
|
79
|
+
|
|
80
|
+
if (GenericTree.isBranch(meta)) {
|
|
81
|
+
if (meta.length !== 2) {
|
|
82
|
+
throw new Error('Invalid metadata tree')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const [credLeaf, sub] = meta
|
|
86
|
+
if (!GenericTree.isLeaf(credLeaf)) {
|
|
87
|
+
throw new Error('Invalid credentialId leaf')
|
|
88
|
+
}
|
|
89
|
+
const credentialId = new TextDecoder().decode(credLeaf.value)
|
|
90
|
+
|
|
91
|
+
if (!GenericTree.isBranch(sub) || sub.length !== 2) {
|
|
92
|
+
throw new Error('Invalid sub-branch for name and createdAt')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const [nameLeaf, createdAtLeaf] = sub
|
|
96
|
+
if (!GenericTree.isLeaf(nameLeaf) || !GenericTree.isLeaf(createdAtLeaf)) {
|
|
97
|
+
throw new Error('Invalid metadata leaves')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
metadata = { credentialId }
|
|
101
|
+
} else if (GenericTree.isNode(meta) && meta.length === 32) {
|
|
102
|
+
metadata = meta
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error('Invalid metadata node')
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
if (!GenericTree.isNode(p2) || p2.length !== 32) {
|
|
108
|
+
throw new Error('Invalid c bytes')
|
|
109
|
+
}
|
|
110
|
+
const p2Bytes = Hex.toBytes(p2)
|
|
111
|
+
requireUserVerification = p2Bytes[31] === 1
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { requireUserVerification, x, y, metadata }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function rootFor(publicKey: PublicKey): Hex.Hex {
|
|
118
|
+
return GenericTree.hash(toTree(publicKey))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type DecodedSignature = {
|
|
122
|
+
publicKey: PublicKey
|
|
123
|
+
r: Bytes.Bytes
|
|
124
|
+
s: Bytes.Bytes
|
|
125
|
+
authenticatorData: Bytes.Bytes
|
|
126
|
+
clientDataJSON: string
|
|
127
|
+
embedMetadata?: boolean
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function encode(decoded: DecodedSignature): Bytes.Bytes {
|
|
131
|
+
const challengeIndex = decoded.clientDataJSON.indexOf('"challenge"')
|
|
132
|
+
const typeIndex = decoded.clientDataJSON.indexOf('"type"')
|
|
133
|
+
|
|
134
|
+
const authDataSize = decoded.authenticatorData.length
|
|
135
|
+
const clientDataJSONSize = decoded.clientDataJSON.length
|
|
136
|
+
|
|
137
|
+
if (authDataSize > 65535) {
|
|
138
|
+
throw new Error('Authenticator data size is too large')
|
|
139
|
+
}
|
|
140
|
+
if (clientDataJSONSize > 65535) {
|
|
141
|
+
throw new Error('Client data JSON size is too large')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const bytesAuthDataSize = authDataSize <= 255 ? 1 : 2
|
|
145
|
+
const bytesClientDataJSONSize = clientDataJSONSize <= 255 ? 1 : 2
|
|
146
|
+
const bytesChallengeIndex = challengeIndex <= 255 ? 1 : 2
|
|
147
|
+
const bytesTypeIndex = typeIndex <= 255 ? 1 : 2
|
|
148
|
+
|
|
149
|
+
let flags = 0
|
|
150
|
+
|
|
151
|
+
flags |= decoded.publicKey.requireUserVerification ? 1 : 0 // 0x01 bit
|
|
152
|
+
flags |= (bytesAuthDataSize - 1) << 1 // 0x02 bit
|
|
153
|
+
flags |= (bytesClientDataJSONSize - 1) << 2 // 0x04 bit
|
|
154
|
+
flags |= (bytesChallengeIndex - 1) << 3 // 0x08 bit
|
|
155
|
+
flags |= (bytesTypeIndex - 1) << 4 // 0x10 bit
|
|
156
|
+
|
|
157
|
+
// Set metadata flag if metadata exists
|
|
158
|
+
if (decoded.embedMetadata) {
|
|
159
|
+
flags |= 1 << 6 // 0x40 bit
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let result: Bytes.Bytes = Bytes.from([flags])
|
|
163
|
+
|
|
164
|
+
// Add metadata if it exists
|
|
165
|
+
if (decoded.embedMetadata) {
|
|
166
|
+
if (!decoded.publicKey.metadata) {
|
|
167
|
+
throw new Error('Metadata is not present in the public key')
|
|
168
|
+
}
|
|
169
|
+
result = Bytes.concat(result, Hex.toBytes(metadataNode(decoded.publicKey.metadata)))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(authDataSize), bytesAuthDataSize))
|
|
173
|
+
result = Bytes.concat(result, decoded.authenticatorData)
|
|
174
|
+
|
|
175
|
+
result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(decoded.clientDataJSON.length), bytesClientDataJSONSize))
|
|
176
|
+
result = Bytes.concat(result, Bytes.from(new TextEncoder().encode(decoded.clientDataJSON)))
|
|
177
|
+
|
|
178
|
+
result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(challengeIndex), bytesChallengeIndex))
|
|
179
|
+
result = Bytes.concat(result, Bytes.padLeft(Bytes.fromNumber(typeIndex), bytesTypeIndex))
|
|
180
|
+
|
|
181
|
+
result = Bytes.concat(result, Bytes.padLeft(decoded.r, 32))
|
|
182
|
+
result = Bytes.concat(result, Bytes.padLeft(decoded.s, 32))
|
|
183
|
+
|
|
184
|
+
result = Bytes.concat(result, Bytes.fromHex(decoded.publicKey.x))
|
|
185
|
+
result = Bytes.concat(result, Bytes.fromHex(decoded.publicKey.y))
|
|
186
|
+
|
|
187
|
+
return result
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function isValidSignature(challenge: Hex.Hex, decoded: DecodedSignature): boolean {
|
|
191
|
+
return WebAuthnP256.verify({
|
|
192
|
+
challenge,
|
|
193
|
+
publicKey: {
|
|
194
|
+
x: Hex.toBigInt(decoded.publicKey.x),
|
|
195
|
+
y: Hex.toBigInt(decoded.publicKey.y),
|
|
196
|
+
prefix: 4,
|
|
197
|
+
},
|
|
198
|
+
metadata: {
|
|
199
|
+
authenticatorData: Hex.fromBytes(decoded.authenticatorData),
|
|
200
|
+
challengeIndex: decoded.clientDataJSON.indexOf('"challenge"'),
|
|
201
|
+
clientDataJSON: decoded.clientDataJSON,
|
|
202
|
+
typeIndex: decoded.clientDataJSON.indexOf('"type"'),
|
|
203
|
+
userVerificationRequired: decoded.publicKey.requireUserVerification,
|
|
204
|
+
},
|
|
205
|
+
signature: {
|
|
206
|
+
r: Bytes.toBigInt(decoded.r),
|
|
207
|
+
s: Bytes.toBigInt(decoded.s),
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function decode(data: Bytes.Bytes): Required<DecodedSignature> & { challengeIndex: number; typeIndex: number } {
|
|
213
|
+
let offset = 0
|
|
214
|
+
|
|
215
|
+
const flags = data[0]
|
|
216
|
+
offset += 1
|
|
217
|
+
|
|
218
|
+
if (flags === undefined) {
|
|
219
|
+
throw new Error('Invalid flags')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const requireUserVerification = (flags & 0x01) !== 0x00
|
|
223
|
+
const bytesAuthDataSize = ((flags >> 1) & 0x01) + 1
|
|
224
|
+
const bytesClientDataJSONSize = ((flags >> 2) & 0x01) + 1
|
|
225
|
+
const bytesChallengeIndex = ((flags >> 3) & 0x01) + 1
|
|
226
|
+
const bytesTypeIndex = ((flags >> 4) & 0x01) + 1
|
|
227
|
+
const hasMetadata = ((flags >> 6) & 0x01) === 0x01
|
|
228
|
+
|
|
229
|
+
// Check if fallback to abi decode is needed
|
|
230
|
+
if ((flags & 0x20) !== 0) {
|
|
231
|
+
throw new Error('Fallback to abi decode is not supported in this implementation')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let metadata: Hex.Hex | undefined
|
|
235
|
+
|
|
236
|
+
// Read metadata if present
|
|
237
|
+
if (hasMetadata) {
|
|
238
|
+
const metadataBytes = Bytes.slice(data, offset, offset + 32)
|
|
239
|
+
metadata = Hex.fromBytes(metadataBytes)
|
|
240
|
+
offset += 32
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const authDataSize = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesAuthDataSize))
|
|
244
|
+
offset += bytesAuthDataSize
|
|
245
|
+
const authenticatorData = Bytes.slice(data, offset, offset + authDataSize)
|
|
246
|
+
offset += authDataSize
|
|
247
|
+
|
|
248
|
+
const clientDataJSONSize = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesClientDataJSONSize))
|
|
249
|
+
offset += bytesClientDataJSONSize
|
|
250
|
+
const clientDataJSONBytes = Bytes.slice(data, offset, offset + clientDataJSONSize)
|
|
251
|
+
offset += clientDataJSONSize
|
|
252
|
+
const clientDataJSON = new TextDecoder().decode(clientDataJSONBytes)
|
|
253
|
+
|
|
254
|
+
const challengeIndex = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesChallengeIndex))
|
|
255
|
+
offset += bytesChallengeIndex
|
|
256
|
+
const typeIndex = Bytes.toNumber(Bytes.slice(data, offset, offset + bytesTypeIndex))
|
|
257
|
+
offset += bytesTypeIndex
|
|
258
|
+
|
|
259
|
+
const r = Bytes.slice(data, offset, offset + 32)
|
|
260
|
+
offset += 32
|
|
261
|
+
const s = Bytes.slice(data, offset, offset + 32)
|
|
262
|
+
offset += 32
|
|
263
|
+
|
|
264
|
+
const xBytes = Bytes.slice(data, offset, offset + 32)
|
|
265
|
+
offset += 32
|
|
266
|
+
const yBytes = Bytes.slice(data, offset, offset + 32)
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
publicKey: {
|
|
270
|
+
requireUserVerification,
|
|
271
|
+
x: Hex.fromBytes(xBytes),
|
|
272
|
+
y: Hex.fromBytes(yBytes),
|
|
273
|
+
metadata,
|
|
274
|
+
},
|
|
275
|
+
r,
|
|
276
|
+
s,
|
|
277
|
+
authenticatorData,
|
|
278
|
+
clientDataJSON,
|
|
279
|
+
challengeIndex,
|
|
280
|
+
typeIndex,
|
|
281
|
+
embedMetadata: hasMetadata,
|
|
282
|
+
}
|
|
283
|
+
}
|