Skip to content

Commit 3e954c7

Browse files
feat(awm): demo script for dinamo internface
1 parent 484601a commit 3e954c7

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Dinamo HSM KMS Implementation Documentation
2+
3+
This document provides comprehensive documentation for the KMS API's integration with Dinamo HSM, covering the complete request-response flow from API handlers to HSM operations.
4+
5+
## Demo Scripts
6+
7+
- **`real-dinamo-flow.ts`** - Complete handler-to-provider-to-HSM-to-database flow demonstration
8+
- **`dinamo-provider-implementation.ts`** - Core provider implementation patterns and methods
9+
- **`DINAMO_HSM_IMPLEMENTATION.md`** - Original deep-dive documentation (legacy)
10+
11+
## Quick Overview
12+
13+
The KMS API provides secure key management through four main endpoints that integrate with Dinamo HSM:
14+
15+
- `POST /key` - Store private keys using envelope encryption
16+
- `GET /key/{pub}` - Retrieve private keys using envelope decryption
17+
- `POST /generateDataKey` - Generate AES keys in HSM for encryption
18+
- `POST /decryptDataKey` - Decrypt data keys using root keys
19+
20+
## Architecture Flow
21+
22+
```
23+
API Request → Handler → KMS Provider → Dinamo HSM → Database → Response
24+
```
25+
26+
### Handler-to-Provider Mapping
27+
28+
| API Endpoint | Handler File | Provider Method | HSM Operations |
29+
|--------------|--------------|-----------------|----------------|
30+
| `POST /key` | `storePrivateKey.ts` | `postKey()` | Create AES key, export, encrypt |
31+
| `GET /key/{pub}` | `getPrivateKey.ts` | `getKey()` | Decrypt data key locally |
32+
| `POST /generateDataKey` | `generateDataKey.ts` | `generateDataKey()` | Create/export AES key |
33+
| `POST /decryptDataKey` | `decryptDataKey.ts` | `decryptDataKey()` | Local SJCL decryption |
34+
35+
## Envelope Encryption Pattern
36+
37+
### Layer 1: Root Keys (HSM)
38+
- **Algorithm**: RSA-2048 asymmetric keys
39+
- **Storage**: Dinamo HSM hardware (permanent)
40+
- **Purpose**: Encrypt/decrypt data keys
41+
- **Security**: Never exported from HSM
42+
43+
### Layer 2: Data Keys (Generated in HSM, Used Locally)
44+
- **Algorithm**: AES-256 symmetric keys
45+
- **Generation**: Dinamo HSM (temporary keys)
46+
- **Export**: Raw key material exported as Buffer
47+
- **Encryption**: Encrypted with root key using SJCL
48+
- **Storage**: Database (encrypted), Memory (plaintext, temporary)
49+
50+
### Layer 3: Private Keys (Application Data)
51+
- **Encryption**: AES-256-CCM using SJCL
52+
- **Key**: Data key plaintext (from Layer 2)
53+
- **Storage**: Database (encrypted only)
54+
55+
## Implementation Details
56+
57+
### Connection Management Pattern
58+
59+
```typescript
60+
private async withClient<T>(fn: (client) => Promise<T>): Promise<T> {
61+
const conn = await hsm.connect({
62+
host: process.env.DINAMO_HOST || "",
63+
authUsernamePassword: {
64+
username: process.env.DINAMO_USERNAME || "",
65+
password: process.env.DINAMO_PASSWORD || "",
66+
},
67+
});
68+
69+
try {
70+
return await fn(conn);
71+
} finally {
72+
try {
73+
await conn.disconnect();
74+
} catch (e) {
75+
logger.warn("Failed to disconnect from Dinamo HSM", e);
76+
}
77+
}
78+
}
79+
```
80+
81+
**Key Features:**
82+
- Environment-based configuration
83+
- Automatic connection cleanup using try/finally
84+
- Error handling for disconnect failures
85+
- Generic typing for return values
86+
- Centralized connection logic for all HSM operations
87+
88+
### Root Key Creation
89+
90+
```typescript
91+
async createRootKey(): Promise<{ rootKey: string }> {
92+
const keyName = getRandomHash(32);
93+
94+
return await this.withClient(async (client) => {
95+
const created = await client.key.create(
96+
keyName, // Unique key identifier
97+
hsm.enums.RSA_ASYMMETRIC_KEYS.ALG_RSA_2048, // 2048-bit RSA
98+
true, // Exportable for public key ops
99+
false // Permanent storage
100+
);
101+
102+
if (!created) {
103+
throw { message: 'Failed to create symmetric key in HSM', code: 500 };
104+
}
105+
106+
return { rootKey: keyName };
107+
});
108+
}
109+
```
110+
111+
**HSM Operations:**
112+
- **Key Naming**: 32-character random hash for uniqueness
113+
- **Algorithm**: RSA-2048 for asymmetric operations
114+
- **Exportable**: Set to true to allow public key export
115+
- **Permanent**: Root keys stored permanently in HSM
116+
- **Error Handling**: Structured errors with HTTP codes
117+
118+
### Data Key Generation Process
119+
120+
```typescript
121+
async generateDataKey(rootKey: string, keySpec: DataKeyTypeType): Promise<GenerateDataKeyKmsRes> {
122+
return await this.withClient(async (client) => {
123+
// 1. Create temporary AES key in HSM
124+
const dataKeyName = getRandomHash(32);
125+
const created = await client.key.create(
126+
dataKeyName,
127+
hsm.enums.SYMMETRICAL_KEYS.ALG_AES_256, // 256-bit AES
128+
true, // Exportable
129+
true // Temporary (auto-deleted)
130+
);
131+
132+
// 2. Export plaintext key material
133+
const exportedKey = await client.key.exportSymmetric(dataKeyName);
134+
const plaintextKey = exportedKey.toString('base64');
135+
136+
// 3. Encrypt with root key (envelope encryption)
137+
return {
138+
encryptedKey: encrypt(rootKey, plaintextKey), // SJCL encryption
139+
plaintextKey: plaintextKey, // For immediate use
140+
};
141+
});
142+
}
143+
```
144+
145+
**Process Flow:**
146+
1. **Temporary Key Creation**: AES-256 symmetric key in HSM
147+
2. **Key Export**: Raw key material extracted as Buffer
148+
3. **Format Conversion**: Buffer → base64 string
149+
4. **Envelope Encryption**: Encrypt plaintext with root key
150+
5. **Automatic Cleanup**: HSM deletes temporary key
151+
152+
### Private Key Storage (POST /key)
153+
154+
```typescript
155+
async postKey(rootKey: string, prv: string): Promise<PostKeyKmsRes> {
156+
// 1. Generate fresh data key for this private key
157+
const dataKey = await this.generateDataKey(rootKey, 'AES-256');
158+
159+
// 2. Encrypt private key with data key
160+
const encryptedPrv = encrypt(dataKey.plaintextKey, prv);
161+
162+
return {
163+
encryptedPrv, // Encrypted private key
164+
rootKeyId: rootKey, // Root key reference
165+
metadata: {
166+
encryptedDataKey: dataKey.encryptedKey, // Encrypted data key
167+
},
168+
};
169+
}
170+
```
171+
172+
**Encryption Layers:**
173+
- **Layer 1**: Private Key → AES-256-CCM → Encrypted Private Key
174+
- Uses plaintextKey from HSM-generated data key
175+
- SJCL library for AES-256-CCM encryption
176+
- **Layer 2**: Data Key → Local AES → Encrypted Data Key
177+
- Uses root key as password
178+
- Custom encrypt() function from utils/encrypt.ts
179+
180+
### Private Key Retrieval (GET /key/{pub})
181+
182+
```typescript
183+
async getKey(rootKey: string, keyId: string, options: GetKeyOptions): Promise<GetKeyKmsRes> {
184+
// 1. Decrypt data key using root key
185+
const decryptedKey = await this.decryptDataKey(rootKey, options.encryptedDataKey);
186+
187+
// 2. Convert data key format
188+
const aesKeyBuffer = Buffer.from(decryptedKey.plaintextKey, 'base64');
189+
const password = aesKeyBuffer.toString('base64');
190+
191+
// 3. Decrypt private key with recovered data key
192+
const decryptedPrv = decrypt(password, keyId);
193+
194+
return { prv: decryptedPrv };
195+
}
196+
```
197+
198+
**Decryption Process:**
199+
1. **Data Key Recovery**: Decrypt encrypted data key with root key
200+
2. **Format Conversion**: base64 → Buffer → base64 (consistent format)
201+
3. **Private Key Decryption**: Use recovered data key to decrypt private key
202+
203+
### Data Key Decryption
204+
205+
```typescript
206+
async decryptDataKey(rootKey: string, encryptedKey: string): Promise<DecryptDataKeyKmsRes> {
207+
return {
208+
plaintextKey: decrypt(rootKey, encryptedKey),
209+
};
210+
}
211+
```
212+
213+
**Implementation Notes:**
214+
- **Local Operation**: Uses SJCL decryption, not HSM
215+
- **Root Key as Password**: Simple symmetric decryption
216+
- **Performance**: Fast local operation vs. slower HSM calls
217+
218+
## Database Schema
219+
220+
### private_keys Table
221+
222+
```sql
223+
CREATE TABLE private_keys (
224+
id INTEGER PRIMARY KEY AUTOINCREMENT,
225+
pub TEXT NOT NULL, -- Public key (identifier)
226+
source TEXT NOT NULL, -- 'user' or 'backup'
227+
encryptedPrv TEXT NOT NULL, -- Private key encrypted with data key
228+
encryptedDataKey TEXT NOT NULL, -- Data key encrypted with root key
229+
provider TEXT NOT NULL, -- 'dinamo'
230+
rootKey TEXT NOT NULL, -- Root key name in HSM
231+
coin TEXT NOT NULL, -- Cryptocurrency type
232+
type TEXT NOT NULL, -- Key type (e.g., 'tss')
233+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
234+
);
235+
```
236+
237+
**Storage Pattern:**
238+
- **encryptedPrv**: SJCL AES-256-CCM encrypted private key
239+
- **encryptedDataKey**: Root-key-encrypted data key
240+
- **rootKey**: Reference to HSM root key name
241+
- **No plaintext**: All sensitive data encrypted
242+
243+
## SJCL Encryption Details
244+
245+
### Configuration
246+
- **Algorithm**: AES-256-CCM
247+
- **Iterations**: 10,000 (PBKDF2)
248+
- **Key Size**: 256 bits
249+
- **Tag Size**: 128 bits
250+
- **Mode**: CCM (Counter with CBC-MAC)
251+
252+
### Example SJCL Output
253+
```json
254+
{
255+
"iv": "a1b2c3d4e5f6...",
256+
"v": 1,
257+
"iter": 10000,
258+
"ks": 256,
259+
"ts": 128,
260+
"mode": "ccm",
261+
"adata": "",
262+
"cipher": "aes",
263+
"salt": "f6e5d4c3b2a1...",
264+
"ct": "base64-encrypted-data"
265+
}
266+
```

0 commit comments

Comments
 (0)