@sphereon/ssi-sdk-ext.key-utils 0.26.1-next.14 → 0.26.1-next.17

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/src/functions.ts CHANGED
@@ -1,37 +1,32 @@
1
- import {randomBytes} from '@ethersproject/random'
2
- import {bls12_381} from "@noble/curves/bls12-381";
3
- import {ed25519} from '@noble/curves/ed25519';
4
- import {p256} from '@noble/curves/p256';
5
- import {p384} from "@noble/curves/p384";
6
- import {p521} from "@noble/curves/p521";
7
- import {secp256k1} from '@noble/curves/secp256k1';
8
- import {sha256, sha384, sha512} from '@noble/hashes/sha2'
9
- import {
10
- generateRSAKeyAsPEM,
11
- hexToBase64,
12
- hexToPEM,
13
- PEMToJwk,
14
- privateKeyHexFromPEM
15
- } from '@sphereon/ssi-sdk-ext.x509-utils'
16
- import {JoseCurve, JoseSignatureAlgorithm, JWK, JwkKeyType, Loggers} from '@sphereon/ssi-types'
17
- import {generateKeyPair as generateSigningKeyPair} from '@stablelib/ed25519'
18
- import {IAgentContext, IKey, IKeyManager, ManagedKeyInfo, MinimalImportableKey} from '@veramo/core'
19
-
20
- import {JsonWebKey} from 'did-resolver'
1
+ import { randomBytes } from '@ethersproject/random'
2
+ // Do not change these require statements to imports before we change to ESM. Breaks external CJS packages depending on this module
3
+ const { bls12_381 } = require('@noble/curves/bls12-381')
4
+ const { ed25519 } = require('@noble/curves/ed25519')
5
+ const { p256 } = require('@noble/curves/p256')
6
+ const { p384 } = require('@noble/curves/p384')
7
+ const { p521 } = require('@noble/curves/p521')
8
+ const { secp256k1 } = require('@noble/curves/secp256k1')
9
+ const { sha256, sha384, sha512 } = require('@noble/hashes/sha2')
10
+ import { generateRSAKeyAsPEM, hexToBase64, hexToPEM, PEMToJwk, privateKeyHexFromPEM } from '@sphereon/ssi-sdk-ext.x509-utils'
11
+ import { JoseCurve, JoseSignatureAlgorithm, JWK, JwkKeyType, Loggers } from '@sphereon/ssi-types'
12
+ import { generateKeyPair as generateSigningKeyPair } from '@stablelib/ed25519'
13
+ import { IAgentContext, IKey, IKeyManager, ManagedKeyInfo, MinimalImportableKey } from '@veramo/core'
14
+
15
+ import { JsonWebKey } from 'did-resolver'
21
16
  import elliptic from 'elliptic'
22
- import * as rsa from 'micro-rsa-dsa-dh/rsa.js';
17
+ import * as rsa from 'micro-rsa-dsa-dh/rsa.js'
23
18
  import * as u8a from 'uint8arrays'
24
- import {digestMethodParams} from './digest-methods'
25
- import {validateJwk} from "./jwk-jcs";
19
+ import { digestMethodParams } from './digest-methods'
20
+ import { validateJwk } from './jwk-jcs'
26
21
  import {
27
- ENC_KEY_ALGS,
28
- IImportProvidedOrGeneratedKeyArgs,
29
- JwkKeyUse,
30
- KeyTypeFromCryptographicSuiteArgs,
31
- SIG_KEY_ALGS,
32
- SignatureAlgorithmFromKeyArgs,
33
- SignatureAlgorithmFromKeyTypeArgs,
34
- TKeyType,
22
+ ENC_KEY_ALGS,
23
+ IImportProvidedOrGeneratedKeyArgs,
24
+ JwkKeyUse,
25
+ KeyTypeFromCryptographicSuiteArgs,
26
+ SIG_KEY_ALGS,
27
+ SignatureAlgorithmFromKeyArgs,
28
+ SignatureAlgorithmFromKeyTypeArgs,
29
+ TKeyType,
35
30
  } from './types'
36
31
 
37
32
  export const logger = Loggers.DEFAULT.get('sphereon:key-utils')
@@ -43,13 +38,13 @@ export const logger = Loggers.DEFAULT.get('sphereon:key-utils')
43
38
  * @param kms. Optional KMS to use. If provided will be the returned name. Otherwise the default KMS will be returned
44
39
  */
45
40
  export const getKms = async (context: IAgentContext<any>, kms?: string): Promise<string> => {
46
- if (kms) {
47
- return kms
48
- }
49
- if (!context.agent.availableMethods().includes('keyManagerGetDefaultKeyManagementSystem')) {
50
- throw Error('Cannot determine default KMS if not provided and a non Sphereon Key Manager is being used')
51
- }
52
- return context.agent.keyManagerGetDefaultKeyManagementSystem()
41
+ if (kms) {
42
+ return kms
43
+ }
44
+ if (!context.agent.availableMethods().includes('keyManagerGetDefaultKeyManagementSystem')) {
45
+ throw Error('Cannot determine default KMS if not provided and a non Sphereon Key Manager is being used')
46
+ }
47
+ return context.agent.keyManagerGetDefaultKeyManagementSystem()
53
48
  }
54
49
 
55
50
  /**
@@ -58,41 +53,41 @@ export const getKms = async (context: IAgentContext<any>, kms?: string): Promise
58
53
  * @return The private key in Hex form
59
54
  */
60
55
  export const generatePrivateKeyHex = async (type: TKeyType): Promise<string> => {
61
- switch (type) {
62
- case 'Ed25519': {
63
- const keyPairEd25519 = generateSigningKeyPair()
64
- return u8a.toString(keyPairEd25519.secretKey, 'base16')
65
- }
66
- // The Secp256 types use the same method to generate the key
67
- case 'Secp256r1':
68
- case 'Secp256k1': {
69
- const privateBytes = randomBytes(32)
70
- return u8a.toString(privateBytes, 'base16')
71
- }
72
- case 'RSA': {
73
- const pem = await generateRSAKeyAsPEM('RSA-PSS', 'SHA-256', 2048)
74
- return privateKeyHexFromPEM(pem)
75
- }
76
- default:
77
- throw Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`)
56
+ switch (type) {
57
+ case 'Ed25519': {
58
+ const keyPairEd25519 = generateSigningKeyPair()
59
+ return u8a.toString(keyPairEd25519.secretKey, 'base16')
60
+ }
61
+ // The Secp256 types use the same method to generate the key
62
+ case 'Secp256r1':
63
+ case 'Secp256k1': {
64
+ const privateBytes = randomBytes(32)
65
+ return u8a.toString(privateBytes, 'base16')
66
+ }
67
+ case 'RSA': {
68
+ const pem = await generateRSAKeyAsPEM('RSA-PSS', 'SHA-256', 2048)
69
+ return privateKeyHexFromPEM(pem)
78
70
  }
71
+ default:
72
+ throw Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`)
73
+ }
79
74
  }
80
75
 
81
76
  const keyMetaAlgorithmsFromKeyType = (type: string | TKeyType) => {
82
- switch (type) {
83
- case 'Ed25519':
84
- return ['Ed25519', 'EdDSA']
85
- case 'ES256K':
86
- case 'Secp256k1':
87
- return ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage', 'eth_rawSign']
88
- case 'Secp256r1':
89
- return ['ES256']
90
- case 'X25519':
91
- return ['ECDH', 'ECDH-ES', 'ECDH-1PU']
92
- case 'RSA':
93
- return ['RS256', 'RS512', 'PS256', 'PS512']
94
- }
95
- return [type]
77
+ switch (type) {
78
+ case 'Ed25519':
79
+ return ['Ed25519', 'EdDSA']
80
+ case 'ES256K':
81
+ case 'Secp256k1':
82
+ return ['ES256K', 'ES256K-R', 'eth_signTransaction', 'eth_signTypedData', 'eth_signMessage', 'eth_rawSign']
83
+ case 'Secp256r1':
84
+ return ['ES256']
85
+ case 'X25519':
86
+ return ['ECDH', 'ECDH-ES', 'ECDH-1PU']
87
+ case 'RSA':
88
+ return ['RS256', 'RS512', 'PS256', 'PS512']
89
+ }
90
+ return [type]
96
91
  }
