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/src/kms/signer.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
- PublicKey,
3
- Transaction,
4
- VersionedTransaction,
2
+ PublicKey,
3
+ type Transaction,
4
+ type VersionedTransaction,
5
5
  } from '@solana/web3.js';
6
6
  import nacl from 'tweetnacl';
7
- import { KmsClient } from './client.js';
8
- import { extractEd25519PublicKey } from '../utils/publicKey.js';
9
- import type { KmsConfig } from '../types/index.js';
10
7
  import { SignatureVerificationError } from '../errors/index.js';
8
+ import type { KmsConfig } from '../types/index.js';
9
+ import { extractEd25519PublicKey } from '../utils/publicKey.js';
10
+ import { KmsClient } from './client.js';
11
11
 
12
12
  /**
13
13
  * Solana transaction signer using AWS KMS ED25519 keys.
@@ -43,232 +43,228 @@ import { SignatureVerificationError } from '../errors/index.js';
43
43
  * ```
44
44
  */
45
45
  export class SolanaKmsSigner {
46
- private readonly kmsClient: KmsClient;
47
- private publicKey?: PublicKey;
48
- private rawPublicKey?: Uint8Array;
46
+ private readonly kmsClient: KmsClient;
47
+ private publicKey?: PublicKey;
48
+ private rawPublicKey?: Uint8Array;
49
49
 
50
- /**
51
- * Creates a new SolanaKmsSigner instance.
52
- *
53
- * @param config - Either KmsConfig or an existing KmsClient instance
54
- *
55
- * @example
56
- * ```typescript
57
- * // With KmsConfig
58
- * const signer = new SolanaKmsSigner({
59
- * region: 'us-east-1',
60
- * keyId: 'key-id'
61
- * });
62
- *
63
- * // With KmsClient
64
- * const client = new KmsClient(config);
65
- * const signer = new SolanaKmsSigner(client);
66
- * ```
67
- */
68
- constructor(config: KmsConfig | KmsClient) {
69
- if (config instanceof KmsClient) {
70
- this.kmsClient = config;
71
- } else {
72
- this.kmsClient = new KmsClient(config);
73
- }
74
- }
50
+ /**
51
+ * Creates a new SolanaKmsSigner instance.
52
+ *
53
+ * @param config - Either KmsConfig or an existing KmsClient instance
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * // With KmsConfig
58
+ * const signer = new SolanaKmsSigner({
59
+ * region: 'us-east-1',
60
+ * keyId: 'key-id'
61
+ * });
62
+ *
63
+ * // With KmsClient
64
+ * const client = new KmsClient(config);
65
+ * const signer = new SolanaKmsSigner(client);
66
+ * ```
67
+ */
68
+ constructor(config: KmsConfig | KmsClient) {
69
+ if (config instanceof KmsClient) {
70
+ this.kmsClient = config;
71
+ } else {
72
+ this.kmsClient = new KmsClient(config);
73
+ }
74
+ }
75
75
 
76
- /**
77
- * Retrieves the Solana PublicKey associated with the KMS key.
78
- *
79
- * The public key is cached after first retrieval to minimize KMS API calls.
80
- *
81
- * @returns Solana PublicKey object
82
- * @throws {KmsClientError} If KMS API call fails
83
- * @throws {PublicKeyExtractionError} If DER decoding fails
84
- *
85
- * @example
86
- * ```typescript
87
- * const publicKey = await signer.getPublicKey();
88
- * console.log('Address:', publicKey.toBase58());
89
- * ```
90
- */
91
- async getPublicKey(): Promise<PublicKey> {
92
- // Return cached public key if available
93
- if (this.publicKey) {
94
- return this.publicKey;
95
- }
76
+ /**
77
+ * Retrieves the Solana PublicKey associated with the KMS key.
78
+ *
79
+ * The public key is cached after first retrieval to minimize KMS API calls.
80
+ *
81
+ * @returns Solana PublicKey object
82
+ * @throws {KmsClientError} If KMS API call fails
83
+ * @throws {PublicKeyExtractionError} If DER decoding fails
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * const publicKey = await signer.getPublicKey();
88
+ * console.log('Address:', publicKey.toBase58());
89
+ * ```
90
+ */
91
+ async getPublicKey(): Promise<PublicKey> {
92
+ // Return cached public key if available
93
+ if (this.publicKey) {
94
+ return this.publicKey;
95
+ }
96
96
 
97
- // Get DER-encoded public key from KMS
98
- const derPublicKey = await this.kmsClient.getPublicKey();
97
+ // Get DER-encoded public key from KMS
98
+ const derPublicKey = await this.kmsClient.getPublicKey();
99
99
 
100
- // Extract raw 32-byte ED25519 public key
101
- const rawPublicKey = extractEd25519PublicKey(derPublicKey);
100
+ // Extract raw 32-byte ED25519 public key
101
+ const rawPublicKey = extractEd25519PublicKey(derPublicKey);
102
102
 
103
- // Create Solana PublicKey and cache both forms
104
- this.rawPublicKey = rawPublicKey;
105
- this.publicKey = new PublicKey(rawPublicKey);
103
+ // Create Solana PublicKey and cache both forms
104
+ this.rawPublicKey = rawPublicKey;
105
+ this.publicKey = new PublicKey(rawPublicKey);
106
106
 
107
- return this.publicKey;
108
- }
107
+ return this.publicKey;
108
+ }
109
109
 
110
- /**
111
- * Retrieves the raw 32-byte ED25519 public key.
112
- *
113
- * The public key is cached after first retrieval to minimize KMS API calls.
114
- *
115
- * @returns Raw 32-byte public key as Uint8Array
116
- * @throws {KmsClientError} If KMS API call fails
117
- * @throws {PublicKeyExtractionError} If DER decoding fails
118
- *
119
- * @example
120
- * ```typescript
121
- * const rawPublicKey = await signer.getRawPublicKey();
122
- * console.log('Raw public key length:', rawPublicKey.length); // 32
123
- * ```
124
- */
125
- async getRawPublicKey(): Promise<Uint8Array> {
126
- // Return cached raw public key if available
127
- if (this.rawPublicKey) {
128
- return this.rawPublicKey;
129
- }
110
+ /**
111
+ * Retrieves the raw 32-byte ED25519 public key.
112
+ *
113
+ * The public key is cached after first retrieval to minimize KMS API calls.
114
+ *
115
+ * @returns Raw 32-byte public key as Uint8Array
116
+ * @throws {KmsClientError} If KMS API call fails
117
+ * @throws {PublicKeyExtractionError} If DER decoding fails
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const rawPublicKey = await signer.getRawPublicKey();
122
+ * console.log('Raw public key length:', rawPublicKey.length); // 32
123
+ * ```
124
+ */
125
+ async getRawPublicKey(): Promise<Uint8Array> {
126
+ // Return cached raw public key if available
127
+ if (this.rawPublicKey) {
128
+ return this.rawPublicKey;
129
+ }
130
130
 
131
- // Get DER-encoded public key from KMS
132
- const derPublicKey = await this.kmsClient.getPublicKey();
131
+ // Get DER-encoded public key from KMS
132
+ const derPublicKey = await this.kmsClient.getPublicKey();
133
133
 
134
- // Extract raw 32-byte ED25519 public key
135
- this.rawPublicKey = extractEd25519PublicKey(derPublicKey);
134
+ // Extract raw 32-byte ED25519 public key
135
+ this.rawPublicKey = extractEd25519PublicKey(derPublicKey);
136
136
 
137
- return this.rawPublicKey;
138
- }
137
+ return this.rawPublicKey;
138
+ }
139
139
 
140
- /**
141
- * Signs an arbitrary message using the KMS key.
142
- *
143
- * The signature is verified using tweetnacl before being returned
144
- * to ensure cryptographic correctness.
145
- *
146
- * @param message - Message to sign as Uint8Array
147
- * @returns ED25519 signature (64 bytes)
148
- * @throws {KmsClientError} If KMS API call fails
149
- * @throws {SignatureVerificationError} If signature verification fails
150
- *
151
- * @example
152
- * ```typescript
153
- * const message = new TextEncoder().encode('Hello, Solana!');
154
- * const signature = await signer.signMessage(message);
155
- * console.log('Signature length:', signature.length); // 64
156
- * ```
157
- */
158
- async signMessage(message: Uint8Array): Promise<Uint8Array> {
159
- // Get signature from KMS
160
- const signature = await this.kmsClient.sign(message);
140
+ /**
141
+ * Signs an arbitrary message using the KMS key.
142
+ *
143
+ * The signature is verified using tweetnacl before being returned
144
+ * to ensure cryptographic correctness.
145
+ *
146
+ * @param message - Message to sign as Uint8Array
147
+ * @returns ED25519 signature (64 bytes)
148
+ * @throws {KmsClientError} If KMS API call fails
149
+ * @throws {SignatureVerificationError} If signature verification fails
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * const message = new TextEncoder().encode('Hello, Solana!');
154
+ * const signature = await signer.signMessage(message);
155
+ * console.log('Signature length:', signature.length); // 64
156
+ * ```
157
+ */
158
+ async signMessage(message: Uint8Array): Promise<Uint8Array> {
159
+ // Get signature from KMS
160
+ const signature = await this.kmsClient.sign(message);
161
161
 
162
- // Get raw public key for verification
163
- const rawPublicKey = await this.getRawPublicKey();
162
+ // Get raw public key for verification
163
+ const rawPublicKey = await this.getRawPublicKey();
164
164
 
165
- // Verify signature using tweetnacl
166
- const isValid = nacl.sign.detached.verify(
167
- message,
168
- signature,
169
- rawPublicKey
170
- );
165
+ // Verify signature using tweetnacl
166
+ const isValid = nacl.sign.detached.verify(message, signature, rawPublicKey);
171
167
 
172
- if (!isValid) {
173
- throw new SignatureVerificationError(
174
- 'Signature verification failed: signature does not match public key and message'
175
- );
176
- }
168
+ if (!isValid) {
169
+ throw new SignatureVerificationError(
170
+ 'Signature verification failed: signature does not match public key and message',
171
+ );
172
+ }
177
173
 
178
- return signature;
179
- }
174
+ return signature;
175
+ }
180
176
 
181
- /**
182
- * Signs a Solana legacy Transaction.
183
- *
184
- * The transaction must have recentBlockhash and feePayer set before signing.
185
- *
186
- * @param transaction - Transaction to sign
187
- * @returns Signed transaction
188
- * @throws {KmsClientError} If KMS API call fails
189
- * @throws {SignatureVerificationError} If signature verification fails
190
- *
191
- * @example
192
- * ```typescript
193
- * const transaction = new Transaction().add(instruction);
194
- * transaction.recentBlockhash = recentBlockhash;
195
- * transaction.feePayer = await signer.getPublicKey();
196
- * const signedTx = await signer.signTransaction(transaction);
197
- * ```
198
- */
199
- async signTransaction(transaction: Transaction): Promise<Transaction> {
200
- // Get public key for signing
201
- const publicKey = await this.getPublicKey();
177
+ /**
178
+ * Signs a Solana legacy Transaction.
179
+ *
180
+ * The transaction must have recentBlockhash and feePayer set before signing.
181
+ *
182
+ * @param transaction - Transaction to sign
183
+ * @returns Signed transaction
184
+ * @throws {KmsClientError} If KMS API call fails
185
+ * @throws {SignatureVerificationError} If signature verification fails
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const transaction = new Transaction().add(instruction);
190
+ * transaction.recentBlockhash = recentBlockhash;
191
+ * transaction.feePayer = await signer.getPublicKey();
192
+ * const signedTx = await signer.signTransaction(transaction);
193
+ * ```
194
+ */
195
+ async signTransaction(transaction: Transaction): Promise<Transaction> {
196
+ // Get public key for signing
197
+ const publicKey = await this.getPublicKey();
202
198
 
203
- // Serialize transaction message
204
- const message = transaction.serializeMessage();
199
+ // Serialize transaction message
200
+ const message = transaction.serializeMessage();
205
201
 
206
- // Sign the serialized message
207
- const signature = await this.signMessage(message);
202
+ // Sign the serialized message
203
+ const signature = await this.signMessage(message);
208
204
 
209
- // Add signature to transaction
210
- transaction.addSignature(publicKey, Buffer.from(signature));
205
+ // Add signature to transaction
206
+ transaction.addSignature(publicKey, Buffer.from(signature));
211
207
 
212
- return transaction;
213
- }
208
+ return transaction;
209
+ }
214
210
 
215
- /**
216
- * Signs a Solana VersionedTransaction.
217
- *
218
- * The transaction must have a valid message with recentBlockhash set.
219
- *
220
- * @param transaction - VersionedTransaction to sign
221
- * @returns Signed versioned transaction
222
- * @throws {KmsClientError} If KMS API call fails
223
- * @throws {SignatureVerificationError} If signature verification fails
224
- *
225
- * @example
226
- * ```typescript
227
- * const message = MessageV0.compile({
228
- * payerKey: await signer.getPublicKey(),
229
- * instructions: [instruction],
230
- * recentBlockhash: recentBlockhash
231
- * });
232
- * const transaction = new VersionedTransaction(message);
233
- * const signedTx = await signer.signVersionedTransaction(transaction);
234
- * ```
235
- */
236
- async signVersionedTransaction(
237
- transaction: VersionedTransaction
238
- ): Promise<VersionedTransaction> {
239
- // Serialize transaction message
240
- const message = transaction.message.serialize();
211
+ /**
212
+ * Signs a Solana VersionedTransaction.
213
+ *
214
+ * The transaction must have a valid message with recentBlockhash set.
215
+ *
216
+ * @param transaction - VersionedTransaction to sign
217
+ * @returns Signed versioned transaction
218
+ * @throws {KmsClientError} If KMS API call fails
219
+ * @throws {SignatureVerificationError} If signature verification fails
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const message = MessageV0.compile({
224
+ * payerKey: await signer.getPublicKey(),
225
+ * instructions: [instruction],
226
+ * recentBlockhash: recentBlockhash
227
+ * });
228
+ * const transaction = new VersionedTransaction(message);
229
+ * const signedTx = await signer.signVersionedTransaction(transaction);
230
+ * ```
231
+ */
232
+ async signVersionedTransaction(
233
+ transaction: VersionedTransaction,
234
+ ): Promise<VersionedTransaction> {
235
+ // Serialize transaction message
236
+ const message = transaction.message.serialize();
241
237
 
242
- // Sign the serialized message
243
- const signature = await this.signMessage(message);
238
+ // Sign the serialized message
239
+ const signature = await this.signMessage(message);
244
240
 
245
- // Add signature to transaction's signatures array
246
- transaction.addSignature(await this.getPublicKey(), Buffer.from(signature));
241
+ // Add signature to transaction's signatures array
242
+ transaction.addSignature(await this.getPublicKey(), Buffer.from(signature));
247
243
 
248
- return transaction;
249
- }
244
+ return transaction;
245
+ }
250
246
 
251
- /**
252
- * Signs multiple Solana transactions in parallel.
253
- *
254
- * All transactions must have recentBlockhash and feePayer set before signing.
255
- *
256
- * @param transactions - Array of transactions to sign
257
- * @returns Array of signed transactions in the same order
258
- * @throws {KmsClientError} If any KMS API call fails
259
- * @throws {SignatureVerificationError} If any signature verification fails
260
- *
261
- * @example
262
- * ```typescript
263
- * const transactions = [tx1, tx2, tx3];
264
- * const signedTxs = await signer.signAllTransactions(transactions);
265
- * ```
266
- */
267
- async signAllTransactions(
268
- transactions: Transaction[]
269
- ): Promise<Transaction[]> {
270
- return Promise.all(
271
- transactions.map((transaction) => this.signTransaction(transaction))
272
- );
273
- }
247
+ /**
248
+ * Signs multiple Solana transactions in parallel.
249
+ *
250
+ * All transactions must have recentBlockhash and feePayer set before signing.
251
+ *
252
+ * @param transactions - Array of transactions to sign
253
+ * @returns Array of signed transactions in the same order
254
+ * @throws {KmsClientError} If any KMS API call fails
255
+ * @throws {SignatureVerificationError} If any signature verification fails
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const transactions = [tx1, tx2, tx3];
260
+ * const signedTxs = await signer.signAllTransactions(transactions);
261
+ * ```
262
+ */
263
+ async signAllTransactions(
264
+ transactions: Transaction[],
265
+ ): Promise<Transaction[]> {
266
+ return Promise.all(
267
+ transactions.map((transaction) => this.signTransaction(transaction)),
268
+ );
269
+ }
274
270
  }
