@sphereon/ssi-sdk.credential-vcdm2-sdjwt-provider 0.34.1-next.85

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.
@@ -0,0 +1,167 @@
1
+ import { toEthereumAddress } from 'did-jwt'
2
+ import type { VerificationMethod } from 'did-resolver'
3
+ import { base64ToBytes, bytesToHex, type EcdsaSignature, type ECDSASignature, extractPublicKeyBytes, type KNOWN_JWA, stringToBytes } from './util'
4
+ // @ts-ignore
5
+ // import { verifyBlockchainAccountId } from 'did-jwt'
6
+ import { secp256k1 } from '@noble/curves/secp256k1'
7
+ import { p256 } from '@noble/curves/p256'
8
+ import { ed25519 } from '@noble/curves/ed25519'
9
+ // @ts-ignore
10
+ import * as u8a from 'uint8arrays'
11
+ import { sha256 as hash } from '@noble/hashes/sha256'
12
+
13
+ export function sha256(payload: string | Uint8Array): Uint8Array {
14
+ const data = typeof payload === 'string' ? u8a.fromString(payload) : payload
15
+ return hash(data)
16
+ }
17
+ // converts a JOSE signature to it's components
18
+ export function toSignatureObject(signature: string, recoverable = false): EcdsaSignature {
19
+ const rawSig: Uint8Array = base64ToBytes(signature)
20
+ if (rawSig.length !== (recoverable ? 65 : 64)) {
21
+ throw new Error('wrong signature length')
22
+ }
23
+ const r: string = bytesToHex(rawSig.slice(0, 32))
24
+ const s: string = bytesToHex(rawSig.slice(32, 64))
25
+ const sigObj: EcdsaSignature = { r, s }
26
+ if (recoverable) {
27
+ sigObj.recoveryParam = rawSig[64]
28
+ }
29
+ return sigObj
30
+ }
31
+
32
+ export function toSignatureObject2(signature: string, recoverable = false): ECDSASignature {
33
+ const bytes = base64ToBytes(signature)
34
+ if (bytes.length !== (recoverable ? 65 : 64)) {
35
+ throw new Error('wrong signature length')
36
+ }
37
+ return {
38
+ compact: bytes.slice(0, 64),
39
+ recovery: bytes[64],
40
+ }
41
+ }
42
+
43
+ export function verifyES256(data: string, signature: string, authenticators: VerificationMethod[]): VerificationMethod {
44
+ const hash = sha256(data)
45
+ const sig = p256.Signature.fromCompact(toSignatureObject2(signature).compact)
46
+ const fullPublicKeys = authenticators.filter((a: VerificationMethod) => !a.ethereumAddress && !a.blockchainAccountId)
47
+
48
+ const signer: VerificationMethod | undefined = fullPublicKeys.find((pk: VerificationMethod) => {
49
+ try {
50
+ const { keyBytes } = extractPublicKeyBytes(pk)
51
+ return p256.verify(sig, hash, keyBytes)
52
+ } catch (err) {
53
+ return false
54
+ }
55
+ })
56
+
57
+ if (!signer) throw new Error('invalid_signature: Signature invalid for JWT')
58
+ return signer
59
+ }
60
+
61
+ export function verifyES256K(data: string, signature: string, authenticators: VerificationMethod[]): VerificationMethod {
62
+ const hash = sha256(data)
63
+ const signatureNormalized = secp256k1.Signature.fromCompact(base64ToBytes(signature)).normalizeS()
64
+ const fullPublicKeys = authenticators.filter((a: VerificationMethod) => {
65
+ return !a.ethereumAddress && !a.blockchainAccountId
66
+ })
67
+ const blockchainAddressKeys = authenticators.filter((a: VerificationMethod) => {
68
+ return a.ethereumAddress || a.blockchainAccountId
69
+ })
70
+
71
+ let signer: VerificationMethod | undefined = fullPublicKeys.find((pk: VerificationMethod) => {
72
+ try {
73
+ const { keyBytes } = extractPublicKeyBytes(pk)
74
+ return secp256k1.verify(signatureNormalized, hash, keyBytes)
75
+ } catch (err) {
76
+ return false
77
+ }
78
+ })
79
+
80
+ if (!signer && blockchainAddressKeys.length > 0) {
81
+ signer = verifyRecoverableES256K(data, signature, blockchainAddressKeys)
82
+ }
83
+
84
+ if (!signer) throw new Error('invalid_signature: Signature invalid for JWT')
85
+ return signer
86
+ }
87
+
88
+ export function verifyRecoverableES256K(data: string, signature: string, authenticators: VerificationMethod[]): VerificationMethod {
89
+ const signatures: ECDSASignature[] = []
90
+ if (signature.length > 86) {
91
+ signatures.push(toSignatureObject2(signature, true))
92
+ } else {
93
+ const so = toSignatureObject2(signature, false)
94
+ signatures.push({ ...so, recovery: 0 })
95
+ signatures.push({ ...so, recovery: 1 })
96
+ }
97
+ const hash = sha256(data)
98
+
99
+ const checkSignatureAgainstSigner = (sigObj: ECDSASignature): VerificationMethod | undefined => {
100
+ const signature = secp256k1.Signature.fromCompact(sigObj.compact).addRecoveryBit(sigObj.recovery || 0)
101
+ const recoveredPublicKey = signature.recoverPublicKey(hash)
102
+ const recoveredAddress = toEthereumAddress(recoveredPublicKey.toHex(false)).toLowerCase()
103
+ const recoveredPublicKeyHex = recoveredPublicKey.toHex(false)
104
+ const recoveredCompressedPublicKeyHex = recoveredPublicKey.toHex(true)
105
+
106
+ return authenticators.find((a: VerificationMethod) => {
107
+ const { keyBytes } = extractPublicKeyBytes(a)
108
+ const keyHex = bytesToHex(keyBytes)
109
+ return (
110
+ keyHex === recoveredPublicKeyHex ||
111
+ keyHex === recoveredCompressedPublicKeyHex ||
112
+ a.ethereumAddress?.toLowerCase() === recoveredAddress ||
113
+ a.blockchainAccountId?.split('@eip155')?.[0].toLowerCase() === recoveredAddress //|| // CAIP-2
114
+ // verifyBlockchainAccountId(recoveredPublicKeyHex, a.blockchainAccountId) // CAIP-10
115
+ )
116
+ })
117
+ }
118
+
119
+ // Find first verification method
120
+ for (const signature of signatures) {
121
+ const verificationMethod = checkSignatureAgainstSigner(signature)
122
+ if (verificationMethod) return verificationMethod
123
+ }
124
+ // If no one found matching
125
+ throw new Error('invalid_signature: Signature invalid for JWT')
126
+ }
127
+
128
+ export function verifyEd25519(data: string, signature: string, authenticators: VerificationMethod[]): VerificationMethod {
129
+ const clear = stringToBytes(data)
130
+ const signatureBytes = base64ToBytes(signature)
131
+ const signer = authenticators.find((a: VerificationMethod) => {
132
+ const { keyBytes, keyType } = extractPublicKeyBytes(a)
133
+ if (keyType === 'Ed25519') {
134
+ return ed25519.verify(signatureBytes, clear, keyBytes)
135
+ } else {
136
+ return false
137
+ }
138
+ })
139
+ if (!signer) throw new Error('invalid_signature: Signature invalid for JWT')
140
+ return signer
141
+ }
142
+
143
+ type Verifier = (data: string, signature: string, authenticators: VerificationMethod[]) => VerificationMethod
144
+
145
+ type Algorithms = Record<KNOWN_JWA, Verifier>
146
+
147
+ const algorithms: Algorithms = {
148
+ ES256: verifyES256,
149
+ ES256K: verifyES256K,
150
+ // This is a non-standard algorithm but retained for backwards compatibility
151
+ // see https://github.com/decentralized-identity/did-jwt/issues/146
152
+ 'ES256K-R': verifyRecoverableES256K,
153
+ // This is actually incorrect but retained for backwards compatibility
154
+ // see https://github.com/decentralized-identity/did-jwt/issues/130
155
+ Ed25519: verifyEd25519,
156
+ EdDSA: verifyEd25519,
157
+ }
158
+
159
+ function VerifierAlgorithm(alg: string): Verifier {
160
+ const impl: Verifier = algorithms[alg as KNOWN_JWA]
161
+ if (!impl) throw new Error(`not_supported: Unsupported algorithm ${alg}`)
162
+ return impl
163
+ }
164
+
165
+ VerifierAlgorithm.toSignatureObject = toSignatureObject
166
+
167
+ export default VerifierAlgorithm
@@ -0,0 +1,407 @@
1
+ // @ts-ignore
2
+ import * as u8a from 'uint8arrays'
3
+ // const { concat, fromString, toString } = u8a
4
+ import { x25519 } from '@noble/curves/ed25519'
5
+
6
+ // @ts-ignore
7
+ import { varint } from 'multiformats'
8
+ import { BaseName, decode, encode } from 'multibase'
9
+ import type { VerificationMethod } from 'did-resolver'
10
+ import { secp256k1 } from '@noble/curves/secp256k1'
11
+ import { p256 } from '@noble/curves/p256'
12
+
13
+ // const u8a = { toString, fromString, concat }
14
+
15
+ export interface EphemeralPublicKey {
16
+ kty?: string
17
+ //ECC
18
+ crv?: string
19
+ x?: string
20
+ y?: string
21
+ //RSA
22
+ n?: string
23
+ e?: string
24
+ }
25
+ /**
26
+ * @deprecated Signers will be expected to return base64url `string` signatures.
27
+ */
28
+ export interface EcdsaSignature {
29
+ r: string
30
+ s: string
31
+ recoveryParam?: number
32
+ }
33
+
34
+ /**
35
+ * @deprecated Signers will be expected to return base64url `string` signatures.
36
+ */
37
+ export type ECDSASignature = {
38
+ compact: Uint8Array
39
+ recovery?: number
40
+ }
41
+
42
+ export type JsonWebKey = {
43
+ crv: string
44
+ kty: string
45
+ x?: string
46
+ y?: string
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ [key: string]: any
49
+ }
50
+
51
+ export function bytesToBase64url(b: Uint8Array): string {
52
+ return u8a.toString(b, 'base64url')
53
+ }
54
+
55
+ export function base64ToBytes(s: string): Uint8Array {
56
+ const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
57
+ return u8a.fromString(inputBase64Url, 'base64url')
58
+ }
59
+
60
+ export function bytesToBase64(b: Uint8Array): string {
61
+ return u8a.toString(b, 'base64pad')
62
+ }
63
+
64
+ export function base58ToBytes(s: string): Uint8Array {
65
+ return u8a.fromString(s, 'base58btc')
66
+ }
67
+
68
+ export function bytesToBase58(b: Uint8Array): string {
69
+ return u8a.toString(b, 'base58btc')
70
+ }
71
+
72
+ export type KNOWN_JWA = 'ES256' | 'ES256K' | 'ES256K-R' | 'Ed25519' | 'EdDSA'
73
+
74
+ export type KNOWN_VERIFICATION_METHOD =
75
+ | 'JsonWebKey2020'
76
+ | 'Multikey'
77
+ | 'Secp256k1SignatureVerificationKey2018' // deprecated in favor of EcdsaSecp256k1VerificationKey2019
78
+ | 'Secp256k1VerificationKey2018' // deprecated in favor of EcdsaSecp256k1VerificationKey2019
79
+ | 'EcdsaSecp256k1VerificationKey2019' // ES256K / ES256K-R
80
+ | 'EcdsaPublicKeySecp256k1' // deprecated in favor of EcdsaSecp256k1VerificationKey2019
81
+ | 'EcdsaSecp256k1RecoveryMethod2020' // ES256K-R (ES256K also supported with 1 less bit of security)
82
+ | 'EcdsaSecp256r1VerificationKey2019' // ES256 / P-256
83
+ | 'Ed25519VerificationKey2018'
84
+ | 'Ed25519VerificationKey2020'
85
+ | 'ED25519SignatureVerification' // deprecated
86
+ | 'ConditionalProof2022'
87
+ | 'X25519KeyAgreementKey2019' // deprecated
88
+ | 'X25519KeyAgreementKey2020'
89
+
90
+ export type KNOWN_KEY_TYPE = 'Secp256k1' | 'Ed25519' | 'X25519' | 'Bls12381G1' | 'Bls12381G2' | 'P-256'
91
+
92
+ export type PublicKeyTypes = Record<KNOWN_JWA, KNOWN_VERIFICATION_METHOD[]>
93
+
94
+ export const SUPPORTED_PUBLIC_KEY_TYPES: PublicKeyTypes = {
95
+ ES256: ['JsonWebKey2020', 'Multikey', 'EcdsaSecp256r1VerificationKey2019'],
96
+ ES256K: [
97
+ 'EcdsaSecp256k1VerificationKey2019',
98
+ /**
99
+ * Equivalent to EcdsaSecp256k1VerificationKey2019 when key is an ethereumAddress
100
+ */
101
+ 'EcdsaSecp256k1RecoveryMethod2020',
102
+ /**
103
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
104
+ * not an ethereumAddress
105
+ */
106
+ 'Secp256k1VerificationKey2018',
107
+ /**
108
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
109
+ * not an ethereumAddress
110
+ */
111
+ 'Secp256k1SignatureVerificationKey2018',
112
+ /**
113
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
114
+ * not an ethereumAddress
115
+ */
116
+ 'EcdsaPublicKeySecp256k1',
117
+ /**
118
+ * TODO - support R1 key as well
119
+ * 'ConditionalProof2022',
120
+ */
121
+ 'JsonWebKey2020',
122
+ 'Multikey',
123
+ ],
124
+ 'ES256K-R': [
125
+ 'EcdsaSecp256k1VerificationKey2019',
126
+ /**
127
+ * Equivalent to EcdsaSecp256k1VerificationKey2019 when key is an ethereumAddress
128
+ */
129
+ 'EcdsaSecp256k1RecoveryMethod2020',
130
+ /**
131
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
132
+ * not an ethereumAddress
133
+ */
134
+ 'Secp256k1VerificationKey2018',
135
+ /**
136
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
137
+ * not an ethereumAddress
138
+ */
139
+ 'Secp256k1SignatureVerificationKey2018',
140
+ /**
141
+ * @deprecated, supported for backward compatibility. Equivalent to EcdsaSecp256k1VerificationKey2019 when key is
142
+ * not an ethereumAddress
143
+ */
144
+ 'EcdsaPublicKeySecp256k1',
145
+ 'ConditionalProof2022',
146
+ 'JsonWebKey2020',
147
+ 'Multikey',
148
+ ],
149
+ Ed25519: ['ED25519SignatureVerification', 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', 'JsonWebKey2020', 'Multikey'],
150
+ EdDSA: ['ED25519SignatureVerification', 'Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', 'JsonWebKey2020', 'Multikey'],
151
+ }
152
+
153
+ export const VM_TO_KEY_TYPE: Record<KNOWN_VERIFICATION_METHOD, KNOWN_KEY_TYPE | undefined> = {
154
+ Secp256k1SignatureVerificationKey2018: 'Secp256k1',
155
+ Secp256k1VerificationKey2018: 'Secp256k1',
156
+ EcdsaSecp256k1VerificationKey2019: 'Secp256k1',
157
+ EcdsaPublicKeySecp256k1: 'Secp256k1',
158
+ EcdsaSecp256k1RecoveryMethod2020: 'Secp256k1',
159
+ EcdsaSecp256r1VerificationKey2019: 'P-256',
160
+ Ed25519VerificationKey2018: 'Ed25519',
161
+ Ed25519VerificationKey2020: 'Ed25519',
162
+ ED25519SignatureVerification: 'Ed25519',
163
+ X25519KeyAgreementKey2019: 'X25519',
164
+ X25519KeyAgreementKey2020: 'X25519',
165
+ ConditionalProof2022: undefined,
166
+ JsonWebKey2020: undefined, // key type must be specified in the JWK
167
+ Multikey: undefined, // key type must be extracted from the multicodec
168
+ }
169
+
170
+ export type KNOWN_CODECS = 'ed25519-pub' | 'x25519-pub' | 'secp256k1-pub' | 'bls12_381-g1-pub' | 'bls12_381-g2-pub' | 'p256-pub'
171
+
172
+ // this is from the multicodec table https://github.com/multiformats/multicodec/blob/master/table.csv
173
+ export const supportedCodecs: Record<KNOWN_CODECS, number> = {
174
+ 'ed25519-pub': 0xed,
175
+ 'x25519-pub': 0xec,
176
+ 'secp256k1-pub': 0xe7,
177
+ 'bls12_381-g1-pub': 0xea,
178
+ 'bls12_381-g2-pub': 0xeb,
179
+ 'p256-pub': 0x1200,
180
+ }
181
+
182
+ export const CODEC_TO_KEY_TYPE: Record<KNOWN_CODECS, KNOWN_KEY_TYPE> = {
183
+ 'bls12_381-g1-pub': 'Bls12381G1',
184
+ 'bls12_381-g2-pub': 'Bls12381G2',
185
+ 'ed25519-pub': 'Ed25519',
186
+ 'p256-pub': 'P-256',
187
+ 'secp256k1-pub': 'Secp256k1',
188
+ 'x25519-pub': 'X25519',
189
+ }
190
+
191
+ /**
192
+ * Extracts the raw byte representation of a public key from a VerificationMethod along with an inferred key type
193
+ * @param pk a VerificationMethod entry from a DIDDocument
194
+ * @return an object containing the `keyBytes` of the public key and an inferred `keyType`
195
+ */
196
+ export function extractPublicKeyBytes(pk: VerificationMethod): { keyBytes: Uint8Array; keyType?: KNOWN_KEY_TYPE } {
197
+ if (pk.publicKeyBase58) {
198
+ return {
199
+ keyBytes: base58ToBytes(pk.publicKeyBase58),
200
+ keyType: VM_TO_KEY_TYPE[pk.type as KNOWN_VERIFICATION_METHOD],
201
+ }
202
+ } else if (pk.publicKeyBase64) {
203
+ return {
204
+ keyBytes: base64ToBytes(pk.publicKeyBase64),
205
+ keyType: VM_TO_KEY_TYPE[pk.type as KNOWN_VERIFICATION_METHOD],
206
+ }
207
+ } else if (pk.publicKeyHex) {
208
+ return { keyBytes: hexToBytes(pk.publicKeyHex), keyType: VM_TO_KEY_TYPE[pk.type as KNOWN_VERIFICATION_METHOD] }
209
+ } else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'secp256k1' && pk.publicKeyJwk.x && pk.publicKeyJwk.y) {
210
+ return {
211
+ keyBytes: secp256k1.ProjectivePoint.fromAffine({
212
+ x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
213
+ y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y)),
214
+ }).toRawBytes(false),
215
+ keyType: 'Secp256k1',
216
+ }
217
+ } else if (pk.publicKeyJwk && pk.publicKeyJwk.crv === 'P-256' && pk.publicKeyJwk.x && pk.publicKeyJwk.y) {
218
+ return {
219
+ keyBytes: p256.ProjectivePoint.fromAffine({
220
+ x: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.x)),
221
+ y: bytesToBigInt(base64ToBytes(pk.publicKeyJwk.y)),
222
+ }).toRawBytes(false),
223
+ keyType: 'P-256',
224
+ }
225
+ } else if (pk.publicKeyJwk && pk.publicKeyJwk.kty === 'OKP' && ['Ed25519', 'X25519'].includes(pk.publicKeyJwk.crv ?? '') && pk.publicKeyJwk.x) {
226
+ return { keyBytes: base64ToBytes(pk.publicKeyJwk.x), keyType: pk.publicKeyJwk.crv as KNOWN_KEY_TYPE }
227
+ } else if (pk.publicKeyMultibase) {
228
+ const { keyBytes, keyType } = multibaseToBytes(pk.publicKeyMultibase)
229
+ return { keyBytes, keyType: keyType ?? VM_TO_KEY_TYPE[pk.type as KNOWN_VERIFICATION_METHOD] }
230
+ }
231
+ return { keyBytes: new Uint8Array() }
232
+ }
233
+
234
+ /**
235
+ * Encodes the given byte array to a multibase string (defaulting to base58btc).
236
+ * If a codec is provided, the corresponding multicodec prefix will be added.
237
+ *
238
+ * @param b - the Uint8Array to be encoded
239
+ * @param base - the base to use for encoding (defaults to base58btc)
240
+ * @param codec - the codec to use for encoding (defaults to no codec)
241
+ *
242
+ * @returns the multibase encoded string
243
+ *
244
+ * @public
245
+ */
246
+ export function bytesToMultibase(b: Uint8Array, base: BaseName = 'base58btc', codec?: keyof typeof supportedCodecs | number): string {
247
+ if (!codec) {
248
+ return u8a.toString(encode(base, b), 'utf-8')
249
+ } else {
250
+ const codecCode = typeof codec === 'string' ? supportedCodecs[codec] : codec
251
+ const prefixLength = varint.encodingLength(codecCode)
252
+ const multicodecEncoding = new Uint8Array(prefixLength + b.length)
253
+ varint.encodeTo(codecCode, multicodecEncoding) // set prefix
254
+ multicodecEncoding.set(b, prefixLength) // add the original bytes
255
+ return u8a.toString(encode(base, multicodecEncoding), 'utf-8')
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Converts a multibase string to the Uint8Array it represents.
261
+ * This method will assume the byte array that is multibase encoded is a multicodec and will attempt to decode it.
262
+ *
263
+ * @param s - the string to be converted
264
+ *
265
+ * @throws if the string is not formatted correctly.
266
+ *
267
+ * @public
268
+ */
269
+ export function multibaseToBytes(s: string): { keyBytes: Uint8Array; keyType?: KNOWN_KEY_TYPE } {
270
+ const bytes = decode(s)
271
+
272
+ // look for known key lengths first
273
+ // Ed25519/X25519, secp256k1/P256 compressed or not, BLS12-381 G1/G2 compressed
274
+ if ([32, 33, 48, 64, 65, 96].includes(bytes.length)) {
275
+ return { keyBytes: bytes }
276
+ }
277
+
278
+ // then assume multicodec, otherwise return the bytes
279
+ try {
280
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
281
+ const [codec, length] = varint.decode(bytes)
282
+ const possibleCodec: string | undefined = Object.entries(supportedCodecs).filter(([, code]) => code === codec)?.[0][0] ?? ''
283
+ return { keyBytes: bytes.slice(length), keyType: CODEC_TO_KEY_TYPE[possibleCodec as KNOWN_CODECS] }
284
+ } catch (e) {
285
+ // not a multicodec, return the bytes
286
+ return { keyBytes: bytes }
287
+ }
288
+ }
289
+
290
+ export function hexToBytes(s: string, minLength?: number): Uint8Array {
291
+ let input = s.startsWith('0x') ? s.substring(2) : s
292
+
293
+ if (input.length % 2 !== 0) {
294
+ input = `0${input}`
295
+ }
296
+
297
+ if (minLength) {
298
+ const paddedLength = Math.max(input.length, minLength * 2)
299
+ input = input.padStart(paddedLength, '00')
300
+ }
301
+
302
+ return u8a.fromString(input.toLowerCase(), 'base16')
303
+ }
304
+
305
+ export function encodeBase64url(s: string): string {
306
+ return bytesToBase64url(u8a.fromString(s))
307
+ }
308
+
309
+ export function decodeBase64url(s: string): string {
310
+ return u8a.toString(base64ToBytes(s))
311
+ }
312
+
313
+ export function bytesToHex(b: Uint8Array): string {
314
+ return u8a.toString(b, 'base16')
315
+ }
316
+
317
+ export function bytesToBigInt(b: Uint8Array): bigint {
318
+ return BigInt(`0x` + u8a.toString(b, 'base16'))
319
+ }
320
+
321
+ export function bigintToBytes(n: bigint, minLength?: number): Uint8Array {
322
+ return hexToBytes(n.toString(16), minLength)
323
+ }
324
+
325
+ export function stringToBytes(s: string): Uint8Array {
326
+ return u8a.fromString(s, 'utf-8')
327
+ }
328
+
329
+ export function toJose({ r, s, recoveryParam }: EcdsaSignature, recoverable?: boolean): string {
330
+ const jose = new Uint8Array(recoverable ? 65 : 64)
331
+ jose.set(u8a.fromString(r, 'base16'), 0)
332
+ jose.set(u8a.fromString(s, 'base16'), 32)
333
+ if (recoverable) {
334
+ if (typeof recoveryParam === 'undefined') {
335
+ throw new Error('Signer did not return a recoveryParam')
336
+ }
337
+ jose[64] = <number>recoveryParam
338
+ }
339
+ return bytesToBase64url(jose)
340
+ }
341
+
342
+ export function fromJose(signature: string): { r: string; s: string; recoveryParam?: number } {
343
+ const signatureBytes: Uint8Array = base64ToBytes(signature)
344
+ if (signatureBytes.length < 64 || signatureBytes.length > 65) {
345
+ throw new TypeError(`Wrong size for signature. Expected 64 or 65 bytes, but got ${signatureBytes.length}`)
346
+ }
347
+ const r = bytesToHex(signatureBytes.slice(0, 32))
348
+ const s = bytesToHex(signatureBytes.slice(32, 64))
349
+ const recoveryParam = signatureBytes.length === 65 ? signatureBytes[64] : undefined
350
+ return { r, s, recoveryParam }
351
+ }
352
+
353
+ export function toSealed(ciphertext: string, tag?: string): Uint8Array {
354
+ return u8a.concat([base64ToBytes(ciphertext), tag ? base64ToBytes(tag) : new Uint8Array(0)])
355
+ }
356
+
357
+ export function leftpad(data: string, size = 64): string {
358
+ if (data.length === size) return data
359
+ return '0'.repeat(size - data.length) + data
360
+ }
361
+
362
+ /**
363
+ * Generate random x25519 key pair.
364
+ */
365
+ export function generateKeyPair(): { secretKey: Uint8Array; publicKey: Uint8Array } {
366
+ const secretKey = x25519.utils.randomPrivateKey()
367
+ const publicKey = x25519.getPublicKey(secretKey)
368
+ return {
369
+ secretKey: secretKey,
370
+ publicKey: publicKey,
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Generate private-public x25519 key pair from `seed`.
376
+ */
377
+ export function generateKeyPairFromSeed(seed: Uint8Array): { secretKey: Uint8Array; publicKey: Uint8Array } {
378
+ if (seed.length !== 32) {
379
+ throw new Error(`x25519: seed must be ${32} bytes`)
380
+ }
381
+ return {
382
+ publicKey: x25519.getPublicKey(seed),
383
+ secretKey: seed,
384
+ }
385
+ }
386
+ /*
387
+
388
+ export function genX25519EphemeralKeyPair(): EphemeralKeyPair {
389
+ const epk = generateKeyPair()
390
+ return {
391
+ publicKeyJWK: { kty: 'OKP', crv: 'X25519', x: bytesToBase64url(epk.publicKey) },
392
+ secretKey: epk.secretKey,
393
+ }
394
+ }
395
+ */
396
+
397
+ /**
398
+ * Checks if a variable is defined and not null.
399
+ * After this check, typescript sees the variable as defined.
400
+ *
401
+ * @param arg - The input to be verified
402
+ *
403
+ * @returns true if the input variable is defined.
404
+ */
405
+ export function isDefined<T>(arg: T): arg is Exclude<T, null | undefined> {
406
+ return arg !== null && typeof arg !== 'undefined'
407
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { CredentialProviderVcdm2SdJwt } from './agent/CredentialProviderVcdm2SdJwt'