solana-kms-signer 0.1.0 → 1.0.0
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/README.md +82 -27
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/kms/client.d.ts.map +1 -1
- package/dist/kms/client.js +1 -1
- package/dist/kms/client.js.map +1 -1
- package/dist/kms/signer.d.ts +2 -2
- package/dist/kms/signer.d.ts.map +1 -1
- package/dist/kms/signer.js +2 -2
- package/dist/kms/signer.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/publicKey.js.map +1 -1
- package/package.json +17 -16
- package/src/errors/index.test.ts +185 -170
- package/src/errors/index.ts +21 -12
- package/src/index.ts +10 -14
- package/src/kms/client.test.ts +318 -255
- package/src/kms/client.ts +100 -98
- package/src/kms/signer.test.ts +415 -396
- package/src/kms/signer.ts +205 -209
- package/src/types/index.ts +27 -27
- package/src/utils/publicKey.test.ts +178 -119
- package/src/utils/publicKey.ts +34 -34
package/src/kms/client.test.ts
CHANGED
|
@@ -1,22 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { KmsClient } from './client.js';
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
2
|
import { KmsClientError } from '../errors/index.js';
|
|
4
3
|
import type { KmsConfig } from '../types/index.js';
|
|
4
|
+
import { KmsClient } from './client.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Mock DER-encoded ED25519 public key matching AWS KMS GetPublicKey format.
|
|
8
8
|
* Reuses the same structure as publicKey.test.ts for consistency.
|
|
9
9
|
*/
|
|
10
10
|
const MOCK_DER_PUBLIC_KEY = new Uint8Array([
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
0x30,
|
|
12
|
+
0x2a, // SEQUENCE, 42 bytes
|
|
13
|
+
0x30,
|
|
14
|
+
0x05, // AlgorithmIdentifier SEQUENCE, 5 bytes
|
|
15
|
+
0x06,
|
|
16
|
+
0x03,
|
|
17
|
+
0x2b,
|
|
18
|
+
0x65,
|
|
19
|
+
0x70, // OID: 1.3.101.112 (Ed25519)
|
|
20
|
+
0x03,
|
|
21
|
+
0x21,
|
|
22
|
+
0x00, // BIT STRING, 33 bytes (32 + 1 padding)
|
|
23
|
+
// Mock 32-byte ED25519 public key
|
|
24
|
+
0x01,
|
|
25
|
+
0x02,
|
|
26
|
+
0x03,
|
|
27
|
+
0x04,
|
|
28
|
+
0x05,
|
|
29
|
+
0x06,
|
|
30
|
+
0x07,
|
|
31
|
+
0x08,
|
|
32
|
+
0x09,
|
|
33
|
+
0x0a,
|
|
34
|
+
0x0b,
|
|
35
|
+
0x0c,
|
|
36
|
+
0x0d,
|
|
37
|
+
0x0e,
|
|
38
|
+
0x0f,
|
|
39
|
+
0x10,
|
|
40
|
+
0x11,
|
|
41
|
+
0x12,
|
|
42
|
+
0x13,
|
|
43
|
+
0x14,
|
|
44
|
+
0x15,
|
|
45
|
+
0x16,
|
|
46
|
+
0x17,
|
|
47
|
+
0x18,
|
|
48
|
+
0x19,
|
|
49
|
+
0x1a,
|
|
50
|
+
0x1b,
|
|
51
|
+
0x1c,
|
|
52
|
+
0x1d,
|
|
53
|
+
0x1e,
|
|
54
|
+
0x1f,
|
|
55
|
+
0x20,
|
|
20
56
|
]);
|
|
21
57
|
|
|
22
58
|
/**
|
|
@@ -29,257 +65,284 @@ const MOCK_SIGNATURE = new Uint8Array(64).fill(0xab);
|
|
|
29
65
|
* Mock KMS configuration for testing
|
|
30
66
|
*/
|
|
31
67
|
const MOCK_KMS_CONFIG: KmsConfig = {
|
|
32
|
-
|
|
33
|
-
|
|
68
|
+
region: 'us-east-1',
|
|
69
|
+
keyId:
|
|
70
|
+
'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012',
|
|
34
71
|
};
|
|
35
72
|
|
|
36
73
|
// Mock AWS SDK client and commands
|
|
37
74
|
const mockSend = vi.fn();
|
|
38
75
|
|
|
39
76
|
vi.mock('@aws-sdk/client-kms', () => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
77
|
+
return {
|
|
78
|
+
KMSClient: class {
|
|
79
|
+
send = mockSend;
|
|
80
|
+
},
|
|
81
|
+
GetPublicKeyCommand: class {
|
|
82
|
+
constructor(public params: unknown) {}
|
|
83
|
+
},
|
|
84
|
+
SignCommand: class {
|
|
85
|
+
constructor(public params: unknown) {}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
51
88
|
});
|
|
52
89
|
|
|
53
90
|
describe('KmsClient', () => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
91
|
+
beforeEach(() => {
|
|
92
|
+
// Reset mock state before each test
|
|
93
|
+
mockSend.mockReset();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('Happy Path', () => {
|
|
97
|
+
it('should successfully retrieve public key as DER-encoded Uint8Array', async () => {
|
|
98
|
+
// given: KmsClient instance and mocked successful KMS response
|
|
99
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
100
|
+
mockSend.mockResolvedValueOnce({
|
|
101
|
+
PublicKey: MOCK_DER_PUBLIC_KEY,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// when: Getting public key from KMS
|
|
105
|
+
const publicKey = await client.getPublicKey();
|
|
106
|
+
|
|
107
|
+
// then: Should return DER-encoded public key as Uint8Array
|
|
108
|
+
expect(publicKey).toBeInstanceOf(Uint8Array);
|
|
109
|
+
expect(publicKey.length).toBe(MOCK_DER_PUBLIC_KEY.length);
|
|
110
|
+
expect(publicKey).toEqual(MOCK_DER_PUBLIC_KEY);
|
|
111
|
+
|
|
112
|
+
// then: Should have called KMS with correct parameters
|
|
113
|
+
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should successfully sign message and return 64-byte signature', async () => {
|
|
117
|
+
// given: KmsClient instance and message to sign
|
|
118
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
119
|
+
const message = new Uint8Array([1, 2, 3, 4, 5]);
|
|
120
|
+
|
|
121
|
+
mockSend.mockResolvedValueOnce({
|
|
122
|
+
Signature: MOCK_SIGNATURE,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// when: Signing message with KMS
|
|
126
|
+
const signature = await client.sign(message);
|
|
127
|
+
|
|
128
|
+
// then: Should return 64-byte signature
|
|
129
|
+
expect(signature).toBeInstanceOf(Uint8Array);
|
|
130
|
+
expect(signature.length).toBe(64);
|
|
131
|
+
expect(signature).toEqual(MOCK_SIGNATURE);
|
|
132
|
+
|
|
133
|
+
// then: Should have called KMS with correct SigningAlgorithm
|
|
134
|
+
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
135
|
+
const signCommand = mockSend.mock.calls[0][0];
|
|
136
|
+
expect(signCommand.params.SigningAlgorithm).toBe('ED25519_SHA_512');
|
|
137
|
+
expect(signCommand.params.MessageType).toBe('RAW');
|
|
138
|
+
expect(signCommand.params.Message).toEqual(message);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('Failure Paths', () => {
|
|
143
|
+
it('should throw KmsClientError when getPublicKey receives AccessDeniedException', async () => {
|
|
144
|
+
// given: KmsClient and mocked AccessDeniedException
|
|
145
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
146
|
+
const awsError = new Error(
|
|
147
|
+
'User: arn:aws:iam::123456789012:user/test is not authorized',
|
|
148
|
+
);
|
|
149
|
+
awsError.name = 'AccessDeniedException';
|
|
150
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
151
|
+
|
|
152
|
+
// when: Getting public key with insufficient permissions
|
|
153
|
+
const getPublicKeyPromise = client.getPublicKey();
|
|
154
|
+
|
|
155
|
+
// then: Should throw KmsClientError with cause
|
|
156
|
+
await expect(getPublicKeyPromise).rejects.toThrow(KmsClientError);
|
|
157
|
+
await expect(getPublicKeyPromise).rejects.toThrow(
|
|
158
|
+
'Failed to get public key from KMS',
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// then: Verify cause is preserved in error
|
|
162
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
163
|
+
try {
|
|
164
|
+
await client.getPublicKey();
|
|
165
|
+
} catch (error) {
|
|
166
|
+
expect(error).toBeInstanceOf(KmsClientError);
|
|
167
|
+
expect((error as KmsClientError).cause).toBe(awsError);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should throw KmsClientError when getPublicKey receives KeyNotFoundException', async () => {
|
|
172
|
+
// given: KmsClient and mocked KeyNotFoundException
|
|
173
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
174
|
+
const awsError = new Error(
|
|
175
|
+
'Key arn:aws:kms:us-east-1:123456789012:key/nonexistent is not found',
|
|
176
|
+
);
|
|
177
|
+
awsError.name = 'KeyNotFoundException';
|
|
178
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
179
|
+
|
|
180
|
+
// when: Getting public key for non-existent key
|
|
181
|
+
const getPublicKeyPromise = client.getPublicKey();
|
|
182
|
+
|
|
183
|
+
// then: Should throw KmsClientError with cause
|
|
184
|
+
await expect(getPublicKeyPromise).rejects.toThrow(KmsClientError);
|
|
185
|
+
await expect(getPublicKeyPromise).rejects.toThrow(
|
|
186
|
+
'Failed to get public key from KMS',
|
|
187
|
+
);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should throw KmsClientError when getPublicKey receives InvalidKeyUsageException', async () => {
|
|
191
|
+
// given: KmsClient and mocked InvalidKeyUsageException
|
|
192
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
193
|
+
const awsError = new Error(
|
|
194
|
+
'The request is not valid for the specified key usage',
|
|
195
|
+
);
|
|
196
|
+
awsError.name = 'InvalidKeyUsageException';
|
|
197
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
198
|
+
|
|
199
|
+
// when: Getting public key for key with wrong usage (e.g., ENCRYPT_DECRYPT)
|
|
200
|
+
const getPublicKeyPromise = client.getPublicKey();
|
|
201
|
+
|
|
202
|
+
// then: Should throw KmsClientError with cause
|
|
203
|
+
await expect(getPublicKeyPromise).rejects.toThrow(KmsClientError);
|
|
204
|
+
await expect(getPublicKeyPromise).rejects.toThrow(
|
|
205
|
+
'Failed to get public key from KMS',
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should throw KmsClientError when sign receives ThrottlingException', async () => {
|
|
210
|
+
// given: KmsClient and mocked ThrottlingException
|
|
211
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
212
|
+
const message = new Uint8Array([1, 2, 3]);
|
|
213
|
+
const awsError = new Error('Rate exceeded');
|
|
214
|
+
awsError.name = 'ThrottlingException';
|
|
215
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
216
|
+
|
|
217
|
+
// when: Signing message with rate limit exceeded
|
|
218
|
+
const signPromise = client.sign(message);
|
|
219
|
+
|
|
220
|
+
// then: Should throw KmsClientError with cause
|
|
221
|
+
await expect(signPromise).rejects.toThrow(KmsClientError);
|
|
222
|
+
await expect(signPromise).rejects.toThrow(
|
|
223
|
+
'Failed to sign message with KMS',
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('should throw KmsClientError when sign receives KeyUnavailableException', async () => {
|
|
228
|
+
// given: KmsClient and mocked KeyUnavailableException
|
|
229
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
230
|
+
const message = new Uint8Array([1, 2, 3]);
|
|
231
|
+
const awsError = new Error(
|
|
232
|
+
'The request was rejected because the specified KMS key is not available',
|
|
233
|
+
);
|
|
234
|
+
awsError.name = 'KeyUnavailableException';
|
|
235
|
+
mockSend.mockRejectedValueOnce(awsError);
|
|
236
|
+
|
|
237
|
+
// when: Signing message with unavailable key
|
|
238
|
+
const signPromise = client.sign(message);
|
|
239
|
+
|
|
240
|
+
// then: Should throw KmsClientError with cause
|
|
241
|
+
await expect(signPromise).rejects.toThrow(KmsClientError);
|
|
242
|
+
await expect(signPromise).rejects.toThrow(
|
|
243
|
+
'Failed to sign message with KMS',
|
|
244
|
+
);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should handle signing empty message appropriately', async () => {
|
|
248
|
+
// given: KmsClient and empty message
|
|
249
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
250
|
+
const emptyMessage = new Uint8Array(0);
|
|
251
|
+
|
|
252
|
+
mockSend.mockResolvedValueOnce({
|
|
253
|
+
Signature: MOCK_SIGNATURE,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// when: Signing empty message
|
|
257
|
+
const signature = await client.sign(emptyMessage);
|
|
258
|
+
|
|
259
|
+
// then: Should successfully return signature (AWS KMS allows empty messages)
|
|
260
|
+
expect(signature).toBeInstanceOf(Uint8Array);
|
|
261
|
+
expect(signature.length).toBe(64);
|
|
262
|
+
expect(mockSend).toHaveBeenCalledTimes(1);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should throw KmsClientError on network timeout simulation', async () => {
|
|
266
|
+
// given: KmsClient and mocked network timeout
|
|
267
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
268
|
+
const message = new Uint8Array([1, 2, 3]);
|
|
269
|
+
const timeoutError = new Error('Connection timeout after 10000ms');
|
|
270
|
+
timeoutError.name = 'TimeoutError';
|
|
271
|
+
mockSend.mockRejectedValueOnce(timeoutError);
|
|
272
|
+
|
|
273
|
+
// when: Signing message with network timeout
|
|
274
|
+
const signPromise = client.sign(message);
|
|
275
|
+
|
|
276
|
+
// then: Should throw KmsClientError with timeout cause
|
|
277
|
+
await expect(signPromise).rejects.toThrow(KmsClientError);
|
|
278
|
+
await expect(signPromise).rejects.toThrow(
|
|
279
|
+
'Failed to sign message with KMS',
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// then: Verify cause is preserved in error
|
|
283
|
+
mockSend.mockRejectedValueOnce(timeoutError);
|
|
284
|
+
try {
|
|
285
|
+
await client.sign(message);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
expect(error).toBeInstanceOf(KmsClientError);
|
|
288
|
+
expect((error as KmsClientError).cause).toBe(timeoutError);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('Edge Cases', () => {
|
|
294
|
+
it('should throw KmsClientError when getPublicKey response has null PublicKey', async () => {
|
|
295
|
+
// given: KmsClient and response with null PublicKey
|
|
296
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
297
|
+
mockSend.mockResolvedValueOnce({
|
|
298
|
+
PublicKey: null,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// when: Getting public key with null response
|
|
302
|
+
const getPublicKeyPromise = client.getPublicKey();
|
|
303
|
+
|
|
304
|
+
// then: Should throw KmsClientError
|
|
305
|
+
await expect(getPublicKeyPromise).rejects.toThrow(KmsClientError);
|
|
306
|
+
await expect(getPublicKeyPromise).rejects.toThrow(
|
|
307
|
+
'GetPublicKey response missing PublicKey field',
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should throw KmsClientError when sign response has null Signature', async () => {
|
|
312
|
+
// given: KmsClient and response with null Signature
|
|
313
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
314
|
+
const message = new Uint8Array([1, 2, 3]);
|
|
315
|
+
mockSend.mockResolvedValueOnce({
|
|
316
|
+
Signature: null,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// when: Signing message with null response
|
|
320
|
+
const signPromise = client.sign(message);
|
|
321
|
+
|
|
322
|
+
// then: Should throw KmsClientError
|
|
323
|
+
await expect(signPromise).rejects.toThrow(KmsClientError);
|
|
324
|
+
await expect(signPromise).rejects.toThrow(
|
|
325
|
+
'Sign response missing Signature field',
|
|
326
|
+
);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should throw KmsClientError when signature length is not 64 bytes', async () => {
|
|
330
|
+
// given: KmsClient and invalid signature length (e.g., 32 bytes)
|
|
331
|
+
const client = new KmsClient(MOCK_KMS_CONFIG);
|
|
332
|
+
const message = new Uint8Array([1, 2, 3]);
|
|
333
|
+
const invalidSignature = new Uint8Array(32); // Wrong length
|
|
334
|
+
mockSend.mockResolvedValueOnce({
|
|
335
|
+
Signature: invalidSignature,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// when: Receiving signature with wrong length
|
|
339
|
+
const signPromise = client.sign(message);
|
|
340
|
+
|
|
341
|
+
// then: Should throw KmsClientError with length validation message
|
|
342
|
+
await expect(signPromise).rejects.toThrow(KmsClientError);
|
|
343
|
+
await expect(signPromise).rejects.toThrow(
|
|
344
|
+
'Invalid signature length: expected 64 bytes, got 32 bytes',
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
});
|
|
285
348
|
});
|