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.
@@ -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
- 0x30, 0x2a, // SEQUENCE, 42 bytes
15
- 0x30, 0x05, // AlgorithmIdentifier SEQUENCE, 5 bytes
16
- 0x06, 0x03, 0x2b, 0x65, 0x70, // OID: 1.3.101.112 (Ed25519)
17
- 0x03, 0x21, 0x00, // BIT STRING, 33 bytes (32 + 1 padding)
18
- // Mock 32-byte ED25519 public key
19
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
20
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
21
- 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
22
- 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
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
- describe('Happy Path', () => {
27
- it('should extract 32-byte public key from valid DER encoding', () => {
28
- // given: Valid DER-encoded public key from AWS KMS
29
- const derEncoded = MOCK_DER_PUBLIC_KEY;
30
-
31
- // when: Extracting public key
32
- const publicKey = extractEd25519PublicKey(derEncoded);
33
-
34
- // then: Should return exactly 32 bytes
35
- expect(publicKey).toBeInstanceOf(Uint8Array);
36
- expect(publicKey.length).toBe(32);
37
-
38
- // then: Should match the mock public key bytes (starting at offset 12)
39
- const expectedPublicKey = new Uint8Array([
40
- 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
41
- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
42
- 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
43
- 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
44
- ]);
45
- expect(publicKey).toEqual(expectedPublicKey);
46
- });
47
- });
48
-
49
- describe('Failure Paths', () => {
50
- it('should throw PublicKeyExtractionError when SEQUENCE tag is missing', () => {
51
- // given: Invalid DER with wrong first byte (not 0x30)
52
- const invalidDer = new Uint8Array([
53
- 0x31, 0x2a, // Wrong tag (0x31 instead of 0x30)
54
- 0x30, 0x05,
55
- 0x06, 0x03, 0x2b, 0x65, 0x70,
56
- 0x03, 0x21, 0x00,
57
- ...new Array(32).fill(0x01),
58
- ]);
59
-
60
- // when & then: Should throw PublicKeyExtractionError
61
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
62
- PublicKeyExtractionError
63
- );
64
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
65
- 'Invalid DER encoding: missing SEQUENCE tag'
66
- );
67
- });
68
-
69
- it('should throw PublicKeyExtractionError when BIT STRING tag is missing', () => {
70
- // given: Invalid DER without BIT STRING tag (0x03)
71
- const invalidDer = new Uint8Array([
72
- 0x30, 0x2a, // SEQUENCE
73
- 0x30, 0x05, // AlgorithmIdentifier
74
- 0x06, 0x03, 0x2b, 0x65, 0x70, // OID
75
- 0x04, 0x21, 0x00, // Wrong tag (0x04 instead of 0x03)
76
- ...new Array(32).fill(0x01),
77
- ]);
78
-
79
- // when & then: Should throw PublicKeyExtractionError
80
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
81
- PublicKeyExtractionError
82
- );
83
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
84
- 'Invalid DER encoding: missing BIT STRING'
85
- );
86
- });
87
-
88
- it('should throw PublicKeyExtractionError when BIT STRING length is incorrect', () => {
89
- // given: Invalid DER with wrong BIT STRING length (0x20 instead of 0x21)
90
- const invalidDer = new Uint8Array([
91
- 0x30, 0x2a, // SEQUENCE
92
- 0x30, 0x05, // AlgorithmIdentifier
93
- 0x06, 0x03, 0x2b, 0x65, 0x70, // OID
94
- 0x03, 0x20, 0x00, // Wrong length (0x20 = 32 instead of 0x21 = 33)
95
- ...new Array(32).fill(0x01),
96
- ]);
97
-
98
- // when & then: Should throw PublicKeyExtractionError with specific message
99
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
100
- PublicKeyExtractionError
101
- );
102
- expect(() => extractEd25519PublicKey(invalidDer)).toThrow(
103
- 'Unexpected BIT STRING length: expected 0x21 (33 bytes), got 0x20'
104
- );
105
- });
106
-
107
- it('should throw PublicKeyExtractionError when input is empty', () => {
108
- // given: Empty Uint8Array
109
- const emptyInput = new Uint8Array(0);
110
-
111
- // when & then: Should throw PublicKeyExtractionError
112
- expect(() => extractEd25519PublicKey(emptyInput)).toThrow(
113
- PublicKeyExtractionError
114
- );
115
- expect(() => extractEd25519PublicKey(emptyInput)).toThrow(
116
- 'Invalid DER encoding: missing SEQUENCE tag'
117
- );
118
- });
119
-
120
- it('should throw error when input is null or undefined', () => {
121
- // given: Null input
122
- const nullInput = null as unknown as Uint8Array;
123
-
124
- // when & then: Should throw TypeError (not PublicKeyExtractionError)
125
- // Note: This tests TypeScript runtime behavior - null/undefined are not Uint8Array
126
- expect(() => extractEd25519PublicKey(nullInput)).toThrow();
127
-
128
- // given: Undefined input
129
- const undefinedInput = undefined as unknown as Uint8Array;
130
-
131
- // when & then: Should throw TypeError
132
- expect(() => extractEd25519PublicKey(undefinedInput)).toThrow();
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
  });
@@ -27,44 +27,44 @@ import { PublicKeyExtractionError } from '../errors/index.js';
27
27
  * ```
28
28
  */
29
29
  export function extractEd25519PublicKey(derEncoded: Uint8Array): Uint8Array {
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
- }
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
- // 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
- }
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
- // Get length of AlgorithmIdentifier content
46
- const algorithmIdentifierLength = derEncoded[3];
45
+ // Get length of AlgorithmIdentifier content
46
+ const algorithmIdentifierLength = derEncoded[3];
47
47
 
48
- // BIT STRING starts after AlgorithmIdentifier SEQUENCE
49
- // Position = 4 (first content byte) + algorithmIdentifierLength
50
- const bitStringIndex = 4 + algorithmIdentifierLength;
48
+ // BIT STRING starts after AlgorithmIdentifier SEQUENCE
49
+ // Position = 4 (first content byte) + algorithmIdentifierLength
50
+ const bitStringIndex = 4 + algorithmIdentifierLength;
51
51
 
52
- // Verify BIT STRING tag (0x03)
53
- if (derEncoded[bitStringIndex] !== 0x03) {
54
- throw new PublicKeyExtractionError(
55
- 'Invalid DER encoding: missing BIT STRING'
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
- // 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
- }
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
- // 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);
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
  }