@solana/keychain-gcp-kms 0.0.0 → 0.4.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 +103 -0
- package/dist/__tests__/gcp-kms-signer.integration.test.d.ts +2 -0
- package/dist/__tests__/gcp-kms-signer.integration.test.d.ts.map +1 -0
- package/dist/__tests__/gcp-kms-signer.integration.test.js +49 -0
- package/dist/__tests__/gcp-kms-signer.integration.test.js.map +1 -0
- package/dist/__tests__/gcp-kms-signer.test.d.ts +2 -0
- package/dist/__tests__/gcp-kms-signer.test.d.ts.map +1 -0
- package/dist/__tests__/gcp-kms-signer.test.js +286 -0
- package/dist/__tests__/gcp-kms-signer.test.js.map +1 -0
- package/dist/gcp-kms-signer.d.ts +53 -0
- package/dist/gcp-kms-signer.d.ts.map +1 -0
- package/dist/gcp-kms-signer.js +161 -0
- package/dist/gcp-kms-signer.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -8
- package/src/__tests__/gcp-kms-signer.integration.test.ts +82 -0
- package/src/__tests__/gcp-kms-signer.test.ts +361 -0
- package/src/gcp-kms-signer.ts +191 -0
- package/src/index.ts +2 -0
- package/src/types.ts +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @solana/keychain-gcp-kms
|
|
2
|
+
|
|
3
|
+
Google Cloud KMS-based signer for Solana transactions using EdDSA (Ed25519) signing.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @solana/keychain-gcp-kms
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
1. A Google Cloud KMS key with:
|
|
14
|
+
- **Algorithm**: `EC_SIGN_ED25519`
|
|
15
|
+
- **Purpose**: `ASYMMETRIC_SIGN`
|
|
16
|
+
|
|
17
|
+
2. Google Cloud credentials configured (see [Google Cloud Credentials](#google-cloud-credentials) below)
|
|
18
|
+
|
|
19
|
+
## Google Cloud Credentials
|
|
20
|
+
|
|
21
|
+
The signer uses the **Application Default Credentials (ADC)** to authenticate. You don't need to pass credentials explicitly when running in a Google Cloud environment (Compute Engine, GKE, Cloud Run, etc.).
|
|
22
|
+
|
|
23
|
+
### IAM Permissions
|
|
24
|
+
|
|
25
|
+
For this signer:
|
|
26
|
+
|
|
27
|
+
- Signing operations require `cloudkms.cryptoKeyVersions.useToSign`
|
|
28
|
+
- Availability checks (`isAvailable()`) require `cloudkms.cryptoKeyVersions.viewPublicKey`
|
|
29
|
+
|
|
30
|
+
### Local Development
|
|
31
|
+
|
|
32
|
+
For local development, you can:
|
|
33
|
+
|
|
34
|
+
1. **Use the gcloud CLI**:
|
|
35
|
+
```bash
|
|
36
|
+
gcloud auth application-default login
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Use a Service Account Key**:
|
|
40
|
+
```bash
|
|
41
|
+
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/service-account-file.json"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Creating a Google Cloud KMS Key
|
|
45
|
+
|
|
46
|
+
Use the `gcloud` CLI to create a key suitable for Solana signing:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Create a KeyRing
|
|
50
|
+
gcloud kms keyrings create "my-keyring" --location "us-east1"
|
|
51
|
+
|
|
52
|
+
# Create a CryptoKey
|
|
53
|
+
gcloud kms keys create "my-key" \
|
|
54
|
+
--location "us-east1" \
|
|
55
|
+
--keyring "my-keyring" \
|
|
56
|
+
--purpose "asymmetric-signing" \
|
|
57
|
+
--default-algorithm "ec-sign-ed25519"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Basic Example
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { GcpKmsSigner } from '@solana/keychain-gcp-kms';
|
|
66
|
+
|
|
67
|
+
const signer = new GcpKmsSigner({
|
|
68
|
+
keyName: 'projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1',
|
|
69
|
+
publicKey: 'YourSolanaPublicKeyBase58',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Sign a message
|
|
73
|
+
const message = { content: new Uint8Array([1, 2, 3, 4]) };
|
|
74
|
+
const signatures = await signer.signMessages([message]);
|
|
75
|
+
|
|
76
|
+
// Sign a transaction
|
|
77
|
+
const signatures = await signer.signTransactions([transaction]);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### `GcpKmsSigner`
|
|
83
|
+
|
|
84
|
+
#### Constructor
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
new GcpKmsSigner(config: GcpKmsSignerConfig)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Config Options:**
|
|
91
|
+
- `keyName` (required): Full resource name of the GCP KMS crypto key version
|
|
92
|
+
- `publicKey` (required): Solana public key (base58-encoded)
|
|
93
|
+
- `requestDelayMs` (optional): Delay in ms between concurrent signing requests to avoid rate limits (default: 0)
|
|
94
|
+
|
|
95
|
+
#### Properties
|
|
96
|
+
|
|
97
|
+
- `address`: The Solana address (public key) for this signer
|
|
98
|
+
|
|
99
|
+
#### Methods
|
|
100
|
+
|
|
101
|
+
- `signMessages(messages)`: Sign multiple messages
|
|
102
|
+
- `signTransactions(transactions)`: Sign multiple transactions
|
|
103
|
+
- `isAvailable()`: Check if the signer is available by retrieving the public key and verifying `EC_SIGN_ED25519`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-kms-signer.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gcp-kms-signer.integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { appendTransactionMessageInstructions, createSolanaRpc, createTransactionMessage, pipe, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit';
|
|
2
|
+
import { getAddMemoInstruction } from '@solana-program/memo';
|
|
3
|
+
import { config } from 'dotenv';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { GcpKmsSigner } from '../gcp-kms-signer.js';
|
|
6
|
+
config();
|
|
7
|
+
const REQUIRED_ENV_VARS = ['GCP_KMS_KEY_NAME', 'GCP_KMS_SIGNER_PUBKEY'];
|
|
8
|
+
function hasRequiredEnvVars() {
|
|
9
|
+
return REQUIRED_ENV_VARS.every(v => process.env[v]);
|
|
10
|
+
}
|
|
11
|
+
function createGcpKmsSigner() {
|
|
12
|
+
return new GcpKmsSigner({
|
|
13
|
+
keyName: process.env.GCP_KMS_KEY_NAME,
|
|
14
|
+
publicKey: process.env.GCP_KMS_SIGNER_PUBKEY,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
describe('GcpKmsSigner Integration', () => {
|
|
18
|
+
it.skipIf(!hasRequiredEnvVars())('signs transactions with GCP KMS', async () => {
|
|
19
|
+
const signer = createGcpKmsSigner();
|
|
20
|
+
const rpcUrl = process.env.SOLANA_RPC_URL ?? 'https://api.devnet.solana.com';
|
|
21
|
+
// Get real blockhash from devnet
|
|
22
|
+
const rpc = createSolanaRpc(rpcUrl);
|
|
23
|
+
const { value: { blockhash, lastValidBlockHeight }, } = await rpc.getLatestBlockhash().send();
|
|
24
|
+
// Create memo transaction (doesn't need funds)
|
|
25
|
+
const transaction = pipe(createTransactionMessage({ version: 0 }), tx => setTransactionMessageFeePayerSigner(signer, tx), tx => appendTransactionMessageInstructions([getAddMemoInstruction({ memo: 'GCP KMS test' })], tx), tx => setTransactionMessageLifetimeUsingBlockhash({ blockhash, lastValidBlockHeight }, tx));
|
|
26
|
+
// Sign via GCP KMS
|
|
27
|
+
const signed = await signTransactionMessageWithSigners(transaction);
|
|
28
|
+
// Verify signature returned
|
|
29
|
+
expect(signed.signatures[signer.address]).toBeDefined();
|
|
30
|
+
expect(signed.signatures[signer.address]?.length).toBe(64);
|
|
31
|
+
}, 60000); // 1 minute timeout
|
|
32
|
+
it.skipIf(!hasRequiredEnvVars())('signs messages', async () => {
|
|
33
|
+
const signer = createGcpKmsSigner();
|
|
34
|
+
const message = {
|
|
35
|
+
content: new Uint8Array([1, 2, 3, 4, 5]),
|
|
36
|
+
signatures: {},
|
|
37
|
+
};
|
|
38
|
+
const result = await signer.signMessages([message]);
|
|
39
|
+
expect(result).toHaveLength(1);
|
|
40
|
+
expect(result[0]?.[signer.address]).toBeDefined();
|
|
41
|
+
expect(result[0]?.[signer.address]?.length).toBe(64);
|
|
42
|
+
});
|
|
43
|
+
it.skipIf(!hasRequiredEnvVars())('checks availability', async () => {
|
|
44
|
+
const signer = createGcpKmsSigner();
|
|
45
|
+
const available = await signer.isAvailable();
|
|
46
|
+
expect(available).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=gcp-kms-signer.integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-kms-signer.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/gcp-kms-signer.integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-kms-signer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/gcp-kms-signer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { address } from '@solana/addresses';
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { assertIsSolanaSigner } from '@solana/keychain-core';
|
|
4
|
+
import { GcpKmsSigner } from '../gcp-kms-signer.js';
|
|
5
|
+
// Mock GCP KMS SDK
|
|
6
|
+
const mockAsymmetricSign = vi.fn();
|
|
7
|
+
const mockGetPublicKey = vi.fn();
|
|
8
|
+
vi.mock('@google-cloud/kms', () => {
|
|
9
|
+
return {
|
|
10
|
+
v1: {
|
|
11
|
+
KeyManagementServiceClient: class {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.asymmetricSign = mockAsymmetricSign;
|
|
14
|
+
this.getPublicKey = mockGetPublicKey;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
describe('GcpKmsSigner', () => {
|
|
21
|
+
const TEST_KEY_NAME = 'projects/test-project/locations/us-east1/keyRings/test-ring/cryptoKeys/test-key/cryptoKeyVersions/1';
|
|
22
|
+
const TEST_PUBLIC_KEY = address('11111111111111111111111111111111');
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
describe('constructor', () => {
|
|
27
|
+
it('creates a GcpKmsSigner with valid config', () => {
|
|
28
|
+
const config = {
|
|
29
|
+
keyName: TEST_KEY_NAME,
|
|
30
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
31
|
+
};
|
|
32
|
+
const signer = new GcpKmsSigner(config);
|
|
33
|
+
expect(signer.address).toBe(TEST_PUBLIC_KEY);
|
|
34
|
+
assertIsSolanaSigner(signer);
|
|
35
|
+
expect(signer.signMessages).toBeDefined();
|
|
36
|
+
expect(signer.signTransactions).toBeDefined();
|
|
37
|
+
expect(signer.isAvailable).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
it('should throw error for missing keyName', () => {
|
|
40
|
+
expect(() => {
|
|
41
|
+
new GcpKmsSigner({
|
|
42
|
+
keyName: '',
|
|
43
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
44
|
+
});
|
|
45
|
+
}).toThrow('Missing required keyName field');
|
|
46
|
+
});
|
|
47
|
+
it('should throw error for missing publicKey', () => {
|
|
48
|
+
expect(() => {
|
|
49
|
+
new GcpKmsSigner({
|
|
50
|
+
keyName: TEST_KEY_NAME,
|
|
51
|
+
publicKey: '',
|
|
52
|
+
});
|
|
53
|
+
}).toThrow('Missing required publicKey field');
|
|
54
|
+
});
|
|
55
|
+
it('should throw error for invalid public key', () => {
|
|
56
|
+
expect(() => {
|
|
57
|
+
new GcpKmsSigner({
|
|
58
|
+
keyName: TEST_KEY_NAME,
|
|
59
|
+
publicKey: 'invalid-key',
|
|
60
|
+
});
|
|
61
|
+
}).toThrow('Invalid Solana public key format');
|
|
62
|
+
});
|
|
63
|
+
it('should validate requestDelayMs', () => {
|
|
64
|
+
expect(() => {
|
|
65
|
+
new GcpKmsSigner({
|
|
66
|
+
keyName: TEST_KEY_NAME,
|
|
67
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
68
|
+
requestDelayMs: -1,
|
|
69
|
+
});
|
|
70
|
+
}).toThrow('requestDelayMs must not be negative');
|
|
71
|
+
});
|
|
72
|
+
it('should warn for high requestDelayMs', () => {
|
|
73
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
74
|
+
new GcpKmsSigner({
|
|
75
|
+
keyName: TEST_KEY_NAME,
|
|
76
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
77
|
+
requestDelayMs: 5000,
|
|
78
|
+
});
|
|
79
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('requestDelayMs is greater than 3000ms'));
|
|
80
|
+
warnSpy.mockRestore();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('signMessages', () => {
|
|
84
|
+
it('should sign a message successfully', async () => {
|
|
85
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
86
|
+
{
|
|
87
|
+
signature: new Uint8Array(64).fill(0x42),
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
const signer = new GcpKmsSigner({
|
|
91
|
+
keyName: TEST_KEY_NAME,
|
|
92
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
93
|
+
});
|
|
94
|
+
const message = {
|
|
95
|
+
content: new Uint8Array([1, 2, 3, 4]),
|
|
96
|
+
signatures: {},
|
|
97
|
+
};
|
|
98
|
+
const result = await signer.signMessages([message]);
|
|
99
|
+
expect(result).toHaveLength(1);
|
|
100
|
+
expect(result[0]?.[signer.address]).toBeDefined();
|
|
101
|
+
expect(mockAsymmetricSign).toHaveBeenCalledTimes(1);
|
|
102
|
+
expect(mockAsymmetricSign).toHaveBeenCalledWith({
|
|
103
|
+
data: message.content,
|
|
104
|
+
name: TEST_KEY_NAME,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it('should handle multiple messages with delay', async () => {
|
|
108
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
109
|
+
{
|
|
110
|
+
signature: new Uint8Array(64).fill(0x42),
|
|
111
|
+
},
|
|
112
|
+
]);
|
|
113
|
+
const signer = new GcpKmsSigner({
|
|
114
|
+
keyName: TEST_KEY_NAME,
|
|
115
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
116
|
+
requestDelayMs: 10,
|
|
117
|
+
});
|
|
118
|
+
const messages = [
|
|
119
|
+
{ content: new Uint8Array([1]), signatures: {} },
|
|
120
|
+
{ content: new Uint8Array([2]), signatures: {} },
|
|
121
|
+
{ content: new Uint8Array([3]), signatures: {} },
|
|
122
|
+
];
|
|
123
|
+
const startTime = Date.now();
|
|
124
|
+
const result = await signer.signMessages(messages);
|
|
125
|
+
const endTime = Date.now();
|
|
126
|
+
expect(result).toHaveLength(3);
|
|
127
|
+
expect(mockAsymmetricSign).toHaveBeenCalledTimes(3);
|
|
128
|
+
// Should have some delay (at least 15ms for 2 delays of 10ms each)
|
|
129
|
+
expect(endTime - startTime).toBeGreaterThanOrEqual(15);
|
|
130
|
+
});
|
|
131
|
+
it('should throw error on invalid signature length', async () => {
|
|
132
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
133
|
+
{
|
|
134
|
+
signature: new Uint8Array(32), // Wrong length
|
|
135
|
+
},
|
|
136
|
+
]);
|
|
137
|
+
const signer = new GcpKmsSigner({
|
|
138
|
+
keyName: TEST_KEY_NAME,
|
|
139
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
140
|
+
});
|
|
141
|
+
const message = { content: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
142
|
+
await expect(signer.signMessages([message])).rejects.toThrow('Invalid signature length');
|
|
143
|
+
});
|
|
144
|
+
it('should throw error on missing signature', async () => {
|
|
145
|
+
mockAsymmetricSign.mockResolvedValue([{}]);
|
|
146
|
+
const signer = new GcpKmsSigner({
|
|
147
|
+
keyName: TEST_KEY_NAME,
|
|
148
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
149
|
+
});
|
|
150
|
+
const message = { content: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
151
|
+
await expect(signer.signMessages([message])).rejects.toThrow('No signature in GCP KMS response');
|
|
152
|
+
});
|
|
153
|
+
it('should handle GCP KMS API errors', async () => {
|
|
154
|
+
const apiError = new Error('GCP Error');
|
|
155
|
+
apiError.code = 403;
|
|
156
|
+
mockAsymmetricSign.mockRejectedValue(apiError);
|
|
157
|
+
const signer = new GcpKmsSigner({
|
|
158
|
+
keyName: TEST_KEY_NAME,
|
|
159
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
160
|
+
});
|
|
161
|
+
const message = { content: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
162
|
+
await expect(signer.signMessages([message])).rejects.toThrow('GCP KMS Sign operation failed: GCP Error');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('signTransactions', () => {
|
|
166
|
+
it('should sign a transaction successfully', async () => {
|
|
167
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
168
|
+
{
|
|
169
|
+
signature: new Uint8Array(64).fill(0x42),
|
|
170
|
+
},
|
|
171
|
+
]);
|
|
172
|
+
const signer = new GcpKmsSigner({
|
|
173
|
+
keyName: TEST_KEY_NAME,
|
|
174
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
175
|
+
});
|
|
176
|
+
const transaction = {
|
|
177
|
+
messageBytes: new Uint8Array([1, 2, 3, 4]),
|
|
178
|
+
signatures: {},
|
|
179
|
+
};
|
|
180
|
+
const result = await signer.signTransactions([transaction]);
|
|
181
|
+
expect(result).toHaveLength(1);
|
|
182
|
+
expect(result[0]).toHaveProperty(signer.address);
|
|
183
|
+
expect(mockAsymmetricSign).toHaveBeenCalledTimes(1);
|
|
184
|
+
});
|
|
185
|
+
it('should sign multiple transactions successfully', async () => {
|
|
186
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
187
|
+
{
|
|
188
|
+
signature: new Uint8Array(64).fill(0x42),
|
|
189
|
+
},
|
|
190
|
+
]);
|
|
191
|
+
const signer = new GcpKmsSigner({
|
|
192
|
+
keyName: TEST_KEY_NAME,
|
|
193
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
194
|
+
});
|
|
195
|
+
const transactions = [
|
|
196
|
+
{ messageBytes: new Uint8Array([1]), signatures: {} },
|
|
197
|
+
{ messageBytes: new Uint8Array([2]), signatures: {} },
|
|
198
|
+
];
|
|
199
|
+
const result = await signer.signTransactions(transactions);
|
|
200
|
+
expect(result).toHaveLength(2);
|
|
201
|
+
expect(mockAsymmetricSign).toHaveBeenCalledTimes(2);
|
|
202
|
+
});
|
|
203
|
+
it('should throw error on invalid signature length', async () => {
|
|
204
|
+
mockAsymmetricSign.mockResolvedValue([
|
|
205
|
+
{
|
|
206
|
+
signature: new Uint8Array(32),
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
const signer = new GcpKmsSigner({
|
|
210
|
+
keyName: TEST_KEY_NAME,
|
|
211
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
212
|
+
});
|
|
213
|
+
const transaction = { messageBytes: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
214
|
+
await expect(signer.signTransactions([transaction])).rejects.toThrow('Invalid signature length');
|
|
215
|
+
});
|
|
216
|
+
it('should throw error on missing signature', async () => {
|
|
217
|
+
mockAsymmetricSign.mockResolvedValue([{}]);
|
|
218
|
+
const signer = new GcpKmsSigner({
|
|
219
|
+
keyName: TEST_KEY_NAME,
|
|
220
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
221
|
+
});
|
|
222
|
+
const transaction = { messageBytes: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
223
|
+
await expect(signer.signTransactions([transaction])).rejects.toThrow('No signature in GCP KMS response');
|
|
224
|
+
});
|
|
225
|
+
it('should handle GCP KMS API errors', async () => {
|
|
226
|
+
const apiError = new Error('GCP Error');
|
|
227
|
+
apiError.code = 403;
|
|
228
|
+
mockAsymmetricSign.mockRejectedValue(apiError);
|
|
229
|
+
const signer = new GcpKmsSigner({
|
|
230
|
+
keyName: TEST_KEY_NAME,
|
|
231
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
232
|
+
});
|
|
233
|
+
const transaction = { messageBytes: new Uint8Array([1, 2, 3, 4]), signatures: {} };
|
|
234
|
+
await expect(signer.signTransactions([transaction])).rejects.toThrow('GCP KMS Sign operation failed: GCP Error');
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('isAvailable', () => {
|
|
238
|
+
it('should return true for valid Ed25519 key', async () => {
|
|
239
|
+
mockGetPublicKey.mockResolvedValue([
|
|
240
|
+
{
|
|
241
|
+
name: TEST_KEY_NAME,
|
|
242
|
+
algorithm: 'EC_SIGN_ED25519',
|
|
243
|
+
},
|
|
244
|
+
]);
|
|
245
|
+
const signer = new GcpKmsSigner({
|
|
246
|
+
keyName: TEST_KEY_NAME,
|
|
247
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
248
|
+
});
|
|
249
|
+
const available = await signer.isAvailable();
|
|
250
|
+
expect(available).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
it('should return false for wrong algorithm', async () => {
|
|
253
|
+
mockGetPublicKey.mockResolvedValue([
|
|
254
|
+
{
|
|
255
|
+
name: TEST_KEY_NAME,
|
|
256
|
+
algorithm: 'RSA_SIGN_PKCS1_2048_SHA256',
|
|
257
|
+
},
|
|
258
|
+
]);
|
|
259
|
+
const signer = new GcpKmsSigner({
|
|
260
|
+
keyName: TEST_KEY_NAME,
|
|
261
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
262
|
+
});
|
|
263
|
+
const available = await signer.isAvailable();
|
|
264
|
+
expect(available).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
it('should return false for missing public key response', async () => {
|
|
267
|
+
mockGetPublicKey.mockResolvedValue([undefined]);
|
|
268
|
+
const signer = new GcpKmsSigner({
|
|
269
|
+
keyName: TEST_KEY_NAME,
|
|
270
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
271
|
+
});
|
|
272
|
+
const available = await signer.isAvailable();
|
|
273
|
+
expect(available).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
it('should return false on error', async () => {
|
|
276
|
+
mockGetPublicKey.mockRejectedValue(new Error('GCP error'));
|
|
277
|
+
const signer = new GcpKmsSigner({
|
|
278
|
+
keyName: TEST_KEY_NAME,
|
|
279
|
+
publicKey: TEST_PUBLIC_KEY,
|
|
280
|
+
});
|
|
281
|
+
const available = await signer.isAvailable();
|
|
282
|
+
expect(available).toBe(false);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
//# sourceMappingURL=gcp-kms-signer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-kms-signer.test.js","sourceRoot":"","sources":["../../src/__tests__/gcp-kms-signer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Address } from '@solana/addresses';
|
|
2
|
+
import { SolanaSigner } from '@solana/keychain-core';
|
|
3
|
+
import { SignableMessage, SignatureDictionary } from '@solana/signers';
|
|
4
|
+
import { Transaction, TransactionWithinSizeLimit, TransactionWithLifetime } from '@solana/transactions';
|
|
5
|
+
import type { GcpKmsSignerConfig } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Google Cloud KMS-based signer using EdDSA (Ed25519) signing
|
|
8
|
+
*
|
|
9
|
+
* The GCP KMS key must be created with:
|
|
10
|
+
* - Algorithm: EC_SIGN_ED25519
|
|
11
|
+
* - Purpose: ASYMMETRIC_SIGN
|
|
12
|
+
*
|
|
13
|
+
* Example gcloud CLI command to create a key:
|
|
14
|
+
* ```bash
|
|
15
|
+
* gcloud kms keys create my-key \
|
|
16
|
+
* --keyring=my-keyring \
|
|
17
|
+
* --location=us-east1 \
|
|
18
|
+
* --purpose=asymmetric-signing \
|
|
19
|
+
* --default-algorithm=ec-sign-ed25519
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class GcpKmsSigner<TAddress extends string = string> implements SolanaSigner<TAddress> {
|
|
23
|
+
readonly address: Address<TAddress>;
|
|
24
|
+
private readonly keyName;
|
|
25
|
+
private readonly client;
|
|
26
|
+
private readonly requestDelayMs;
|
|
27
|
+
constructor(config: GcpKmsSignerConfig);
|
|
28
|
+
/**
|
|
29
|
+
* Validate request delay ms
|
|
30
|
+
*/
|
|
31
|
+
private validateRequestDelayMs;
|
|
32
|
+
/**
|
|
33
|
+
* Add delay between concurrent requests
|
|
34
|
+
*/
|
|
35
|
+
private delay;
|
|
36
|
+
/**
|
|
37
|
+
* Sign message bytes using GCP KMS EdDSA signing
|
|
38
|
+
*/
|
|
39
|
+
private signBytes;
|
|
40
|
+
/**
|
|
41
|
+
* Sign multiple messages using GCP KMS
|
|
42
|
+
*/
|
|
43
|
+
signMessages(messages: readonly SignableMessage[]): Promise<readonly SignatureDictionary[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Sign multiple transactions using GCP KMS
|
|
46
|
+
*/
|
|
47
|
+
signTransactions(transactions: readonly (Transaction & TransactionWithinSizeLimit & TransactionWithLifetime)[]): Promise<readonly SignatureDictionary[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if GCP KMS is available and the key is accessible
|
|
50
|
+
*/
|
|
51
|
+
isAvailable(): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=gcp-kms-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-kms-signer.d.ts","sourceRoot":"","sources":["../src/gcp-kms-signer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAmB,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAA8C,YAAY,EAAoB,MAAM,uBAAuB,CAAC;AAEnH,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAExG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY,CAAC,QAAQ,SAAS,MAAM,GAAG,MAAM,CAAE,YAAW,YAAY,CAAC,QAAQ,CAAC;IACzF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,kBAAkB;IA6BtC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAa9B;;OAEG;YACW,KAAK;IAMnB;;OAEG;YACW,SAAS;IA4CvB;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC;IAiBjG;;OAEG;IACG,gBAAgB,CAClB,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,0BAA0B,GAAG,uBAAuB,CAAC,EAAE,GAC9F,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC;IAc1C;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAgBxC"}
|