97
92
 
98
93
  /**
@@ -103,78 +98,78 @@ const keyMetaAlgorithmsFromKeyType = (type: string | TKeyType) => {
103
98
  * @private
104
99
  */
105
100
  export async function importProvidedOrGeneratedKey(
106
- args: IImportProvidedOrGeneratedKeyArgs & {
107
- kms: string
108
- },
109
- context: IAgentContext<IKeyManager>
101
+ args: IImportProvidedOrGeneratedKeyArgs & {
102
+ kms: string
103
+ },
104
+ context: IAgentContext<IKeyManager>
110
105
  ): Promise<IKey> {
111
- // @ts-ignore
112
- const type = args.options?.type ?? args.options?.key?.type ?? args.options?.keyType ?? 'Secp256r1'
113
- const key = args?.options?.key
114
- // Make sure x509 options are also set on the metadata as that is what the kms will look for
115
- if (args.options?.x509 && key) {
116
- key.meta = {
117
- ...key.meta,
118
- x509: {
119
- ...args.options.x509,
120
- ...key.meta?.x509,
121
- },
122
- }
123
- }
124
-
125
- if (args.options && args.options?.use === JwkKeyUse.Encryption && !ENC_KEY_ALGS.includes(type)) {
126
- throw new Error(`${type} keys are not valid for encryption`)
127
- }
128
-
129
- let privateKeyHex: string | undefined = undefined
130
- if (key) {
131
- privateKeyHex = key.privateKeyHex ?? key.meta?.x509?.privateKeyHex
132
- if ((!privateKeyHex || privateKeyHex.trim() === '') && key?.meta?.x509?.privateKeyPEM) {
133
- // If we do not have a privateKeyHex but do have a PEM
134
- privateKeyHex = privateKeyHexFromPEM(key.meta.x509.privateKeyPEM)
135
- }
136
- }
137
- if (privateKeyHex) {
138
- return context.agent.keyManagerImport({
139
- ...key,
140
- kms: args.kms,
141
- type,
142
- privateKeyHex: privateKeyHex!,
143
- })
144
- }
145
-
146
- return context.agent.keyManagerCreate({
147
- type,
148
- kms: args.kms,
149
- meta: {
150
- ...key?.meta,
151
- algorithms: keyMetaAlgorithmsFromKeyType(type),
152
- keyAlias: args.alias,
153
- },
106
+ // @ts-ignore
107
+ const type = args.options?.type ?? args.options?.key?.type ?? args.options?.keyType ?? 'Secp256r1'
108
+ const key = args?.options?.key
109
+ // Make sure x509 options are also set on the metadata as that is what the kms will look for
110
+ if (args.options?.x509 && key) {
111
+ key.meta = {
112
+ ...key.meta,
113
+ x509: {
114
+ ...args.options.x509,
115
+ ...key.meta?.x509,
116
+ },
117
+ }
118
+ }
119
+
120
+ if (args.options && args.options?.use === JwkKeyUse.Encryption && !ENC_KEY_ALGS.includes(type)) {
121
+ throw new Error(`${type} keys are not valid for encryption`)
122
+ }
123
+
124
+ let privateKeyHex: string | undefined = undefined
125
+ if (key) {
126
+ privateKeyHex = key.privateKeyHex ?? key.meta?.x509?.privateKeyHex
127
+ if ((!privateKeyHex || privateKeyHex.trim() === '') && key?.meta?.x509?.privateKeyPEM) {
128
+ // If we do not have a privateKeyHex but do have a PEM
129
+ privateKeyHex = privateKeyHexFromPEM(key.meta.x509.privateKeyPEM)
130
+ }
131
+ }
132
+ if (privateKeyHex) {
133
+ return context.agent.keyManagerImport({
134
+ ...key,
135
+ kms: args.kms,
136
+ type,
137
+ privateKeyHex: privateKeyHex!,
154
138
  })
139
+ }
140
+
141
+ return context.agent.keyManagerCreate({
142
+ type,
143
+ kms: args.kms,
144
+ meta: {
145
+ ...key?.meta,
146
+ algorithms: keyMetaAlgorithmsFromKeyType(type),
147
+ keyAlias: args.alias,
148
+ },
149
+ })
155
150
  }
156
151
 
157
152
  export const calculateJwkThumbprintForKey = (args: {
158
- key: IKey | MinimalImportableKey | ManagedKeyInfo
159
- digestAlgorithm?: 'sha256' | 'sha512'
153
+ key: IKey | MinimalImportableKey | ManagedKeyInfo
154
+ digestAlgorithm?: 'sha256' | 'sha512'
160
155
  }): string => {
161
- const {key} = args
162
-
163
- const jwk = key.publicKeyHex
164
- ? toJwk(key.publicKeyHex, key.type, {key: key, isPrivateKey: false})
165
- : 'privateKeyHex' in key && key.privateKeyHex
166
- ? toJwk(key.privateKeyHex, key.type, {isPrivateKey: true})
167
- : undefined
168
- if (!jwk) {
169
- throw Error(`Could not determine jwk from key ${key.kid}`)
170
- }
171
- return calculateJwkThumbprint({jwk, digestAlgorithm: args.digestAlgorithm})
156
+ const { key } = args
157
+
158
+ const jwk = key.publicKeyHex
159
+ ? toJwk(key.publicKeyHex, key.type, { key: key, isPrivateKey: false })
160
+ : 'privateKeyHex' in key && key.privateKeyHex
161
+ ? toJwk(key.privateKeyHex, key.type, { isPrivateKey: true })
162
+ : undefined
163
+ if (!jwk) {
164
+ throw Error(`Could not determine jwk from key ${key.kid}`)
165
+ }
166
+ return calculateJwkThumbprint({ jwk, digestAlgorithm: args.digestAlgorithm })
172
167
  }
173
168
 
174
169
  const assertJwkClaimPresent = (value: unknown, description: string) => {
175
- if (typeof value !== 'string' || !value) {
176
- throw new Error(`${description} missing or invalid`)
177
- }
170
+ if (typeof value !== 'string' || !value) {
171
+ throw new Error(`${description} missing or invalid`)
172
+ }
178
173
  }
179
174
  export const toBase64url = (input: string): string => u8a.toString(u8a.fromString(input), 'base64url')
180
175
 
@@ -183,48 +178,48 @@ export const toBase64url = (input: string): string => u8a.toString(u8a.fromStrin
183
178
  * @param args
184
179
  */
185
180
  export const calculateJwkThumbprint = (args: { jwk: JWK; digestAlgorithm?: 'sha256' | 'sha512' }): string => {
186
- const {jwk, digestAlgorithm = 'sha256'} = args
187
- let components
188
- switch (jwk.kty) {
189
- case 'EC':
190
- assertJwkClaimPresent(jwk.crv, '"crv" (Curve) Parameter')
191
- assertJwkClaimPresent(jwk.x, '"x" (X Coordinate) Parameter')
192
- assertJwkClaimPresent(jwk.y, '"y" (Y Coordinate) Parameter')
193
- components = {crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y}
194
- break
195
- case 'OKP':
196
- assertJwkClaimPresent(jwk.crv, '"crv" (Subtype of Key Pair) Parameter')
197
- assertJwkClaimPresent(jwk.x, '"x" (Public Key) Parameter')
198
- components = {crv: jwk.crv, kty: jwk.kty, x: jwk.x}
199
- break
200
- case 'RSA':
201
- assertJwkClaimPresent(jwk.e, '"e" (Exponent) Parameter')
202
- assertJwkClaimPresent(jwk.n, '"n" (Modulus) Parameter')
203
- components = {e: jwk.e, kty: jwk.kty, n: jwk.n}
204
- break
205
- case 'oct':
206
- assertJwkClaimPresent(jwk.k, '"k" (Key Value) Parameter')
207
- components = {k: jwk.k, kty: jwk.kty}
208
- break
209
- default:
210
- throw new Error('"kty" (Key Type) Parameter missing or unsupported')
211
- }
212
- const data = JSON.stringify(components)
213
-
214
- return digestAlgorithm === 'sha512'
215
- ? digestMethodParams('SHA-512').digestMethod(data, 'base64url')
216
- : digestMethodParams('SHA-256').digestMethod(data, 'base64url')
181
+ const { jwk, digestAlgorithm = 'sha256' } = args
182
+ let components
183
+ switch (jwk.kty) {
184
+ case 'EC':
185
+ assertJwkClaimPresent(jwk.crv, '"crv" (Curve) Parameter')
186
+ assertJwkClaimPresent(jwk.x, '"x" (X Coordinate) Parameter')
187
+ assertJwkClaimPresent(jwk.y, '"y" (Y Coordinate) Parameter')
188
+ components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y }
189
+ break
190
+ case 'OKP':
191
+ assertJwkClaimPresent(jwk.crv, '"crv" (Subtype of Key Pair) Parameter')
192
+ assertJwkClaimPresent(jwk.x, '"x" (Public Key) Parameter')
193
+ components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x }
194
+ break
195
+ case 'RSA':
196
+ assertJwkClaimPresent(jwk.e, '"e" (Exponent) Parameter')
197
+ assertJwkClaimPresent(jwk.n, '"n" (Modulus) Parameter')
198
+ components = { e: jwk.e, kty: jwk.kty, n: jwk.n }
199
+ break
200
+ case 'oct':
201
+ assertJwkClaimPresent(jwk.k, '"k" (Key Value) Parameter')
202
+ components = { k: jwk.k, kty: jwk.kty }
203
+ break
204
+ default:
205
+ throw new Error('"kty" (Key Type) Parameter missing or unsupported')
206
+ }
207
+ const data = JSON.stringify(components)
208
+
209
+ return digestAlgorithm === 'sha512'
210
+ ? digestMethodParams('SHA-512').digestMethod(data, 'base64url')
211
+ : digestMethodParams('SHA-256').digestMethod(data, 'base64url')
217
212
  }
218
213
 
219
214
  export const toJwkFromKey = (
220
- key: IKey | MinimalImportableKey | ManagedKeyInfo,
221
- opts?: {
222
- use?: JwkKeyUse
223
- noKidThumbprint?: boolean
224
- }
215
+ key: IKey | MinimalImportableKey | ManagedKeyInfo,
216
+ opts?: {
217
+ use?: JwkKeyUse
218
+ noKidThumbprint?: boolean
219
+ }
225
220
  ): JWK => {
226
- const isPrivateKey = 'privateKeyHex' in key
227
- return toJwk(key.publicKeyHex!, key.type, {...opts, key, isPrivateKey})
221
+ const isPrivateKey = 'privateKeyHex' in key
222
+ return toJwk(key.publicKeyHex!, key.type, { ...opts, key, isPrivateKey })
228
223
  }
229
224
 
230
225
  /**
@@ -235,38 +230,38 @@ export const toJwkFromKey = (
235
230
  * @return The JWK
236
231
  */
237
232
  export const toJwk = (
238
- publicKeyHex: string,
239
- type: TKeyType,
240
- opts?: { use?: JwkKeyUse; key?: IKey | MinimalImportableKey; isPrivateKey?: boolean; noKidThumbprint?: boolean }
233
+ publicKeyHex: string,
234
+ type: TKeyType,
235
+ opts?: { use?: JwkKeyUse; key?: IKey | MinimalImportableKey; isPrivateKey?: boolean; noKidThumbprint?: boolean }
241
236
  ): JWK => {
242
- const {key, noKidThumbprint = false} = opts ?? {}
243
- if (key && key.publicKeyHex !== publicKeyHex && opts?.isPrivateKey !== true) {
244
- throw Error(`Provided key with id ${key.kid}, has a different public key hex ${key.publicKeyHex} than supplied public key ${publicKeyHex}`)
245
- }
246
- let jwk: JWK
247
- switch (type) {
248
- case 'Ed25519':
249
- jwk = toEd25519OrX25519Jwk(publicKeyHex, {...opts, crv: JoseCurve.Ed25519})
250
- break
251
- case 'X25519':
252
- jwk = toEd25519OrX25519Jwk(publicKeyHex, {...opts, crv: JoseCurve.X25519})
253
- break
254
- case 'Secp256k1':
255
- jwk = toSecp256k1Jwk(publicKeyHex, opts)
256
- break
257
- case 'Secp256r1':
258
- jwk = toSecp256r1Jwk(publicKeyHex, opts)
259
- break
260
- case 'RSA':
261
- jwk = toRSAJwk(publicKeyHex, opts)
262
- break
263
- default:
264
- throw new Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`)
265
- }
266
- if (!jwk.kid && !noKidThumbprint) {
267
- jwk['kid'] = calculateJwkThumbprint({jwk})
268
- }
269
- return jwk
237
+ const { key, noKidThumbprint = false } = opts ?? {}
238
+ if (key && key.publicKeyHex !== publicKeyHex && opts?.isPrivateKey !== true) {
239
+ throw Error(`Provided key with id ${key.kid}, has a different public key hex ${key.publicKeyHex} than supplied public key ${publicKeyHex}`)
240
+ }
241
+ let jwk: JWK
242
+ switch (type) {
243
+ case 'Ed25519':
244
+ jwk = toEd25519OrX25519Jwk(publicKeyHex, { ...opts, crv: JoseCurve.Ed25519 })
245
+ break
246
+ case 'X25519':
247
+ jwk = toEd25519OrX25519Jwk(publicKeyHex, { ...opts, crv: JoseCurve.X25519 })
248
+ break
249
+ case 'Secp256k1':
250
+ jwk = toSecp256k1Jwk(publicKeyHex, opts)
251
+ break
252
+ case 'Secp256r1':
253
+ jwk = toSecp256r1Jwk(publicKeyHex, opts)
254
+ break
255
+ case 'RSA':
256
+ jwk = toRSAJwk(publicKeyHex, opts)
257
+ break
258
+ default:
259
+ throw new Error(`not_supported: Key type ${type} not yet supported for this did:jwk implementation`)
260
+ }
261
+ if (!jwk.kid && !noKidThumbprint) {
262
+ jwk['kid'] = calculateJwkThumbprint({ jwk })
263
+ }
264
+ return jwk
270
265
  }
271
266
 
272
267
  /**
@@ -276,18 +271,18 @@ export const toJwk = (
276
271
  * @returns A string representing the key in raw hexadecimal format.
277
272
  */
278
273
  export const jwkToRawHexKey = async (jwk: JWK): Promise<string> => {
279
- // TODO: Probably makes sense to have an option to do the same for private keys
280
- if (jwk.kty === 'RSA') {
281
- return rsaJwkToRawHexKey(jwk);
282
- } else if (jwk.kty === 'EC') {
283
- return "04" + ecJwkToRawHexKey(jwk);
284
- } else if (jwk.kty === 'OKP') {
285
- return okpJwkToRawHexKey(jwk);
286
- } else if (jwk.kty === 'oct') {
287
- return octJwkToRawHexKey(jwk);
288
- } else {
289
- throw new Error(`Unsupported key type: ${jwk.kty}`);
290
- }
274
+ // TODO: Probably makes sense to have an option to do the same for private keys
275
+ if (jwk.kty === 'RSA') {
276
+ return rsaJwkToRawHexKey(jwk)
277
+ } else if (jwk.kty === 'EC') {
278
+ return '04' + ecJwkToRawHexKey(jwk)
279
+ } else if (jwk.kty === 'OKP') {
280
+ return okpJwkToRawHexKey(jwk)
281
+ } else if (jwk.kty === 'oct') {
282
+ return octJwkToRawHexKey(jwk)
283
+ } else {
284
+ throw new Error(`Unsupported key type: ${jwk.kty}`)
285
+ }
291
286
  }
292
287
 
293
288
  /**
@@ -296,14 +291,14 @@ export const jwkToRawHexKey = async (jwk: JWK): Promise<string> => {
296
291
  * @returns A string representing the RSA key in raw hexadecimal format.
297
292
  */
298
293
  function rsaJwkToRawHexKey(jwk: JsonWebKey): string {
299
- if (!jwk.n || !jwk.e) {
300
- throw new Error("RSA JWK must contain 'n' and 'e' properties.");
301
- }
294
+ if (!jwk.n || !jwk.e) {
295
+ throw new Error("RSA JWK must contain 'n' and 'e' properties.")
296
+ }
302
297
 
303
- const modulus = u8a.fromString(jwk.n, 'base64url'); // 'n' is the modulus
304
- const exponent = u8a.fromString(jwk.e, 'base64url'); // 'e' is the exponent
298
+ const modulus = u8a.fromString(jwk.n, 'base64url') // 'n' is the modulus
299
+ const exponent = u8a.fromString(jwk.e, 'base64url') // 'e' is the exponent
305
300
 
306
- return u8a.toString(modulus, 'hex') + u8a.toString(exponent, 'hex');
301
+ return u8a.toString(modulus, 'hex') + u8a.toString(exponent, 'hex')
307
302
  }
308
303
 
309
304
  /**
@@ -312,14 +307,14 @@ function rsaJwkToRawHexKey(jwk: JsonWebKey): string {
312
307
  * @returns A string representing the EC key in raw hexadecimal format.
313
308
  */
314
309
  function ecJwkToRawHexKey(jwk: JsonWebKey): string {
315
- if (!jwk.x || !jwk.y) {
316
- throw new Error("EC JWK must contain 'x' and 'y' properties.");
317
- }
310
+ if (!jwk.x || !jwk.y) {
311
+ throw new Error("EC JWK must contain 'x' and 'y' properties.")
312
+ }
318
313
 
319
- const x = u8a.fromString(jwk.x, 'base64url');
320
- const y = u8a.fromString(jwk.y, 'base64url');
314
+ const x = u8a.fromString(jwk.x, 'base64url')
315
+ const y = u8a.fromString(jwk.y, 'base64url')
321
316
 
322
- return u8a.toString(x, 'hex') + u8a.toString(y, 'hex');
317
+ return u8a.toString(x, 'hex') + u8a.toString(y, 'hex')
323
318
  }
324
319
 
325
320
  /**
@@ -328,29 +323,28 @@ function ecJwkToRawHexKey(jwk: JsonWebKey): string {
328
323
  * @returns A string representing the EC key in raw hexadecimal format.
329
324
  */
330
325
  function okpJwkToRawHexKey(jwk: JsonWebKey): string {
331
- if (!jwk.x) {
332
- throw new Error("OKP JWK must contain 'x' property.");
333
- }
326
+ if (!jwk.x) {
327
+ throw new Error("OKP JWK must contain 'x' property.")
328
+ }
334
329
 
335
- const x = u8a.fromString(jwk.x, 'base64url');;
330
+ const x = u8a.fromString(jwk.x, 'base64url')
336
331
 
337
- return u8a.toString(x, 'hex');
332
+ return u8a.toString(x, 'hex')
338
333
  }
339
334
 
340
-
341
335
  /**
342
336
  * Convert an octet JWK to a raw hex key.
343
337
  * @param jwk - The octet JWK object.
344
338
  * @returns A string representing the octet key in raw hexadecimal format.
345
339
  */
346
340
  function octJwkToRawHexKey(jwk: JsonWebKey): string {
347
- if (!jwk.k) {
348
- throw new Error("Octet JWK must contain 'k' property.");
349
- }
341
+ if (!jwk.k) {
342
+ throw new Error("Octet JWK must contain 'k' property.")
343
+ }
350
344
 
351
- const key = u8a.fromString(jwk.k, 'base64url');
345
+ const key = u8a.fromString(jwk.k, 'base64url')
352
346
 
353
- return u8a.toString(key, 'hex');
347
+ return u8a.toString(key, 'hex')
354
348
  }
355
349
 
356
350
  /**
@@ -360,13 +354,13 @@ function octJwkToRawHexKey(jwk: JsonWebKey): string {
360
354
  * @param suppliedUse A supplied use. Will be used in case it is present
361
355
  */
362
356
  export const jwkDetermineUse = (type: TKeyType, suppliedUse?: JwkKeyUse): JwkKeyUse | undefined => {
363
- return suppliedUse
364
- ? suppliedUse
365
- : SIG_KEY_ALGS.includes(type)
366
- ? JwkKeyUse.Signature
367
- : ENC_KEY_ALGS.includes(type)
368
- ? JwkKeyUse.Encryption
369
- : undefined
357
+ return suppliedUse
358
+ ? suppliedUse
359
+ : SIG_KEY_ALGS.includes(type)
360
+ ? JwkKeyUse.Signature
361
+ : ENC_KEY_ALGS.includes(type)
362
+ ? JwkKeyUse.Encryption
363
+ : undefined
370
364
  }
371
365
 
372
366
  /**
@@ -376,17 +370,17 @@ export const jwkDetermineUse = (type: TKeyType, suppliedUse?: JwkKeyUse): JwkKey
376
370
  * @param expectedKeyLength Expected key length(s)
377
371
  */
378
372
  const assertProperKeyLength = (keyHex: string, expectedKeyLength: number | number[]) => {
379
- if (Array.isArray(expectedKeyLength)) {
380
- if (!expectedKeyLength.includes(keyHex.length)) {
381
- throw Error(
382
- `Invalid key length. Needs to be a hex string with length from ${JSON.stringify(expectedKeyLength)} instead of ${
383
- keyHex.length
384
- }. Input: ${keyHex}`
385
- )
386
- }
387
- } else if (keyHex.length !== expectedKeyLength) {
388
- throw Error(`Invalid key length. Needs to be a hex string with length ${expectedKeyLength} instead of ${keyHex.length}. Input: ${keyHex}`)
373
+ if (Array.isArray(expectedKeyLength)) {
374
+ if (!expectedKeyLength.includes(keyHex.length)) {
375
+ throw Error(
376
+ `Invalid key length. Needs to be a hex string with length from ${JSON.stringify(expectedKeyLength)} instead of ${
377
+ keyHex.length
378
+ }. Input: ${keyHex}`
379
+ )
389
380
  }
381
+ } else if (keyHex.length !== expectedKeyLength) {
382
+ throw Error(`Invalid key length. Needs to be a hex string with length ${expectedKeyLength} instead of ${keyHex.length}. Input: ${keyHex}`)
383
+ }
390
384
  }
391
385
 
392
386
  /**
@@ -396,28 +390,28 @@ const assertProperKeyLength = (keyHex: string, expectedKeyLength: number | numbe
396
390
  * @return The JWK
397
391
  */
398
392
  const toSecp256k1Jwk = (keyHex: string, opts?: { use?: JwkKeyUse; isPrivateKey?: boolean }): JWK => {
399
- const {use} = opts ?? {}
400
- logger.debug(`toSecp256k1Jwk keyHex: ${keyHex}, length: ${keyHex.length}`)
401
- if (opts?.isPrivateKey) {
402
- assertProperKeyLength(keyHex, [64])
403
- } else {
404
- assertProperKeyLength(keyHex, [66, 130])
405
- }
406
-
407
- const secp256k1 = new elliptic.ec('secp256k1')
408
- const keyBytes = u8a.fromString(keyHex, 'base16')
409
- const keyPair = opts?.isPrivateKey ? secp256k1.keyFromPrivate(keyBytes) : secp256k1.keyFromPublic(keyBytes)
410
- const pubPoint = keyPair.getPublic()
411
-
412
- return {
413
- alg: JoseSignatureAlgorithm.ES256K,
414
- ...(use !== undefined && {use}),
415
- kty: JwkKeyType.EC,
416
- crv: JoseCurve.secp256k1,
417
- x: hexToBase64(pubPoint.getX().toString('hex'), 'base64url'),
418
- y: hexToBase64(pubPoint.getY().toString('hex'), 'base64url'),
419
- ...(opts?.isPrivateKey && {d: hexToBase64(keyPair.getPrivate('hex'), 'base64url')}),
420
- }
393
+ const { use } = opts ?? {}
394
+ logger.debug(`toSecp256k1Jwk keyHex: ${keyHex}, length: ${keyHex.length}`)
395
+ if (opts?.isPrivateKey) {
396
+ assertProperKeyLength(keyHex, [64])
397
+ } else {
398
+ assertProperKeyLength(keyHex, [66, 130])
399
+ }
400
+
401
+ const secp256k1 = new elliptic.ec('secp256k1')
402
+ const keyBytes = u8a.fromString(keyHex, 'base16')
403
+ const keyPair = opts?.isPrivateKey ? secp256k1.keyFromPrivate(keyBytes) : secp256k1.keyFromPublic(keyBytes)
404
+ const pubPoint = keyPair.getPublic()
405
+
406
+ return {
407
+ alg: JoseSignatureAlgorithm.ES256K,
408
+ ...(use !== undefined && { use }),
409
+ kty: JwkKeyType.EC,
410
+ crv: JoseCurve.secp256k1,
411
+ x: hexToBase64(pubPoint.getX().toString('hex'), 'base64url'),
412
+ y: hexToBase64(pubPoint.getY().toString('hex'), 'base64url'),
413
+ ...(opts?.isPrivateKey && { d: hexToBase64(keyPair.getPrivate('hex'), 'base64url') }),
414
+ }
421
415
  }
422
416
 
423
417
  /**
@@ -427,28 +421,28 @@ const toSecp256k1Jwk = (keyHex: string, opts?: { use?: JwkKeyUse; isPrivateKey?:
427
421
  * @return The JWK
428
422
  */
429
423
  const toSecp256r1Jwk = (keyHex: string, opts?: { use?: JwkKeyUse; isPrivateKey?: boolean }): JWK => {
430
- const {use} = opts ?? {}
431
- logger.debug(`toSecp256r1Jwk keyHex: ${keyHex}, length: ${keyHex.length}`)
432
- if (opts?.isPrivateKey) {
433
- assertProperKeyLength(keyHex, [64])
434
- } else {
435
- assertProperKeyLength(keyHex, [66, 130])
436
- }
437
-
438
- const secp256r1 = new elliptic.ec('p256')
439
- const keyBytes = u8a.fromString(keyHex, 'base16')
440
- logger.debug(`keyBytes length: ${keyBytes}`)
441
- const keyPair = opts?.isPrivateKey ? secp256r1.keyFromPrivate(keyBytes) : secp256r1.keyFromPublic(keyBytes)
442
- const pubPoint = keyPair.getPublic()
443
- return {
444
- alg: JoseSignatureAlgorithm.ES256,
445
- ...(use !== undefined && {use}),
446
- kty: JwkKeyType.EC,
447
- crv: JoseCurve.P_256,
448
- x: hexToBase64(pubPoint.getX().toString('hex'), 'base64url'),
449
- y: hexToBase64(pubPoint.getY().toString('hex'), 'base64url'),
450
- ...(opts?.isPrivateKey && {d: hexToBase64(keyPair.getPrivate('hex'), 'base64url')}),
451
- }
424
+ const { use } = opts ?? {}
425
+ logger.debug(`toSecp256r1Jwk keyHex: ${keyHex}, length: ${keyHex.length}`)
426
+ if (opts?.isPrivateKey) {
427
+ assertProperKeyLength(keyHex, [64])
428
+ } else {
429
+ assertProperKeyLength(keyHex, [66, 130])
430
+ }
431
+
432
+ const secp256r1 = new elliptic.ec('p256')
433
+ const keyBytes = u8a.fromString(keyHex, 'base16')
434
+ logger.debug(`keyBytes length: ${keyBytes}`)
435
+ const keyPair = opts?.isPrivateKey ? secp256r1.keyFromPrivate(keyBytes) : secp256r1.keyFromPublic(keyBytes)
436
+ const pubPoint = keyPair.getPublic()
437
+ return {
438
+ alg: JoseSignatureAlgorithm.ES256,
439
+ ...(use !== undefined && { use }),
440
+ kty: JwkKeyType.EC,
441
+ crv: JoseCurve.P_256,
442
+ x: hexToBase64(pubPoint.getX().toString('hex'), 'base64url'),
443
+ y: hexToBase64(pubPoint.getY().toString('hex'), 'base64url'),
444
+ ...(opts?.isPrivateKey && { d: hexToBase64(keyPair.getPrivate('hex'), 'base64url') }),
445
+ }
452
446
  }
453
447
 
454
448
  /**
@@ -458,335 +452,350 @@ const toSecp256r1Jwk = (keyHex: string, opts?: { use?: JwkKeyUse; isPrivateKey?:
458
452
  * @return The JWK
459
453
  */
460
454
  const toEd25519OrX25519Jwk = (
461
- publicKeyHex: string,
462
- opts: {
463
- use?: JwkKeyUse
464
- crv: JoseCurve.Ed25519 | JoseCurve.X25519
465
- }
455
+ publicKeyHex: string,
456
+ opts: {
457
+ use?: JwkKeyUse
458
+ crv: JoseCurve.Ed25519 | JoseCurve.X25519
459
+ }
466
460
  ): JWK => {
467
- assertProperKeyLength(publicKeyHex, 64)
468
- const {use} = opts ?? {}
469
- return {
470
- alg: JoseSignatureAlgorithm.EdDSA,
471
- ...(use !== undefined && {use}),
472
- kty: JwkKeyType.OKP,
473
- crv: opts?.crv ?? JoseCurve.Ed25519,
474
- x: hexToBase64(publicKeyHex, 'base64url'),
475
- }
461
+ assertProperKeyLength(publicKeyHex, 64)
462
+ const { use } = opts ?? {}
463
+ return {
464
+ alg: JoseSignatureAlgorithm.EdDSA,
465
+ ...(use !== undefined && { use }),
466
+ kty: JwkKeyType.OKP,
467
+ crv: opts?.crv ?? JoseCurve.Ed25519,
468
+ x: hexToBase64(publicKeyHex, 'base64url'),
469
+ }
476
470
  }
477
471
 
478
472
  const toRSAJwk = (publicKeyHex: string, opts?: { use?: JwkKeyUse; key?: IKey | MinimalImportableKey }): JWK => {
479
- const meta = opts?.key?.meta
480
- if (meta?.publicKeyJwk || meta?.publicKeyPEM) {
481
- if (meta?.publicKeyJwk) {
482
- return meta.publicKeyJwk as JWK
483
- }
484
- const publicKeyPEM = meta?.publicKeyPEM ?? hexToPEM(publicKeyHex, 'public')
485
- return PEMToJwk(publicKeyPEM, 'public') as JWK
486
- }
487
-
488
-
489
- // exponent (e) is 5 chars long, rest is modulus (n)
490
- // const publicKey = publicKeyHex
491
- // assertProperKeyLength(publicKey, [2048, 3072, 4096])
492
- const exponent = publicKeyHex.slice(-5)
493
- const modulus = publicKeyHex.slice(0, -5)
494
- // const modulusBitLength = (modulus.length / 2) * 8
495
-
496
- // const alg = modulusBitLength === 2048 ? JoseSignatureAlgorithm.RS256 : modulusBitLength === 3072 ? JoseSignatureAlgorithm.RS384 : modulusBitLength === 4096 ? JoseSignatureAlgorithm.RS512 : undefined
497
- return {
498
- kty: 'RSA',
499
- n: hexToBase64(modulus, 'base64url'),
500
- e: hexToBase64(exponent, 'base64url'),
501
- // ...(alg && { alg }),
502
- }
473
+ const meta = opts?.key?.meta
474
+ if (meta?.publicKeyJwk || meta?.publicKeyPEM) {
475
+ if (meta?.publicKeyJwk) {
476
+ return meta.publicKeyJwk as JWK
477
+ }
478
+ const publicKeyPEM = meta?.publicKeyPEM ?? hexToPEM(publicKeyHex, 'public')
479
+ return PEMToJwk(publicKeyPEM, 'public') as JWK
480
+ }
481
+
482
+ // exponent (e) is 5 chars long, rest is modulus (n)
483
+ // const publicKey = publicKeyHex
484
+ // assertProperKeyLength(publicKey, [2048, 3072, 4096])
485
+ const exponent = publicKeyHex.slice(-5)
486
+ const modulus = publicKeyHex.slice(0, -5)
487
+ // const modulusBitLength = (modulus.length / 2) * 8
488
+
489
+ // const alg = modulusBitLength === 2048 ? JoseSignatureAlgorithm.RS256 : modulusBitLength === 3072 ? JoseSignatureAlgorithm.RS384 : modulusBitLength === 4096 ? JoseSignatureAlgorithm.RS512 : undefined
490
+ return {
491
+ kty: 'RSA',
492
+ n: hexToBase64(modulus, 'base64url'),
493
+ e: hexToBase64(exponent, 'base64url'),
494
+ // ...(alg && { alg }),
495
+ }
503
496
  }
504
497
 
505
498
  export const padLeft = (args: { data: string; size?: number; padString?: string }): string => {
506
- const {data} = args
507
- const size = args.size ?? 32
508
- const padString = args.padString ?? '0'
509
- if (data.length >= size) {
510
- return data
511
- }
499
+ const { data } = args
500
+ const size = args.size ?? 32
501
+ const padString = args.padString ?? '0'
502
+ if (data.length >= size) {
503
+ return data
504
+ }
512
505
 
513
- if (padString && padString.length === 0) {
514
- throw Error(`Pad string needs to have at least a length of 1`)
515
- }
516
- const length = padString.length
517
- return padString.repeat((size - data.length) / length) + data
506
+ if (padString && padString.length === 0) {
507
+ throw Error(`Pad string needs to have at least a length of 1`)
508
+ }
509
+ const length = padString.length
510
+ return padString.repeat((size - data.length) / length) + data
518
511
  }
519
512
 
520
513
  enum OIDType {
521
- Secp256k1,
522
- Secp256r1,
523
- Ed25519,
514
+ Secp256k1,
515
+ Secp256r1,
516
+ Ed25519,
524
517
  }
525
518
 
526
519
  const OID: Record<OIDType, Uint8Array> = {
527
- [OIDType.Secp256k1]: new Uint8Array([0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01]),
528
- [OIDType.Secp256r1]: new Uint8Array([0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]),
529
- [OIDType.Ed25519]: new Uint8Array([0x06, 0x03, 0x2b, 0x65, 0x70]),
520
+ [OIDType.Secp256k1]: new Uint8Array([0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01]),
521
+ [OIDType.Secp256r1]: new Uint8Array([0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]),
522
+ [OIDType.Ed25519]: new Uint8Array([0x06, 0x03, 0x2b, 0x65, 0x70]),
530
523
  }
531
524
 
532
525
  const compareUint8Arrays = (a: Uint8Array, b: Uint8Array): boolean => {
533
- if (a.length !== b.length) {
534
- return false
526
+ if (a.length !== b.length) {
527
+ return false
528
+ }
529
+ for (let i = 0; i < a.length; i++) {
530
+ if (a[i] !== b[i]) {
531
+ return false
535
532
  }
536
- for (let i = 0; i < a.length; i++) {
537
- if (a[i] !== b[i]) {
538
- return false
539
- }
540
- }
541
- return true
533
+ }
534
+ return true
542
535
  }
543
536
 
544
537
  const findSubarray = (haystack: Uint8Array, needle: Uint8Array): number => {
545
- for (let i = 0; i <= haystack.length - needle.length; i++) {
546
- if (compareUint8Arrays(haystack.subarray(i, i + needle.length), needle)) {
547
- return i
548
- }
538
+ for (let i = 0; i <= haystack.length - needle.length; i++) {
539
+ if (compareUint8Arrays(haystack.subarray(i, i + needle.length), needle)) {
540
+ return i
549
541
  }
550
- return -1
542
+ }
543
+ return -1
551
544
  }
552
545
 
553
546
  const getTargetOID = (keyType: TKeyType) => {
554
- switch (keyType) {
555
- case 'Secp256k1':
556
- return OID[OIDType.Secp256k1]
557
- case 'Secp256r1':
558
- return OID[OIDType.Secp256r1]
559
- case 'Ed25519':
560
- return OID[OIDType.Ed25519]
561
- default:
562
- throw new Error(`Unsupported key type: ${keyType}`)
563
- }
547
+ switch (keyType) {
548
+ case 'Secp256k1':
549
+ return OID[OIDType.Secp256k1]
550
+ case 'Secp256r1':
551
+ return OID[OIDType.Secp256r1]
552
+ case 'Ed25519':
553
+ return OID[OIDType.Ed25519]
554
+ default:
555
+ throw new Error(`Unsupported key type: ${keyType}`)
556
+ }
564
557
  }
565
558
 
566
559
  export const isAsn1Der = (key: Uint8Array): boolean => key[0] === 0x30
567
560
 
568
561
  export const asn1DerToRawPublicKey = (derKey: Uint8Array, keyType: TKeyType): Uint8Array => {
569
- if (!isAsn1Der(derKey)) {
570
- throw new Error('Invalid DER encoding: Expected to start with sequence tag')
571
- }
572
-
573
- let index = 2
574
- if (derKey[1] & 0x80) {
575
- const lengthBytesCount = derKey[1] & 0x7f
576
- index += lengthBytesCount
577
- }
578
- const targetOid = getTargetOID(keyType)
579
- const oidIndex = findSubarray(derKey, targetOid)
580
- if (oidIndex === -1) {
581
- throw new Error(`OID for ${keyType} not found in DER encoding`)
582
- }
583
-
584
- index = oidIndex + targetOid.length
585
-
586
- while (index < derKey.length && derKey[index] !== 0x03) {
587
- index++
588
- }
562
+ if (!isAsn1Der(derKey)) {
563
+ throw new Error('Invalid DER encoding: Expected to start with sequence tag')
564
+ }
565
+
566
+ let index = 2
567
+ if (derKey[1] & 0x80) {
568
+ const lengthBytesCount = derKey[1] & 0x7f
569
+ index += lengthBytesCount
570
+ }
571
+ const targetOid = getTargetOID(keyType)
572
+ const oidIndex = findSubarray(derKey, targetOid)
573
+ if (oidIndex === -1) {
574
+ throw new Error(`OID for ${keyType} not found in DER encoding`)
575
+ }
576
+
577
+ index = oidIndex + targetOid.length
578
+
579
+ while (index < derKey.length && derKey[index] !== 0x03) {
580
+ index++
581
+ }
589
582
 
590
- if (index >= derKey.length) {
591
- throw new Error('Invalid DER encoding: Bit string not found')
592
- }
583
+ if (index >= derKey.length) {
584
+ throw new Error('Invalid DER encoding: Bit string not found')
585
+ }
593
586
 
594
- // Skip the bit string tag (0x03) and length byte
595
- index += 2
587
+ // Skip the bit string tag (0x03) and length byte
588
+ index += 2
596
589
 
597
- // Skip the unused bits count byte
598
- index++
590
+ // Skip the unused bits count byte
591
+ index++
599
592
 
600
- return derKey.slice(index)
593
+ return derKey.slice(index)
601
594
  }
602
595
 
603
596
  export const isRawCompressedPublicKey = (key: Uint8Array): boolean => key.length === 33 && (key[0] === 0x02 || key[0] === 0x03)
604
597
 
605
598
  export const toRawCompressedHexPublicKey = (rawPublicKey: Uint8Array, keyType: TKeyType): string => {
606
- if (isRawCompressedPublicKey(rawPublicKey)) {
607
- return hexStringFromUint8Array(rawPublicKey)
608
- }
599
+ if (isRawCompressedPublicKey(rawPublicKey)) {
600
+ return hexStringFromUint8Array(rawPublicKey)
601
+ }
609
602
 
610
- if (keyType === 'Secp256k1' || keyType === 'Secp256r1') {
611
- if (rawPublicKey[0] === 0x04 && rawPublicKey.length === 65) {
612
- const xCoordinate = rawPublicKey.slice(1, 33)
613
- const yCoordinate = rawPublicKey.slice(33)
614
- const prefix = new Uint8Array([yCoordinate[31] % 2 === 0 ? 0x02 : 0x03])
615
- const resultKey = hexStringFromUint8Array(new Uint8Array([...prefix, ...xCoordinate]))
616
- logger.debug(`converted public key ${hexStringFromUint8Array(rawPublicKey)} to ${resultKey}`)
617
- return resultKey
618
- }
619
- return u8a.toString(rawPublicKey, 'base16')
620
- } else if (keyType === 'Ed25519') {
621
- // Ed25519 keys are always in compressed form
622
- return u8a.toString(rawPublicKey, 'base16')
603
+ if (keyType === 'Secp256k1' || keyType === 'Secp256r1') {
604
+ if (rawPublicKey[0] === 0x04 && rawPublicKey.length === 65) {
605
+ const xCoordinate = rawPublicKey.slice(1, 33)
606
+ const yCoordinate = rawPublicKey.slice(33)
607
+ const prefix = new Uint8Array([yCoordinate[31] % 2 === 0 ? 0x02 : 0x03])
608
+ const resultKey = hexStringFromUint8Array(new Uint8Array([...prefix, ...xCoordinate]))
609
+ logger.debug(`converted public key ${hexStringFromUint8Array(rawPublicKey)} to ${resultKey}`)
610
+ return resultKey
623
611
  }
612
+ return u8a.toString(rawPublicKey, 'base16')
613
+ } else if (keyType === 'Ed25519') {
614
+ // Ed25519 keys are always in compressed form
615
+ return u8a.toString(rawPublicKey, 'base16')
616
+ }
624
617
 
625
- throw new Error(`Unsupported key type: ${keyType}`)
618
+ throw new Error(`Unsupported key type: ${keyType}`)
626
619
  }
627
620
 
628
621
  export const hexStringFromUint8Array = (value: Uint8Array): string => u8a.toString(value, 'base16')
629
622
 
630
623
  export const signatureAlgorithmFromKey = async (args: SignatureAlgorithmFromKeyArgs): Promise<JoseSignatureAlgorithm> => {
631
- const {key} = args
632
- return signatureAlgorithmFromKeyType({type: key.type})
624
+ const { key } = args
625
+ return signatureAlgorithmFromKeyType({ type: key.type })
633
626
  }
634
627
 
635
628
  export const signatureAlgorithmFromKeyType = (args: SignatureAlgorithmFromKeyTypeArgs): JoseSignatureAlgorithm => {
636
- const {type} = args
637
- switch (type) {
638
- case 'Ed25519':
639
- case 'X25519':
640
- return JoseSignatureAlgorithm.EdDSA
641
- case 'Secp256r1':
642
- return JoseSignatureAlgorithm.ES256
643
- case 'Secp384r1':
644
- return JoseSignatureAlgorithm.ES384
645
- case 'Secp521r1':
646
- return JoseSignatureAlgorithm.ES512
647
- case 'Secp256k1':
648
- return JoseSignatureAlgorithm.ES256K
649
- default:
650
- throw new Error(`Key type '${type}' not supported`)
651
- }
629
+ const { type } = args
630
+ switch (type) {
631
+ case 'Ed25519':
632
+ case 'X25519':
633
+ return JoseSignatureAlgorithm.EdDSA
634
+ case 'Secp256r1':
635
+ return JoseSignatureAlgorithm.ES256
636
+ case 'Secp384r1':
637
+ return JoseSignatureAlgorithm.ES384
638
+ case 'Secp521r1':
639
+ return JoseSignatureAlgorithm.ES512
640
+ case 'Secp256k1':
641
+ return JoseSignatureAlgorithm.ES256K
642
+ default:
643
+ throw new Error(`Key type '${type}' not supported`)
644
+ }
652
645
  }
653
646
 
654
647
  // TODO improve this conversion for jwt and jsonld, not a fan of current structure
655
648
  export const keyTypeFromCryptographicSuite = (args: KeyTypeFromCryptographicSuiteArgs): TKeyType => {
656
- const {crv, kty, alg} = args
657
-
658
- switch (alg) {
659
- case 'RSASSA-PSS':
660
- case 'RS256':
661
- case 'RS384':
662
- case 'RS512':
663
- case 'PS256':
664
- case 'PS384':
665
- case 'PS512':
666
- return 'RSA'
667
- }
668
-
669
- switch (crv) {
670
- case 'EdDSA':
671
- case 'Ed25519':
672
- case 'Ed25519Signature2018':
673
- case 'Ed25519Signature2020':
674
- case 'JcsEd25519Signature2020':
675
- return 'Ed25519'
676
- case 'JsonWebSignature2020':
677
- case 'ES256':
678
- case 'ECDSA':
679
- case 'P-256':
680
- return 'Secp256r1'
681
- case 'ES384':
682
- case 'P-384':
683
- return 'Secp384r1'
684
- case 'ES512':
685
- case 'P-521':
686
- return 'Secp521r1'
687
- case 'EcdsaSecp256k1Signature2019':
688
- case 'secp256k1':
689
- case 'ES256K':
690
- return 'Secp256k1'
691
- }
692
- if (kty) {
693
- return kty as TKeyType
694
- }
695
-
696
-
697
- throw new Error(`Cryptographic suite '${crv}' not supported`)
649
+ const { crv, kty, alg } = args
650
+
651
+ switch (alg) {
652
+ case 'RSASSA-PSS':
653
+ case 'RS256':
654
+ case 'RS384':
655
+ case 'RS512':
656
+ case 'PS256':
657
+ case 'PS384':
658
+ case 'PS512':
659
+ return 'RSA'
660
+ }
661
+
662
+ switch (crv) {
663
+ case 'EdDSA':
664
+ case 'Ed25519':
665
+ case 'Ed25519Signature2018':
666
+ case 'Ed25519Signature2020':
667
+ case 'JcsEd25519Signature2020':
668
+ return 'Ed25519'
669
+ case 'JsonWebSignature2020':
670
+ case 'ES256':
671
+ case 'ECDSA':
672
+ case 'P-256':
673
+ return 'Secp256r1'
674
+ case 'ES384':
675
+ case 'P-384':
676
+ return 'Secp384r1'
677
+ case 'ES512':
678
+ case 'P-521':
679
+ return 'Secp521r1'
680
+ case 'EcdsaSecp256k1Signature2019':
681
+ case 'secp256k1':
682
+ case 'ES256K':
683
+ return 'Secp256k1'
684
+ }
685
+ if (kty) {
686
+ return kty as TKeyType
687
+ }
688
+
689
+ throw new Error(`Cryptographic suite '${crv}' not supported`)
698
690
  }
699
691
 
700
692
  export function removeNulls<T>(obj: T | any) {
701
- Object.keys(obj).forEach((key) => {
702
- if (obj[key] && typeof obj[key] === 'object') removeNulls(obj[key])
703
- else if (obj[key] == null) delete obj[key]
704
- })
705
- return obj
693
+ Object.keys(obj).forEach((key) => {
694
+ if (obj[key] && typeof obj[key] === 'object') removeNulls(obj[key])
695
+ else if (obj[key] == null) delete obj[key]
696
+ })
697
+ return obj
706
698
  }
707
699
 
708
-
709
700
  /**
710
701
  *
711
702
  */
712
703
  export async function verifyRawSignature({
713
- data,
714
- signature,
715
- key: inputKey,
716
- opts
717
- }: {
718
- data: Uint8Array
719
- signature: Uint8Array
720
- key: JWK
721
- opts?: {
722
- signatureAlg?: JoseSignatureAlgorithm
723
- }
704
+ data,
705
+ signature,
706
+ key: inputKey,
707
+ opts,
708
+ }: {
709
+ data: Uint8Array
710
+ signature: Uint8Array
711
+ key: JWK
712
+ opts?: {
713
+ signatureAlg?: JoseSignatureAlgorithm
714
+ }
724
715
  }) {
725
- /**
726
- * Converts a Base64URL-encoded JWK property to a BigInt.
727
- * @param jwkProp - The Base64URL-encoded string.
728
- * @returns The BigInt representation of the decoded value.
729
- */
730
- function jwkPropertyToBigInt(jwkProp: string): bigint {
731
- // Decode Base64URL to Uint8Array
732
- const byteArray = u8a.fromString(jwkProp, 'base64url');
733
-
734
- // Convert Uint8Array to hexadecimal string and then to BigInt
735
- const hex = u8a.toString(byteArray, 'hex');
736
- return BigInt(`0x${hex}`);
737
- }
738
-
739
- const key = removeNulls(inputKey)
740
- validateJwk(key)
741
- const keyType = keyTypeFromCryptographicSuite({crv: key.crv, kty: key.kty, alg: key.alg})
742
- const publicKeyHex = (await jwkToRawHexKey(key))
743
-
744
- // TODO: We really should look at the signature alg first if provided! From key type should be the last resort
745
- switch (keyType) {
746
- case 'Secp256k1':
747
- return secp256k1.verify(signature, data, publicKeyHex, {format: "compact", prehash: true})
748
- case 'Secp256r1':
749
- return p256.verify(signature, data, publicKeyHex, {format: "compact", prehash: true})
750
- case 'Secp384r1':
751
- return p384.verify(signature, data, publicKeyHex, {format: "compact", prehash: true})
752
- case 'Secp521r1':
753
- return p521.verify(signature, data, publicKeyHex, {format: "compact", prehash: true})
754
- case 'Ed25519':
755
- return ed25519.verify(signature, data, u8a.fromString(publicKeyHex, 'hex',))
756
- case "Bls12381G1":
757
- case "Bls12381G2":
758
- return bls12_381.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'))
759
- case "RSA": {
760
-
761
- const signatureAlgorithm = opts?.signatureAlg ?? JoseSignatureAlgorithm.PS256
762
- const hashAlg = signatureAlgorithm === (JoseSignatureAlgorithm.RS512 || JoseSignatureAlgorithm.PS512) ? sha512 : signatureAlgorithm === (JoseSignatureAlgorithm.RS384 || JoseSignatureAlgorithm.PS384) ? sha384 : sha256
763
- switch (signatureAlgorithm) {
764
- case JoseSignatureAlgorithm.RS256:
765
- return rsa.PKCS1_SHA256.verify({
766
- n: jwkPropertyToBigInt(key.n),
767
- e: jwkPropertyToBigInt(key.e),
768
- }, data, signature)
769
- case JoseSignatureAlgorithm.RS384:
770
- return rsa.PKCS1_SHA384.verify({
771
- n: jwkPropertyToBigInt(key.n),
772
- e: jwkPropertyToBigInt(key.e),
773
- }, data, signature)
774
- case JoseSignatureAlgorithm.RS512:
775
- return rsa.PKCS1_SHA512.verify({
776
- n: jwkPropertyToBigInt(key.n),
777
- e: jwkPropertyToBigInt(key.e),
778
- }, data, signature)
779
- case JoseSignatureAlgorithm.PS256:
780
- case JoseSignatureAlgorithm.PS384:
781
- case JoseSignatureAlgorithm.PS512:
782
- return rsa.PSS(hashAlg, rsa.mgf1(hashAlg)).verify({
783
- n: jwkPropertyToBigInt(key.n),
784
- e: jwkPropertyToBigInt(key.e),
785
- }, data, signature)
786
- }
787
-
788
- }
789
-
716
+ /**
717
+ * Converts a Base64URL-encoded JWK property to a BigInt.
718
+ * @param jwkProp - The Base64URL-encoded string.
719
+ * @returns The BigInt representation of the decoded value.
720
+ */
721
+ function jwkPropertyToBigInt(jwkProp: string): bigint {
722
+ // Decode Base64URL to Uint8Array
723
+ const byteArray = u8a.fromString(jwkProp, 'base64url')
724
+
725
+ // Convert Uint8Array to hexadecimal string and then to BigInt
726
+ const hex = u8a.toString(byteArray, 'hex')
727
+ return BigInt(`0x${hex}`)
728
+ }
729
+
730
+ const key = removeNulls(inputKey)
731
+ validateJwk(key)
732
+ const keyType = keyTypeFromCryptographicSuite({ crv: key.crv, kty: key.kty, alg: key.alg })
733
+ const publicKeyHex = await jwkToRawHexKey(key)
734
+
735
+ // TODO: We really should look at the signature alg first if provided! From key type should be the last resort
736
+ switch (keyType) {
737
+ case 'Secp256k1':
738
+ return secp256k1.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
739
+ case 'Secp256r1':
740
+ return p256.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
741
+ case 'Secp384r1':
742
+ return p384.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
743
+ case 'Secp521r1':
744
+ return p521.verify(signature, data, publicKeyHex, { format: 'compact', prehash: true })
745
+ case 'Ed25519':
746
+ return ed25519.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'))
747
+ case 'Bls12381G1':
748
+ case 'Bls12381G2':
749
+ return bls12_381.verify(signature, data, u8a.fromString(publicKeyHex, 'hex'))
750
+ case 'RSA': {
751
+ const signatureAlgorithm = opts?.signatureAlg ?? JoseSignatureAlgorithm.PS256
752
+ const hashAlg =
753
+ signatureAlgorithm === (JoseSignatureAlgorithm.RS512 || JoseSignatureAlgorithm.PS512)
754
+ ? sha512
755
+ : signatureAlgorithm === (JoseSignatureAlgorithm.RS384 || JoseSignatureAlgorithm.PS384)
756
+ ? sha384
757
+ : sha256
758
+ switch (signatureAlgorithm) {
759
+ case JoseSignatureAlgorithm.RS256:
760
+ return rsa.PKCS1_SHA256.verify(
761
+ {
762
+ n: jwkPropertyToBigInt(key.n),
763
+ e: jwkPropertyToBigInt(key.e),
764
+ },
765
+ data,
766
+ signature
767
+ )
768
+ case JoseSignatureAlgorithm.RS384:
769
+ return rsa.PKCS1_SHA384.verify(
770
+ {
771
+ n: jwkPropertyToBigInt(key.n),
772
+ e: jwkPropertyToBigInt(key.e),
773
+ },
774
+ data,
775
+ signature
776
+ )
777
+ case JoseSignatureAlgorithm.RS512:
778
+ return rsa.PKCS1_SHA512.verify(
779
+ {
780
+ n: jwkPropertyToBigInt(key.n),
781
+ e: jwkPropertyToBigInt(key.e),
782
+ },
783
+ data,
784
+ signature
785
+ )
786
+ case JoseSignatureAlgorithm.PS256:
787
+ case JoseSignatureAlgorithm.PS384:
788
+ case JoseSignatureAlgorithm.PS512:
789
+ return rsa.PSS(hashAlg, rsa.mgf1(hashAlg)).verify(
790
+ {
791
+ n: jwkPropertyToBigInt(key.n),
792
+ e: jwkPropertyToBigInt(key.e),
793
+ },
794
+ data,
795
+ signature
796
+ )
797
+ }
790
798
  }
791
- throw Error(`Unsupported key type for signature validation: ${keyType}`)
799
+ }
800
+ throw Error(`Unsupported key type for signature validation: ${keyType}`)
792
801
  }