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
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { extractEd25519PublicKey } from './publicKey.js';
|
|
2
1
|
import { PublicKeyExtractionError } from '../errors/index.js';
|
|
2
|
+
import { extractEd25519PublicKey } from './publicKey.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Mock DER-encoded ED25519 public key matching AWS KMS GetPublicKey format.
|
|
@@ -11,125 +11,184 @@ import { PublicKeyExtractionError } from '../errors/index.js';
|
|
|
11
11
|
* - [32 bytes]: Mock public key data
|
|
12
12
|
*/
|
|
13
13
|
const MOCK_DER_PUBLIC_KEY = new Uint8Array([
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
0x30,
|
|
15
|
+
0x2a, // SEQUENCE, 42 bytes
|
|
16
|
+
0x30,
|
|
17
|
+
0x05, // AlgorithmIdentifier SEQUENCE, 5 bytes
|
|
18
|
+
0x06,
|
|
19
|
+
0x03,
|
|
20
|
+
0x2b,
|
|
21
|
+
0x65,
|
|
22
|
+
0x70, // OID: 1.3.101.112 (Ed25519)
|
|
23
|
+
0x03,
|
|
24
|
+
0x21,
|
|
25
|
+
0x00, // BIT STRING, 33 bytes (32 + 1 padding)
|
|
26
|
+
// Mock 32-byte ED25519 public key
|
|
27
|
+
0x01,
|
|
28
|
+
0x02,
|
|
29
|
+
0x03,
|
|
30
|
+
0x04,
|
|
31
|
+
0x05,
|
|
32
|
+
0x06,
|
|
33
|
+
0x07,
|
|
34
|
+
0x08,
|
|
35
|
+
0x09,
|
|
36
|
+
0x0a,
|
|
37
|
+
0x0b,
|
|
38
|
+
0x0c,
|
|
39
|
+
0x0d,
|
|
40
|
+
0x0e,
|
|
41
|
+
0x0f,
|
|
42
|
+
0x10,
|
|
43
|
+
0x11,
|
|
44
|
+
0x12,
|
|
45
|
+
0x13,
|
|
46
|
+
0x14,
|
|
47
|
+
0x15,
|
|
48
|
+
0x16,
|
|
49
|
+
0x17,
|
|
50
|
+
0x18,
|
|
51
|
+
0x19,
|
|
52
|
+
0x1a,
|
|
53
|
+
0x1b,
|
|
54
|
+
0x1c,
|
|
55
|
+
0x1d,
|
|
56
|
+
0x1e,
|
|
57
|
+
0x1f,
|
|
58
|
+
0x20,
|
|
23
59
|
]);
|
|
24
60
|
|
|
25
61
|
describe('extractEd25519PublicKey', () => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
62
|
+
describe('Happy Path', () => {
|
|
63
|
+
it('should extract 32-byte public key from valid DER encoding', () => {
|
|
64
|
+
// given: Valid DER-encoded public key from AWS KMS
|
|
65
|
+
const derEncoded = MOCK_DER_PUBLIC_KEY;
|
|
66
|
+
|
|
67
|
+
// when: Extracting public key
|
|
68
|
+
const publicKey = extractEd25519PublicKey(derEncoded);
|
|
69
|
+
|
|
70
|
+
// then: Should return exactly 32 bytes
|
|
71
|
+
expect(publicKey).toBeInstanceOf(Uint8Array);
|
|
72
|
+
expect(publicKey.length).toBe(32);
|
|
73
|
+
|
|
74
|
+
// then: Should match the mock public key bytes (starting at offset 12)
|
|
75
|
+
const expectedPublicKey = new Uint8Array([
|
|
76
|
+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
|
|
77
|
+
0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
78
|
+
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
|
79
|
+
]);
|
|
80
|
+
expect(publicKey).toEqual(expectedPublicKey);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('Failure Paths', () => {
|
|
85
|
+
it('should throw PublicKeyExtractionError when SEQUENCE tag is missing', () => {
|
|
86
|
+
// given: Invalid DER with wrong first byte (not 0x30)
|
|
87
|
+
const invalidDer = new Uint8Array([
|
|
88
|
+
0x31,
|
|
89
|
+
0x2a, // Wrong tag (0x31 instead of 0x30)
|
|
90
|
+
0x30,
|
|
91
|
+
0x05,
|
|
92
|
+
0x06,
|
|
93
|
+
0x03,
|
|
94
|
+
0x2b,
|
|
95
|
+
0x65,
|
|
96
|
+
0x70,
|
|
97
|
+
0x03,
|
|
98
|
+
0x21,
|
|
99
|
+
0x00,
|
|
100
|
+
...new Array(32).fill(0x01),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
// when & then: Should throw PublicKeyExtractionError
|
|
104
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
105
|
+
PublicKeyExtractionError,
|
|
106
|
+
);
|
|
107
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
108
|
+
'Invalid DER encoding: missing SEQUENCE tag',
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should throw PublicKeyExtractionError when BIT STRING tag is missing', () => {
|
|
113
|
+
// given: Invalid DER without BIT STRING tag (0x03)
|
|
114
|
+
const invalidDer = new Uint8Array([
|
|
115
|
+
0x30,
|
|
116
|
+
0x2a, // SEQUENCE
|
|
117
|
+
0x30,
|
|
118
|
+
0x05, // AlgorithmIdentifier
|
|
119
|
+
0x06,
|
|
120
|
+
0x03,
|
|
121
|
+
0x2b,
|
|
122
|
+
0x65,
|
|
123
|
+
0x70, // OID
|
|
124
|
+
0x04,
|
|
125
|
+
0x21,
|
|
126
|
+
0x00, // Wrong tag (0x04 instead of 0x03)
|
|
127
|
+
...new Array(32).fill(0x01),
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
// when & then: Should throw PublicKeyExtractionError
|
|
131
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
132
|
+
PublicKeyExtractionError,
|
|
133
|
+
);
|
|
134
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
135
|
+
'Invalid DER encoding: missing BIT STRING',
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should throw PublicKeyExtractionError when BIT STRING length is incorrect', () => {
|
|
140
|
+
// given: Invalid DER with wrong BIT STRING length (0x20 instead of 0x21)
|
|
141
|
+
const invalidDer = new Uint8Array([
|
|
142
|
+
0x30,
|
|
143
|
+
0x2a, // SEQUENCE
|
|
144
|
+
0x30,
|
|
145
|
+
0x05, // AlgorithmIdentifier
|
|
146
|
+
0x06,
|
|
147
|
+
0x03,
|
|
148
|
+
0x2b,
|
|
149
|
+
0x65,
|
|
150
|
+
0x70, // OID
|
|
151
|
+
0x03,
|
|
152
|
+
0x20,
|
|
153
|
+
0x00, // Wrong length (0x20 = 32 instead of 0x21 = 33)
|
|
154
|
+
...new Array(32).fill(0x01),
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
// when & then: Should throw PublicKeyExtractionError with specific message
|
|
158
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
159
|
+
PublicKeyExtractionError,
|
|
160
|
+
);
|
|
161
|
+
expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
|
|
162
|
+
'Unexpected BIT STRING length: expected 0x21 (33 bytes), got 0x20',
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should throw PublicKeyExtractionError when input is empty', () => {
|
|
167
|
+
// given: Empty Uint8Array
|
|
168
|
+
const emptyInput = new Uint8Array(0);
|
|
169
|
+
|
|
170
|
+
// when & then: Should throw PublicKeyExtractionError
|
|
171
|
+
expect(() => extractEd25519PublicKey(emptyInput)).toThrow(
|
|
172
|
+
PublicKeyExtractionError,
|
|
173
|
+
);
|
|
174
|
+
expect(() => extractEd25519PublicKey(emptyInput)).toThrow(
|
|
175
|
+
'Invalid DER encoding: missing SEQUENCE tag',
|
|
176
|
+
);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should throw error when input is null or undefined', () => {
|
|
180
|
+
// given: Null input
|
|
181
|
+
const nullInput = null as unknown as Uint8Array;
|
|
182
|
+
|
|
183
|
+
// when & then: Should throw TypeError (not PublicKeyExtractionError)
|
|
184
|
+
// Note: This tests TypeScript runtime behavior - null/undefined are not Uint8Array
|
|
185
|
+
expect(() => extractEd25519PublicKey(nullInput)).toThrow();
|
|
186
|
+
|
|
187
|
+
// given: Undefined input
|
|
188
|
+
const undefinedInput = undefined as unknown as Uint8Array;
|
|
189
|
+
|
|
190
|
+
// when & then: Should throw TypeError
|
|
191
|
+
expect(() => extractEd25519PublicKey(undefinedInput)).toThrow();
|
|
192
|
+
});
|
|
193
|
+
});
|
|
135
194
|
});
|
package/src/utils/publicKey.ts
CHANGED
|
@@ -27,44 +27,44 @@ import { PublicKeyExtractionError } from '../errors/index.js';
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
export function extractEd25519PublicKey(derEncoded: Uint8Array): Uint8Array {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
// Validate DER structure - first byte must be SEQUENCE tag (0x30)
|
|
31
|
+
if (derEncoded[0] !== 0x30) {
|
|
32
|
+
throw new PublicKeyExtractionError(
|
|
33
|
+
'Invalid DER encoding: missing SEQUENCE tag',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// Parse AlgorithmIdentifier SEQUENCE to find where BIT STRING starts
|
|
38
|
+
// Position 2 should be another SEQUENCE tag for AlgorithmIdentifier
|
|
39
|
+
if (derEncoded[2] !== 0x30) {
|
|
40
|
+
throw new PublicKeyExtractionError(
|
|
41
|
+
'Invalid DER encoding: missing AlgorithmIdentifier SEQUENCE',
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Get length of AlgorithmIdentifier content
|
|
46
|
+
const algorithmIdentifierLength = derEncoded[3];
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
// BIT STRING starts after AlgorithmIdentifier SEQUENCE
|
|
49
|
+
// Position = 4 (first content byte) + algorithmIdentifierLength
|
|
50
|
+
const bitStringIndex = 4 + algorithmIdentifierLength;
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
// Verify BIT STRING tag (0x03)
|
|
53
|
+
if (derEncoded[bitStringIndex] !== 0x03) {
|
|
54
|
+
throw new PublicKeyExtractionError(
|
|
55
|
+
'Invalid DER encoding: missing BIT STRING',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
// Verify BIT STRING length (0x21 = 33 bytes: 1 byte unused bits + 32 bytes public key)
|
|
60
|
+
const bitStringLength = derEncoded[bitStringIndex + 1];
|
|
61
|
+
if (bitStringLength !== 0x21) {
|
|
62
|
+
throw new PublicKeyExtractionError(
|
|
63
|
+
`Unexpected BIT STRING length: expected 0x21 (33 bytes), got 0x${bitStringLength.toString(16)}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
// First byte after length is unused bits (0x00), next 32 bytes is the public key
|
|
68
|
+
const publicKeyStart = bitStringIndex + 3;
|
|
69
|
+
return derEncoded.slice(publicKeyStart, publicKeyStart + 32);
|
|
70
70
|
}
|