@sphereon/ssi-sdk.kms-rest 0.36.1-next.50 → 0.36.1-next.70

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.
@@ -1,4 +1,14 @@
1
- import { calculateJwkThumbprint, toJwk, x25519PublicHexFromPrivateHex, type X509Opts } from '@sphereon/ssi-sdk-ext.key-utils'
1
+ import {
2
+ calculateJwkThumbprint,
3
+ isHashString,
4
+ joseAlgorithmToDigest,
5
+ shaHasher,
6
+ signatureAlgorithmFromKeyType,
7
+ signatureAlgorithmToJoseAlgorithm,
8
+ toJwk,
9
+ x25519PublicHexFromPrivateHex,
10
+ type X509Opts,
11
+ } from '@sphereon/ssi-sdk-ext.key-utils'
2
12
  import { hexToPEM, jwkToPEM, pemCertChainTox5c, PEMToHex, PEMToJwk } from '@sphereon/ssi-sdk-ext.x509-utils'
3
13
  import type { ManagedKeyInfo as RestManagedKeyInfo } from '@sphereon/ssi-sdk.kms-rest-client'
4
14
  import {
@@ -27,13 +37,17 @@ interface KeyManagementSystemOptions {
27
37
  applicationId: string
28
38
  baseUrl: string
29
39
  providerId?: string
40
+ tenantId?: string
41
+ userId?: string
30
42
  authOpts?: RestClientAuthenticationOpts
31
43
  }
32
44
 
33
45
  export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
34
46
  private client: KmsRestClient
35
47
  private readonly id: string
36
- private providerId: string | undefined
48
+ private readonly providerId: string | undefined
49
+ private readonly tenantId: string | undefined
50
+ private readonly userId: string | undefined
37
51
 
38
52
  constructor(options: KeyManagementSystemOptions) {
39
53
  super()
@@ -45,18 +59,26 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
45
59
 
46
60
  this.id = options.applicationId
47
61
  this.providerId = options.providerId
62
+ this.tenantId = options.tenantId
63
+ this.userId = options.userId
48
64
  this.client = new KmsRestClient(config)
49
65
  }
50
66
 
51
67
  async createKey(args: CreateKeyArgs): Promise<ManagedKeyInfo> {
52
68
  const { type, meta } = args
53
69
 
54
- const signatureAlgorithm = this.mapKeyTypeToSignatureAlgorithm(type)
70
+ const joseAlg = signatureAlgorithmFromKeyType({
71
+ type,
72
+ algorithms: meta?.algorithms as string[] | undefined,
73
+ })
74
+ const signatureAlgorithm = this.mapJoseToRestSignatureAlgorithm(joseAlg)
55
75
  const options = {
56
76
  use: meta && 'keyUsage' in meta ? this.mapKeyUsage(meta.keyUsage) : JwkUse.Sig,
57
77
  alg: signatureAlgorithm,
58
78
  keyOperations: meta && meta.keyOperations ? this.mapKeyOperations(meta.keyOperations as string[]) : [KeyOperations.Sign],
59
79
  ...(meta && 'keyAlias' in meta && meta.keyAlias ? { alias: meta.keyAlias } : {}),
80
+ ...(this.tenantId && { tenantId: this.tenantId }),
81
+ ...(this.userId && { userId: this.userId }),
60
82
  }
61
83
 
62
84
  const key = this.providerId
@@ -68,7 +90,7 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
68
90
 
69
91
  const jwk = {
70
92
  ...key.keyPair.jose.publicJwk,
71
- alg: key.keyPair.jose.publicJwk.alg ? this.mapJoseAlgorithm(key.keyPair.jose.publicJwk.alg) : undefined,
93
+ alg: key.keyPair.jose.publicJwk.alg ? signatureAlgorithmToJoseAlgorithm(key.keyPair.jose.publicJwk.alg) : undefined,
72
94
  } satisfies JWK
73
95
 
74
96
  const kid = key.keyPair.kid ?? key.keyPair.jose.publicJwk.kid
@@ -85,7 +107,7 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
85
107
  algorithms: [key.keyPair.jose.publicJwk.alg ?? 'PS256'],
86
108
  jwkThumbprint: calculateJwkThumbprint({
87
109
  jwk,
88
- digestAlgorithm: this.signatureAlgorithmToDigestAlgorithm(signatureAlgorithm),
110
+ digestAlgorithm: jwk.alg ? joseAlgorithmToDigest(jwk.alg) : 'sha256',
89
111
  }),
90
112
  },
91
113
  publicKeyHex: Buffer.from(key.keyPair.jose.publicJwk.toString(), 'utf8').toString('base64'),
@@ -94,15 +116,20 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
94
116
 
95
117
  async importKey(args: ImportKeyArgs): Promise<ManagedKeyInfo> {
96
118
  const { type } = args
97
- const signatureAlgorithm = this.mapKeyTypeToSignatureAlgorithm(type)
98
119
  const importKey = this.mapImportKey(args)
99
120
 
100
121
  const result = this.providerId
101
122
  ? await this.client.methods.kmsClientProviderStoreKey({
102
123
  ...importKey.key,
103
124
  providerId: this.providerId,
125
+ ...(this.tenantId && { tenantId: this.tenantId }),
126
+ ...(this.userId && { userId: this.userId }),
127
+ })
128
+ : await this.client.methods.kmsClientStoreKey({
129
+ ...importKey.key,
130
+ ...(this.tenantId && { tenantId: this.tenantId }),
131
+ ...(this.userId && { userId: this.userId }),
104
132
  })
105
- : await this.client.methods.kmsClientStoreKey(importKey.key)
106
133
 
107
134
  return {
108
135
  kid: importKey.kid,
@@ -113,7 +140,7 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
113
140
  algorithms: [result.keyInfo.key.alg ?? 'PS256'],
114
141
  jwkThumbprint: calculateJwkThumbprint({
115
142
  jwk: importKey.publicKeyJwk,
116
- digestAlgorithm: this.signatureAlgorithmToDigestAlgorithm(signatureAlgorithm),
143
+ digestAlgorithm: importKey.publicKeyJwk.alg ? joseAlgorithmToDigest(importKey.publicKeyJwk.alg) : 'sha256',
117
144
  }),
118
145
  },
119
146
  publicKeyHex: Buffer.from(result.keyInfo.key.toString(), 'utf8').toString('base64'),
@@ -127,14 +154,27 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
127
154
  ? await this.client.methods.kmsClientProviderDeleteKey({
128
155
  aliasOrKid: kid,
129
156
  providerId: this.providerId,
157
+ ...(this.tenantId && { tenantId: this.tenantId }),
158
+ ...(this.userId && { userId: this.userId }),
159
+ })
160
+ : await this.client.methods.kmsClientDeleteKey({
161
+ aliasOrKid: kid,
162
+ ...(this.tenantId && { tenantId: this.tenantId }),
163
+ ...(this.userId && { userId: this.userId }),
130
164
  })
131
- : await this.client.methods.kmsClientDeleteKey({ aliasOrKid: kid })
132
165
  }
133
166
 
134
167
  async listKeys(): Promise<ManagedKeyInfo[]> {
135
168
  const keys = this.providerId
136
- ? await this.client.methods.kmsClientProviderListKeys({ providerId: this.providerId })
137
- : await this.client.methods.kmsClientListKeys()
169
+ ? await this.client.methods.kmsClientProviderListKeys({
170
+ providerId: this.providerId,
171
+ ...(this.tenantId && { tenantId: this.tenantId }),
172
+ ...(this.userId && { userId: this.userId }),
173
+ })
174
+ : await this.client.methods.kmsClientListKeys({
175
+ ...(this.tenantId && { tenantId: this.tenantId }),
176
+ ...(this.userId && { userId: this.userId }),
177
+ })
138
178
 
139
179
  const restKeys = ListKeysResponseToJSONTyped(keys, false).keyInfos
140
180
 
@@ -163,7 +203,7 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
163
203
  jwk,
164
204
  jwkThumbprint: calculateJwkThumbprint({
165
205
  jwk: jwk as JWK,
166
- digestAlgorithm: restKey.signatureAlgorithm ? this.signatureAlgorithmToDigestAlgorithm(restKey.signatureAlgorithm) : 'sha256',
206
+ digestAlgorithm: restKey.key.alg ? joseAlgorithmToDigest(restKey.key.alg) : 'sha256',
167
207
  }),
168
208
  alias: restKey.alias,
169
209
  providerId: restKey.providerId,
@@ -195,35 +235,84 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
195
235
  }
196
236
 
197
237
  async sign(args: SignArgs): Promise<string> {
198
- const { keyRef, data } = args
238
+ const { keyRef, data, algorithm = 'SHA-256' } = args
199
239
  const key = this.providerId
200
240
  ? await this.client.methods.kmsClientProviderGetKey({
201
241
  aliasOrKid: keyRef.kid,
202
242
  providerId: this.providerId,
243
+ ...(this.tenantId && { tenantId: this.tenantId }),
244
+ ...(this.userId && { userId: this.userId }),
245
+ })
246
+ : await this.client.methods.kmsClientGetKey({
247
+ aliasOrKid: keyRef.kid,
248
+ ...(this.tenantId && { tenantId: this.tenantId }),
249
+ ...(this.userId && { userId: this.userId }),
203
250
  })
204
- : await this.client.methods.kmsClientGetKey({ aliasOrKid: keyRef.kid })
251
+
252
+ // Check if this is an EdDSA/Ed25519 key - these algorithms MUST sign the raw message, not a hash
253
+ const keyAlg = key.keyInfo.key.alg
254
+ const isEdDSA = keyAlg === 'EdDSA' || keyAlg === 'ED25519' || key.keyInfo.key.crv === 'Ed25519'
255
+
256
+ let dataToBeSigned: Uint8Array
257
+ if (isEdDSA) {
258
+ // EdDSA signatures are computed over the raw message (PureEdDSA)
259
+ // The algorithm internally handles hashing with SHA-512
260
+ dataToBeSigned = data
261
+ } else {
262
+ // For other algorithms (RSA, ECDSA), hash the data before signing
263
+ // with remote signing we are not going to send the whole data over the network, we need to hash it (unless we already get a hash)
264
+ dataToBeSigned = isHashString(data)
265
+ ? data
266
+ : shaHasher(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength), algorithm)
267
+ }
205
268
 
206
269
  const signingResult = await this.client.methods.kmsClientCreateRawSignature({
207
270
  keyInfo: key.keyInfo,
208
- input: toString(data, 'base64'),
271
+ input: toString(dataToBeSigned, 'base64'),
272
+ ...(this.tenantId && { tenantId: this.tenantId }),
273
+ ...(this.userId && { userId: this.userId }),
209
274
  })
210
275
 
211
276
  return signingResult.signature
212
277
  }