@@ -2,36 +2,36 @@
2
2
  * Configuration for AWS KMS client.
3
3
  */
4
4
  export interface KmsConfig {
5
- /**
6
- * AWS region where the KMS key is located (e.g., 'us-east-1').
7
- */
8
- region: string;
5
+ /**
6
+ * AWS region where the KMS key is located (e.g., 'us-east-1').
7
+ */
8
+ region: string;
9
9
 
10
- /**
11
- * KMS key ID or ARN to use for signing operations.
12
- */
13
- keyId: string;
10
+ /**
11
+ * KMS key ID or ARN to use for signing operations.
12
+ */
13
+ keyId: string;
14
14
 
15
- /**
16
- * Optional AWS credentials. If not provided, the AWS SDK will use
17
- * environment variables, IAM roles, or other credential providers.
18
- */
19
- credentials?: {
20
- /**
21
- * AWS access key ID.
22
- */
23
- accessKeyId: string;
15
+ /**
16
+ * Optional AWS credentials. If not provided, the AWS SDK will use
17
+ * environment variables, IAM roles, or other credential providers.
18
+ */
19
+ credentials?: {
20
+ /**
21
+ * AWS access key ID.
22
+ */
23
+ accessKeyId: string;
24
24
 
25
- /**
26
- * AWS secret access key.
27
- */
28
- secretAccessKey: string;
25
+ /**
26
+ * AWS secret access key.
27
+ */
28
+ secretAccessKey: string;
29
29
 
30
- /**
31
- * Optional session token for temporary credentials.
32
- */
33
- sessionToken?: string;
34
- };
30
+ /**
31
+ * Optional session token for temporary credentials.
32
+ */
33
+ sessionToken?: string;
34
+ };
35
35
  }
36
36
 
37
37
  /**
@@ -40,5 +40,5 @@ export interface KmsConfig {
40
40
  * Additional configuration options can be added in the future.
41
41
  */
42
42
  export interface SolanaKmsSignerConfig extends KmsConfig {
43
- // Additional configuration options can be added here in the future
43
+ // Additional configuration options can be added here in the future
44
44
  }