213
278
 
214
279
  async verify(args: VerifyArgs): Promise<boolean> {
215
- const { keyRef, data, signature } = args
280
+ const { keyRef, data, signature, algorithm = 'SHA-256' } = args
216
281
  const key = this.providerId
217
282
  ? await this.client.methods.kmsClientProviderGetKey({
218
283
  aliasOrKid: keyRef.kid,
219
284
  providerId: this.providerId,
285
+ ...(this.tenantId && { tenantId: this.tenantId }),
286
+ ...(this.userId && { userId: this.userId }),
287
+ })
288
+ : await this.client.methods.kmsClientGetKey({
289
+ aliasOrKid: keyRef.kid,
290
+ ...(this.tenantId && { tenantId: this.tenantId }),
291
+ ...(this.userId && { userId: this.userId }),
220
292
  })
221
- : await this.client.methods.kmsClientGetKey({ aliasOrKid: keyRef.kid })
293
+
294
+ // Check if this is an EdDSA/Ed25519 key - these algorithms MUST verify the raw message, not a hash
295
+ const keyAlg = key.keyInfo.key.alg
296
+ const isEdDSA = keyAlg === 'EdDSA' || keyAlg === 'ED25519' || key.keyInfo.key.crv === 'Ed25519'
297
+
298
+ let dataToBeVerified: Uint8Array
299
+ if (isEdDSA) {
300
+ // EdDSA signatures are verified over the raw message (PureEdDSA)
301
+ dataToBeVerified = data
302
+ } else {
303
+ // For other algorithms (RSA, ECDSA), hash the data before verifying
304
+ // with remote signing we are not going to send the whole data over the network, we need to hash it (unless we already get a hash)
305
+ dataToBeVerified = isHashString(data)
306
+ ? data
307
+ : shaHasher(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength), algorithm)
308
+ }
222
309
 
223
310
  const verification = await this.client.methods.kmsClientIsValidRawSignature({
224
311
  keyInfo: key.keyInfo,
225
- input: toString(data, 'base64'),
312
+ input: toString(dataToBeVerified, 'base64'),
226
313
  signature,
314
+ ...(this.tenantId && { tenantId: this.tenantId }),
315
+ ...(this.userId && { userId: this.userId }),
227
316
  })
228
317
 
229
318
  return verification.isValid
@@ -233,23 +322,6 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
233
322
  throw new Error('sharedSecret is not implemented for REST KMS.')
234
323
  }
235
324
 
236
- private signatureAlgorithmToDigestAlgorithm = (signatureAlgorithm: SignatureAlgorithm): 'sha256' | 'sha512' => {
237
- switch (signatureAlgorithm) {
238
- case SignatureAlgorithm.EcdsaSha256:
239
- case SignatureAlgorithm.RsaSsaPssSha256Mgf1:
240
- case SignatureAlgorithm.EckaDhSha256:
241
- case SignatureAlgorithm.HmacSha256:
242
- case SignatureAlgorithm.Es256K:
243
- return 'sha256'
244
- case SignatureAlgorithm.EcdsaSha512:
245
- case SignatureAlgorithm.HmacSha512:
246
- case SignatureAlgorithm.RsaSsaPssSha512Mgf1:
247
- return 'sha512'
248
- default:
249
- throw new Error(`Signature algorithm ${signatureAlgorithm} is not supported by REST KMS`)
250
- }
251
- }
252
-
253
325
  private mapKeyUsage = (usage: string): JwkUse => {
254
326
  switch (usage) {
255
327
  case 'sig':
@@ -261,53 +333,32 @@ export class RestKeyManagementSystem extends AbstractKeyManagementSystem {
261
333
  }
262
334
  }
263
335
 
264
- private mapKeyTypeToSignatureAlgorithm = (type: TKeyType): SignatureAlgorithm => {
265
- switch (type) {
266
- case 'Secp256r1':
267
- return SignatureAlgorithm.EcdsaSha256
268
- case 'RSA':
269
- return SignatureAlgorithm.RsaSsaPssSha256Mgf1
270
- case 'X25519':
271
- return SignatureAlgorithm.EckaDhSha256
272
- default:
273
- throw new Error(`Key type ${type} is not supported by REST KMS`)
274
- }
275
- }
276
-
277
- private mapJoseAlgorithm = (alg: string): JoseSignatureAlgorithm => {
336
+ private mapJoseToRestSignatureAlgorithm = (alg: JoseSignatureAlgorithm): SignatureAlgorithm => {
278
337
  switch (alg) {
279
- case 'RS256':
280
- return JoseSignatureAlgorithm.RS256
281
- case 'RS384':
282
- return JoseSignatureAlgorithm.RS384
283
- case 'RS512':
284
- return JoseSignatureAlgorithm.RS512
285
- case 'ES256':
286
- return JoseSignatureAlgorithm.ES256
287
- case 'ES256K':
288
- return JoseSignatureAlgorithm.ES256K
289
- case 'ES384':
290
- return JoseSignatureAlgorithm.ES384
291
- case 'ES512':
292
- return JoseSignatureAlgorithm.ES512
293
- case 'EdDSA':
294
- return JoseSignatureAlgorithm.EdDSA
295
- case 'HS256':
296
- return JoseSignatureAlgorithm.HS256
297
- case 'HS384':
298
- return JoseSignatureAlgorithm.HS384
299
- case 'HS512':
300
- return JoseSignatureAlgorithm.HS512
301
- case 'PS256':
302
- return JoseSignatureAlgorithm.PS256
303
- case 'PS384':
304
- return JoseSignatureAlgorithm.PS384
305
- case 'PS512':
306
- return JoseSignatureAlgorithm.PS512
307
- case 'none':
308
- return JoseSignatureAlgorithm.none
338
+ case JoseSignatureAlgorithm.RS256:
339
+ return SignatureAlgorithm.RsaSha256
340
+ case JoseSignatureAlgorithm.RS384:
341
+ return SignatureAlgorithm.RsaSha384
342
+ case JoseSignatureAlgorithm.RS512:
343
+ return SignatureAlgorithm.RsaSha512
344
+ case JoseSignatureAlgorithm.PS256:
345
+ return SignatureAlgorithm.RsaSsaPssSha256Mgf1
346
+ case JoseSignatureAlgorithm.PS384:
347
+ return SignatureAlgorithm.RsaSsaPssSha384Mgf1
348
+ case JoseSignatureAlgorithm.PS512:
349
+ return SignatureAlgorithm.RsaSsaPssSha512Mgf1
350
+ case JoseSignatureAlgorithm.ES256:
351
+ return SignatureAlgorithm.EcdsaSha256
352
+ case JoseSignatureAlgorithm.ES384:
353
+ return SignatureAlgorithm.EcdsaSha384
354
+ case JoseSignatureAlgorithm.ES512:
355
+ return SignatureAlgorithm.EcdsaSha512
356
+ case JoseSignatureAlgorithm.ES256K:
357
+ return SignatureAlgorithm.Es256K
358
+ case JoseSignatureAlgorithm.EdDSA:
359
+ return SignatureAlgorithm.Ed25519
309
360
  default:
310
- throw new Error(`Signature algorithm ${alg} is not supported by REST KMS`)
361
+ throw new Error(`JOSE algorithm ${alg} not supported by REST KMS`)
311
362
  }
312
363
  }
313
364
 
@@ -15,6 +15,7 @@ export type CreateKeyArgs = {
15
15
  export type SignArgs = {
16
16
  keyRef: Pick<IKey, 'kid'>
17
17
  data: Uint8Array
18
+ algorithm?: string
18
19
  [x: string]: any
19
20
  }
20
21
 
@@ -22,6 +23,7 @@ export type VerifyArgs = {
22
23
  keyRef: Pick<IKey, 'kid'>
23
24
  data: Uint8Array
24
25
  signature: string
26
+ algorithm?: string
25
27
  [x: string]: any
26
28
  }
27
29