@qnsp/tenant-sdk 0.1.1 → 0.3.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/CHANGELOG.md +99 -0
- package/README.md +2 -2
- package/dist/index.d.ts +190 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +347 -4
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/index.test.ts +5 -0
- package/src/index.ts +562 -5
- package/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +0 -5
- package/.turbo/turbo-lint.log +0 -6
- package/.turbo/turbo-test.log +0 -58
- package/.turbo/turbo-typecheck.log +0 -5
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { validateUUID } from "./validation.js";
|
|
|
17
17
|
|
|
18
18
|
export interface TenantClientConfig {
|
|
19
19
|
readonly baseUrl: string;
|
|
20
|
-
readonly apiKey
|
|
20
|
+
readonly apiKey: string;
|
|
21
21
|
readonly timeoutMs?: number;
|
|
22
22
|
readonly telemetry?: TenantClientTelemetry | TenantClientTelemetryConfig;
|
|
23
23
|
readonly maxRetries?: number;
|
|
@@ -34,6 +34,297 @@ type InternalTenantClientConfig = {
|
|
|
34
34
|
|
|
35
35
|
export type TenantStatus = "active" | "suspended" | "pending" | "deleted";
|
|
36
36
|
|
|
37
|
+
export type HsmMode = "none" | "supported" | "required";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Crypto policy tier determines which PQC algorithms are allowed.
|
|
41
|
+
* Maps to tenant_crypto_policies.policy_tier in tenant-service.
|
|
42
|
+
*/
|
|
43
|
+
export type CryptoPolicyTier = "default" | "strict" | "maximum" | "government";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Crypto policy v1 uses profiles + tiers (evidence-first model).
|
|
47
|
+
*/
|
|
48
|
+
export type QnspTier =
|
|
49
|
+
| "TIER0_LEGACY"
|
|
50
|
+
| "TIER1_APPROVED"
|
|
51
|
+
| "TIER2_HIGH_ASSURANCE"
|
|
52
|
+
| "TIER3_DIVERSITY"
|
|
53
|
+
| "TIER4_EXPERIMENTAL";
|
|
54
|
+
|
|
55
|
+
export type TenantType =
|
|
56
|
+
| "FREE_FOREVER"
|
|
57
|
+
| "DEV_STARTER"
|
|
58
|
+
| "DEV_PRO"
|
|
59
|
+
| "DEV_ELITE"
|
|
60
|
+
| "BUSINESS_TEAM"
|
|
61
|
+
| "BUSINESS_ADVANCED"
|
|
62
|
+
| "BUSINESS_ELITE"
|
|
63
|
+
| "ENTERPRISE_STANDARD"
|
|
64
|
+
| "ENTERPRISE_PRO"
|
|
65
|
+
| "ENTERPRISE_ELITE"
|
|
66
|
+
| "PUBLIC_SECTOR";
|
|
67
|
+
|
|
68
|
+
export type CryptoProfileId =
|
|
69
|
+
| "gov-high-assurance"
|
|
70
|
+
| "defense-long-life-data"
|
|
71
|
+
| "financial-hybrid-pqc"
|
|
72
|
+
| "research-eval";
|
|
73
|
+
|
|
74
|
+
export type TenantCryptoPolicyV1Action = "CREATE" | "UPDATE" | "ROLLBACK";
|
|
75
|
+
|
|
76
|
+
export interface CryptoPolicyV1 {
|
|
77
|
+
readonly version: "v1";
|
|
78
|
+
readonly tenantType: TenantType;
|
|
79
|
+
readonly profile: CryptoProfileId;
|
|
80
|
+
readonly enabledTiers: readonly QnspTier[];
|
|
81
|
+
readonly tier0Expiry?: string;
|
|
82
|
+
readonly tier4Acknowledgement?: {
|
|
83
|
+
readonly nonCompliant: true;
|
|
84
|
+
readonly noProductionSecrets: true;
|
|
85
|
+
readonly approvedBy: string;
|
|
86
|
+
readonly approvedAt: string;
|
|
87
|
+
};
|
|
88
|
+
readonly overrides?: {
|
|
89
|
+
readonly allowFalcon?: boolean;
|
|
90
|
+
};
|
|
91
|
+
readonly requirements: {
|
|
92
|
+
readonly fipsAligned: boolean;
|
|
93
|
+
readonly evidenceRequired: boolean;
|
|
94
|
+
readonly cryptoAgilityMetadataRequired: boolean;
|
|
95
|
+
readonly statefulLifecycleGuards: boolean;
|
|
96
|
+
readonly downgradeDetection: boolean;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface TenantCryptoPolicyV1Record {
|
|
101
|
+
readonly id: string;
|
|
102
|
+
readonly tenantId: string;
|
|
103
|
+
readonly version: "v1";
|
|
104
|
+
readonly policy: CryptoPolicyV1;
|
|
105
|
+
readonly policyHash: string;
|
|
106
|
+
readonly etag: string;
|
|
107
|
+
readonly createdAt: string;
|
|
108
|
+
readonly createdByPrincipal: string;
|
|
109
|
+
readonly createdByIp: string | null;
|
|
110
|
+
readonly updatedAt: string;
|
|
111
|
+
readonly updatedByPrincipal: string;
|
|
112
|
+
readonly updatedByIp: string | null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export interface TenantCryptoPolicyV1HistoryRecord {
|
|
116
|
+
readonly id: string;
|
|
117
|
+
readonly tenantId: string;
|
|
118
|
+
readonly version: "v1";
|
|
119
|
+
readonly policy: CryptoPolicyV1;
|
|
120
|
+
readonly policyHash: string;
|
|
121
|
+
readonly action: TenantCryptoPolicyV1Action;
|
|
122
|
+
readonly reason: string | null;
|
|
123
|
+
readonly changedAt: string;
|
|
124
|
+
readonly changedByPrincipal: string;
|
|
125
|
+
readonly changedByIp: string | null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface TenantCryptoPolicyV1HistoryResponse {
|
|
129
|
+
readonly tenantId: string;
|
|
130
|
+
readonly items: readonly TenantCryptoPolicyV1HistoryRecord[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Tenant crypto policy configuration.
|
|
135
|
+
*/
|
|
136
|
+
export interface TenantCryptoPolicy {
|
|
137
|
+
readonly tenantId: string;
|
|
138
|
+
readonly policyTier: CryptoPolicyTier;
|
|
139
|
+
readonly customAllowedKemAlgorithms: readonly string[] | null;
|
|
140
|
+
readonly customAllowedSignatureAlgorithms: readonly string[] | null;
|
|
141
|
+
readonly customAllowedSymmetricAlgorithms?: readonly string[] | null;
|
|
142
|
+
readonly customForbiddenAlgorithms?: readonly string[] | null;
|
|
143
|
+
readonly requireHsmForRootKeys: boolean;
|
|
144
|
+
readonly maxKeyAgeDays: number;
|
|
145
|
+
readonly enforcementMode?: "audit" | "enforce";
|
|
146
|
+
readonly createdAt: string;
|
|
147
|
+
readonly updatedAt: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Input for creating or updating a tenant crypto policy.
|
|
152
|
+
*/
|
|
153
|
+
export interface TenantCryptoPolicyInput {
|
|
154
|
+
readonly policyTier: CryptoPolicyTier;
|
|
155
|
+
readonly customAllowedKemAlgorithms?: readonly string[] | null;
|
|
156
|
+
readonly customAllowedSignatureAlgorithms?: readonly string[] | null;
|
|
157
|
+
readonly requireHsmForRootKeys?: boolean;
|
|
158
|
+
readonly maxKeyAgeDays?: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Algorithm configuration per crypto policy tier.
|
|
163
|
+
*/
|
|
164
|
+
export interface TierAlgorithmConfig {
|
|
165
|
+
readonly kemAlgorithms: readonly string[];
|
|
166
|
+
readonly signatureAlgorithms: readonly string[];
|
|
167
|
+
readonly defaultKemAlgorithm: string;
|
|
168
|
+
readonly defaultSignatureAlgorithm: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Default algorithms per crypto policy tier.
|
|
173
|
+
*/
|
|
174
|
+
export const CRYPTO_POLICY_ALGORITHMS: Record<CryptoPolicyTier, TierAlgorithmConfig> = {
|
|
175
|
+
default: {
|
|
176
|
+
kemAlgorithms: ["kyber-512", "kyber-768", "kyber-1024"],
|
|
177
|
+
signatureAlgorithms: ["dilithium-2", "dilithium-3", "dilithium-5"],
|
|
178
|
+
defaultKemAlgorithm: "kyber-768",
|
|
179
|
+
defaultSignatureAlgorithm: "dilithium-3",
|
|
180
|
+
},
|
|
181
|
+
strict: {
|
|
182
|
+
kemAlgorithms: ["kyber-768", "kyber-1024"],
|
|
183
|
+
signatureAlgorithms: ["dilithium-3", "dilithium-5", "falcon-1024"],
|
|
184
|
+
defaultKemAlgorithm: "kyber-768",
|
|
185
|
+
defaultSignatureAlgorithm: "dilithium-3",
|
|
186
|
+
},
|
|
187
|
+
maximum: {
|
|
188
|
+
kemAlgorithms: ["kyber-1024"],
|
|
189
|
+
signatureAlgorithms: ["dilithium-5", "falcon-1024", "sphincs-shake-256f-simple"],
|
|
190
|
+
defaultKemAlgorithm: "kyber-1024",
|
|
191
|
+
defaultSignatureAlgorithm: "dilithium-5",
|
|
192
|
+
},
|
|
193
|
+
government: {
|
|
194
|
+
kemAlgorithms: ["kyber-1024"],
|
|
195
|
+
signatureAlgorithms: ["dilithium-5", "sphincs-shake-256f-simple"],
|
|
196
|
+
defaultKemAlgorithm: "kyber-1024",
|
|
197
|
+
defaultSignatureAlgorithm: "dilithium-5",
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Mapping from internal algorithm names to NIST/standards display names.
|
|
203
|
+
* Covers all 90 PQC algorithms supported by QNSP.
|
|
204
|
+
* Canonical source: @qnsp/cryptography pqc-standards.ts ALGORITHM_NIST_NAMES
|
|
205
|
+
*/
|
|
206
|
+
export const ALGORITHM_TO_NIST: Record<string, string> = {
|
|
207
|
+
// FIPS 203 — ML-KEM
|
|
208
|
+
"kyber-512": "ML-KEM-512",
|
|
209
|
+
"kyber-768": "ML-KEM-768",
|
|
210
|
+
"kyber-1024": "ML-KEM-1024",
|
|
211
|
+
// FIPS 204 — ML-DSA
|
|
212
|
+
"dilithium-2": "ML-DSA-44",
|
|
213
|
+
"dilithium-3": "ML-DSA-65",
|
|
214
|
+
"dilithium-5": "ML-DSA-87",
|
|
215
|
+
// FIPS 205 — SLH-DSA (SHA-2 variants)
|
|
216
|
+
"sphincs-sha2-128f-simple": "SLH-DSA-SHA2-128f",
|
|
217
|
+
"sphincs-sha2-128s-simple": "SLH-DSA-SHA2-128s",
|
|
218
|
+
"sphincs-sha2-192f-simple": "SLH-DSA-SHA2-192f",
|
|
219
|
+
"sphincs-sha2-192s-simple": "SLH-DSA-SHA2-192s",
|
|
220
|
+
"sphincs-sha2-256f-simple": "SLH-DSA-SHA2-256f",
|
|
221
|
+
"sphincs-sha2-256s-simple": "SLH-DSA-SHA2-256s",
|
|
222
|
+
// FIPS 205 — SLH-DSA (SHAKE variants)
|
|
223
|
+
"sphincs-shake-128f-simple": "SLH-DSA-SHAKE-128f",
|
|
224
|
+
"sphincs-shake-128s-simple": "SLH-DSA-SHAKE-128s",
|
|
225
|
+
"sphincs-shake-192f-simple": "SLH-DSA-SHAKE-192f",
|
|
226
|
+
"sphincs-shake-192s-simple": "SLH-DSA-SHAKE-192s",
|
|
227
|
+
"sphincs-shake-256f-simple": "SLH-DSA-SHAKE-256f",
|
|
228
|
+
"sphincs-shake-256s-simple": "SLH-DSA-SHAKE-256s",
|
|
229
|
+
// FN-DSA (FIPS 206 draft)
|
|
230
|
+
"falcon-512": "FN-DSA-512",
|
|
231
|
+
"falcon-1024": "FN-DSA-1024",
|
|
232
|
+
// HQC (NIST selected March 2025)
|
|
233
|
+
"hqc-128": "HQC-128",
|
|
234
|
+
"hqc-192": "HQC-192",
|
|
235
|
+
"hqc-256": "HQC-256",
|
|
236
|
+
// BIKE (NIST Round 4)
|
|
237
|
+
"bike-l1": "BIKE-L1",
|
|
238
|
+
"bike-l3": "BIKE-L3",
|
|
239
|
+
"bike-l5": "BIKE-L5",
|
|
240
|
+
// Classic McEliece (ISO standard)
|
|
241
|
+
"mceliece-348864": "Classic-McEliece-348864",
|
|
242
|
+
"mceliece-460896": "Classic-McEliece-460896",
|
|
243
|
+
"mceliece-6688128": "Classic-McEliece-6688128",
|
|
244
|
+
"mceliece-6960119": "Classic-McEliece-6960119",
|
|
245
|
+
"mceliece-8192128": "Classic-McEliece-8192128",
|
|
246
|
+
// FrodoKEM (ISO standard)
|
|
247
|
+
"frodokem-640-aes": "FrodoKEM-640-AES",
|
|
248
|
+
"frodokem-640-shake": "FrodoKEM-640-SHAKE",
|
|
249
|
+
"frodokem-976-aes": "FrodoKEM-976-AES",
|
|
250
|
+
"frodokem-976-shake": "FrodoKEM-976-SHAKE",
|
|
251
|
+
"frodokem-1344-aes": "FrodoKEM-1344-AES",
|
|
252
|
+
"frodokem-1344-shake": "FrodoKEM-1344-SHAKE",
|
|
253
|
+
// NTRU (lattice-based, re-added in liboqs 0.15)
|
|
254
|
+
"ntru-hps-2048-509": "NTRU-HPS-2048-509",
|
|
255
|
+
"ntru-hps-2048-677": "NTRU-HPS-2048-677",
|
|
256
|
+
"ntru-hps-4096-821": "NTRU-HPS-4096-821",
|
|
257
|
+
"ntru-hps-4096-1229": "NTRU-HPS-4096-1229",
|
|
258
|
+
"ntru-hrss-701": "NTRU-HRSS-701",
|
|
259
|
+
"ntru-hrss-1373": "NTRU-HRSS-1373",
|
|
260
|
+
// NTRU-Prime
|
|
261
|
+
sntrup761: "sntrup761",
|
|
262
|
+
// MAYO (NIST Additional Signatures Round 2)
|
|
263
|
+
"mayo-1": "MAYO-1",
|
|
264
|
+
"mayo-2": "MAYO-2",
|
|
265
|
+
"mayo-3": "MAYO-3",
|
|
266
|
+
"mayo-5": "MAYO-5",
|
|
267
|
+
// CROSS (NIST Additional Signatures Round 2)
|
|
268
|
+
"cross-rsdp-128-balanced": "CROSS-RSDP-128-balanced",
|
|
269
|
+
"cross-rsdp-128-fast": "CROSS-RSDP-128-fast",
|
|
270
|
+
"cross-rsdp-128-small": "CROSS-RSDP-128-small",
|
|
271
|
+
"cross-rsdp-192-balanced": "CROSS-RSDP-192-balanced",
|
|
272
|
+
"cross-rsdp-192-fast": "CROSS-RSDP-192-fast",
|
|
273
|
+
"cross-rsdp-192-small": "CROSS-RSDP-192-small",
|
|
274
|
+
"cross-rsdp-256-balanced": "CROSS-RSDP-256-balanced",
|
|
275
|
+
"cross-rsdp-256-fast": "CROSS-RSDP-256-fast",
|
|
276
|
+
"cross-rsdp-256-small": "CROSS-RSDP-256-small",
|
|
277
|
+
"cross-rsdpg-128-balanced": "CROSS-RSDPG-128-balanced",
|
|
278
|
+
"cross-rsdpg-128-fast": "CROSS-RSDPG-128-fast",
|
|
279
|
+
"cross-rsdpg-128-small": "CROSS-RSDPG-128-small",
|
|
280
|
+
"cross-rsdpg-192-balanced": "CROSS-RSDPG-192-balanced",
|
|
281
|
+
"cross-rsdpg-192-fast": "CROSS-RSDPG-192-fast",
|
|
282
|
+
"cross-rsdpg-192-small": "CROSS-RSDPG-192-small",
|
|
283
|
+
"cross-rsdpg-256-balanced": "CROSS-RSDPG-256-balanced",
|
|
284
|
+
"cross-rsdpg-256-fast": "CROSS-RSDPG-256-fast",
|
|
285
|
+
"cross-rsdpg-256-small": "CROSS-RSDPG-256-small",
|
|
286
|
+
// UOV (NIST Additional Signatures Round 2)
|
|
287
|
+
"ov-Is": "UOV-Is",
|
|
288
|
+
"ov-Ip": "UOV-Ip",
|
|
289
|
+
"ov-III": "UOV-III",
|
|
290
|
+
"ov-V": "UOV-V",
|
|
291
|
+
"ov-Is-pkc": "UOV-Is-pkc",
|
|
292
|
+
"ov-Ip-pkc": "UOV-Ip-pkc",
|
|
293
|
+
"ov-III-pkc": "UOV-III-pkc",
|
|
294
|
+
"ov-V-pkc": "UOV-V-pkc",
|
|
295
|
+
"ov-Is-pkc-skc": "UOV-Is-pkc-skc",
|
|
296
|
+
"ov-Ip-pkc-skc": "UOV-Ip-pkc-skc",
|
|
297
|
+
"ov-III-pkc-skc": "UOV-III-pkc-skc",
|
|
298
|
+
"ov-V-pkc-skc": "UOV-V-pkc-skc",
|
|
299
|
+
// SNOVA (NIST Additional Signatures Round 2, liboqs 0.14+)
|
|
300
|
+
"snova-24-5-4": "SNOVA-24-5-4",
|
|
301
|
+
"snova-24-5-4-shake": "SNOVA-24-5-4-SHAKE",
|
|
302
|
+
"snova-24-5-4-esk": "SNOVA-24-5-4-ESK",
|
|
303
|
+
"snova-24-5-4-shake-esk": "SNOVA-24-5-4-SHAKE-ESK",
|
|
304
|
+
"snova-25-8-3": "SNOVA-25-8-3",
|
|
305
|
+
"snova-37-17-2": "SNOVA-37-17-2",
|
|
306
|
+
"snova-37-8-4": "SNOVA-37-8-4",
|
|
307
|
+
"snova-24-5-5": "SNOVA-24-5-5",
|
|
308
|
+
"snova-56-25-2": "SNOVA-56-25-2",
|
|
309
|
+
"snova-49-11-3": "SNOVA-49-11-3",
|
|
310
|
+
"snova-60-10-4": "SNOVA-60-10-4",
|
|
311
|
+
"snova-29-6-5": "SNOVA-29-6-5",
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Convert internal algorithm name to NIST standardized name.
|
|
316
|
+
*/
|
|
317
|
+
export function toNistAlgorithmName(internal: string): string {
|
|
318
|
+
return ALGORITHM_TO_NIST[internal] ?? internal;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Get algorithm config for a crypto policy tier.
|
|
323
|
+
*/
|
|
324
|
+
export function getAlgorithmConfigForTier(tier: CryptoPolicyTier): TierAlgorithmConfig {
|
|
325
|
+
return CRYPTO_POLICY_ALGORITHMS[tier];
|
|
326
|
+
}
|
|
327
|
+
|
|
37
328
|
export interface TenantSecurityEnvelope {
|
|
38
329
|
readonly controlPlaneTokenSha256: string | null;
|
|
39
330
|
readonly pqcSignatures: readonly {
|
|
@@ -70,6 +361,7 @@ export interface Tenant {
|
|
|
70
361
|
readonly plan: string;
|
|
71
362
|
readonly region: string;
|
|
72
363
|
readonly complianceTags: readonly string[];
|
|
364
|
+
readonly hsmMode: HsmMode;
|
|
73
365
|
readonly metadata: Record<string, unknown>;
|
|
74
366
|
readonly security: TenantSecurityEnvelope;
|
|
75
367
|
readonly domains: readonly TenantDomain[];
|
|
@@ -83,6 +375,7 @@ export interface CreateTenantRequest {
|
|
|
83
375
|
readonly plan?: string;
|
|
84
376
|
readonly region?: string;
|
|
85
377
|
readonly complianceTags?: readonly string[];
|
|
378
|
+
readonly hsmMode?: HsmMode;
|
|
86
379
|
readonly metadata?: Record<string, unknown>;
|
|
87
380
|
readonly domains?: readonly {
|
|
88
381
|
readonly domain: string;
|
|
@@ -96,6 +389,7 @@ export interface UpdateTenantRequest {
|
|
|
96
389
|
readonly plan?: string;
|
|
97
390
|
readonly status?: TenantStatus;
|
|
98
391
|
readonly complianceTags?: readonly string[];
|
|
392
|
+
readonly hsmMode?: HsmMode;
|
|
99
393
|
readonly metadata?: Record<string, unknown>;
|
|
100
394
|
readonly security: TenantSecurityEnvelope;
|
|
101
395
|
readonly signature?: TenantSignature;
|
|
@@ -121,6 +415,15 @@ export class TenantClient {
|
|
|
121
415
|
private readonly targetService: string;
|
|
122
416
|
|
|
123
417
|
constructor(config: TenantClientConfig) {
|
|
418
|
+
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
419
|
+
throw new Error(
|
|
420
|
+
"QNSP Tenant SDK: apiKey is required. " +
|
|
421
|
+
"Get your free API key at https://cloud.qnsp.cuilabs.io/signup — " +
|
|
422
|
+
"no credit card required (FREE tier: 5 GB storage, 2,000 API calls/month). " +
|
|
423
|
+
"Docs: https://docs.qnsp.cuilabs.io/sdk/tenant-sdk",
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
124
427
|
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
125
428
|
|
|
126
429
|
// Enforce HTTPS in production (allow HTTP only for localhost in development)
|
|
@@ -138,7 +441,7 @@ export class TenantClient {
|
|
|
138
441
|
|
|
139
442
|
this.config = {
|
|
140
443
|
baseUrl,
|
|
141
|
-
apiKey: config.apiKey
|
|
444
|
+
apiKey: config.apiKey,
|
|
142
445
|
timeoutMs: config.timeoutMs ?? 30_000,
|
|
143
446
|
maxRetries: config.maxRetries ?? 3,
|
|
144
447
|
retryDelayMs: config.retryDelayMs ?? 1_000,
|
|
@@ -173,9 +476,7 @@ export class TenantClient {
|
|
|
173
476
|
...options?.headers,
|
|
174
477
|
};
|
|
175
478
|
|
|
176
|
-
|
|
177
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
178
|
-
}
|
|
479
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
179
480
|
|
|
180
481
|
const controller = new AbortController();
|
|
181
482
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
@@ -290,6 +591,7 @@ export class TenantClient {
|
|
|
290
591
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
291
592
|
...(request.region !== undefined ? { region: request.region } : {}),
|
|
292
593
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
594
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
293
595
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
294
596
|
...(request.domains !== undefined ? { domains: request.domains } : {}),
|
|
295
597
|
security: request.security,
|
|
@@ -311,6 +613,7 @@ export class TenantClient {
|
|
|
311
613
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
312
614
|
...(request.status !== undefined ? { status: request.status } : {}),
|
|
313
615
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
616
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
314
617
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
315
618
|
security: request.security,
|
|
316
619
|
...(request.signature !== undefined ? { signature: request.signature } : {}),
|
|
@@ -353,6 +656,260 @@ export class TenantClient {
|
|
|
353
656
|
operation: "listTenants",
|
|
354
657
|
});
|
|
355
658
|
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Get the crypto policy for a tenant.
|
|
662
|
+
* Returns the tenant's crypto policy configuration including allowed algorithms.
|
|
663
|
+
* If no policy exists, a default policy is created and returned.
|
|
664
|
+
*/
|
|
665
|
+
async getTenantCryptoPolicy(tenantId: string): Promise<TenantCryptoPolicy> {
|
|
666
|
+
validateUUID(tenantId, "tenantId");
|
|
667
|
+
|
|
668
|
+
return this.request<TenantCryptoPolicy>("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
669
|
+
operation: "getTenantCryptoPolicy",
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Get the v1 crypto policy for a tenant (profiles + tiers model).
|
|
675
|
+
* If no policy exists, a default policy is created and returned.
|
|
676
|
+
*/
|
|
677
|
+
async getTenantCryptoPolicyV1(tenantId: string): Promise<TenantCryptoPolicyV1Record> {
|
|
678
|
+
validateUUID(tenantId, "tenantId");
|
|
679
|
+
|
|
680
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
681
|
+
"GET",
|
|
682
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
683
|
+
{
|
|
684
|
+
operation: "getTenantCryptoPolicyV1",
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* List v1 crypto policy history entries.
|
|
691
|
+
*/
|
|
692
|
+
async listTenantCryptoPolicyV1History(
|
|
693
|
+
tenantId: string,
|
|
694
|
+
options?: { readonly limit?: number },
|
|
695
|
+
): Promise<TenantCryptoPolicyV1HistoryResponse> {
|
|
696
|
+
validateUUID(tenantId, "tenantId");
|
|
697
|
+
const params = new URLSearchParams();
|
|
698
|
+
if (options?.limit !== undefined) {
|
|
699
|
+
params.set("limit", String(options.limit));
|
|
700
|
+
}
|
|
701
|
+
const query = params.toString();
|
|
702
|
+
const path = query
|
|
703
|
+
? `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history?${query}`
|
|
704
|
+
: `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history`;
|
|
705
|
+
|
|
706
|
+
return this.request<TenantCryptoPolicyV1HistoryResponse>("GET", path, {
|
|
707
|
+
operation: "listTenantCryptoPolicyV1History",
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Create or update the crypto policy for a tenant.
|
|
713
|
+
* Sets the policy tier and optional custom algorithm restrictions.
|
|
714
|
+
*/
|
|
715
|
+
async upsertTenantCryptoPolicy(
|
|
716
|
+
tenantId: string,
|
|
717
|
+
policy: TenantCryptoPolicyInput,
|
|
718
|
+
): Promise<TenantCryptoPolicy> {
|
|
719
|
+
validateUUID(tenantId, "tenantId");
|
|
720
|
+
|
|
721
|
+
return this.request<TenantCryptoPolicy>("PUT", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
722
|
+
body: {
|
|
723
|
+
policyTier: policy.policyTier,
|
|
724
|
+
...(policy.customAllowedKemAlgorithms !== undefined
|
|
725
|
+
? { customAllowedKemAlgorithms: policy.customAllowedKemAlgorithms }
|
|
726
|
+
: {}),
|
|
727
|
+
...(policy.customAllowedSignatureAlgorithms !== undefined
|
|
728
|
+
? { customAllowedSignatureAlgorithms: policy.customAllowedSignatureAlgorithms }
|
|
729
|
+
: {}),
|
|
730
|
+
...(policy.requireHsmForRootKeys !== undefined
|
|
731
|
+
? { requireHsmForRootKeys: policy.requireHsmForRootKeys }
|
|
732
|
+
: {}),
|
|
733
|
+
...(policy.maxKeyAgeDays !== undefined ? { maxKeyAgeDays: policy.maxKeyAgeDays } : {}),
|
|
734
|
+
},
|
|
735
|
+
operation: "upsertTenantCryptoPolicy",
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Update the v1 crypto policy for a tenant (requires If-Match with current ETag).
|
|
741
|
+
*/
|
|
742
|
+
async updateTenantCryptoPolicyV1(
|
|
743
|
+
tenantId: string,
|
|
744
|
+
policy: CryptoPolicyV1,
|
|
745
|
+
etag: string,
|
|
746
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
747
|
+
validateUUID(tenantId, "tenantId");
|
|
748
|
+
if (!etag) {
|
|
749
|
+
throw new Error("etag is required for updateTenantCryptoPolicyV1");
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
753
|
+
"PUT",
|
|
754
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
755
|
+
{
|
|
756
|
+
body: { policy },
|
|
757
|
+
headers: {
|
|
758
|
+
"If-Match": etag,
|
|
759
|
+
},
|
|
760
|
+
operation: "updateTenantCryptoPolicyV1",
|
|
761
|
+
},
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Enable Tier0 legacy algorithms (time-bounded) for a tenant.
|
|
767
|
+
*/
|
|
768
|
+
async enableTier0Legacy(
|
|
769
|
+
tenantId: string,
|
|
770
|
+
input: { readonly expiry: string },
|
|
771
|
+
etag: string,
|
|
772
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
773
|
+
validateUUID(tenantId, "tenantId");
|
|
774
|
+
if (!etag) {
|
|
775
|
+
throw new Error("etag is required for enableTier0Legacy");
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
779
|
+
"POST",
|
|
780
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/enable`,
|
|
781
|
+
{
|
|
782
|
+
body: { expiry: input.expiry },
|
|
783
|
+
headers: {
|
|
784
|
+
"If-Match": etag,
|
|
785
|
+
},
|
|
786
|
+
operation: "enableTier0Legacy",
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Disable Tier0 legacy algorithms for a tenant.
|
|
793
|
+
*/
|
|
794
|
+
async disableTier0Legacy(tenantId: string, etag: string): Promise<TenantCryptoPolicyV1Record> {
|
|
795
|
+
validateUUID(tenantId, "tenantId");
|
|
796
|
+
if (!etag) {
|
|
797
|
+
throw new Error("etag is required for disableTier0Legacy");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
801
|
+
"POST",
|
|
802
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/disable`,
|
|
803
|
+
{
|
|
804
|
+
body: {},
|
|
805
|
+
headers: {
|
|
806
|
+
"If-Match": etag,
|
|
807
|
+
},
|
|
808
|
+
operation: "disableTier0Legacy",
|
|
809
|
+
},
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Enable Tier4 experimental algorithms with acknowledgement.
|
|
815
|
+
*/
|
|
816
|
+
async enableTier4Experimental(
|
|
817
|
+
tenantId: string,
|
|
818
|
+
input: { readonly approvedBy: string },
|
|
819
|
+
etag: string,
|
|
820
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
821
|
+
validateUUID(tenantId, "tenantId");
|
|
822
|
+
if (!etag) {
|
|
823
|
+
throw new Error("etag is required for enableTier4Experimental");
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
827
|
+
"POST",
|
|
828
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier4/enable`,
|
|
829
|
+
{
|
|
830
|
+
body: { approvedBy: input.approvedBy },
|
|
831
|
+
headers: {
|
|
832
|
+
"If-Match": etag,
|
|
833
|
+
},
|
|
834
|
+
operation: "enableTier4Experimental",
|
|
835
|
+
},
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Roll back the v1 crypto policy to a previous history record or policy hash.
|
|
841
|
+
*/
|
|
842
|
+
async rollbackTenantCryptoPolicyV1(
|
|
843
|
+
tenantId: string,
|
|
844
|
+
input: { readonly historyId?: string; readonly policyHash?: string },
|
|
845
|
+
etag: string,
|
|
846
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
847
|
+
validateUUID(tenantId, "tenantId");
|
|
848
|
+
if (!etag) {
|
|
849
|
+
throw new Error("etag is required for rollbackTenantCryptoPolicyV1");
|
|
850
|
+
}
|
|
851
|
+
if (!input.historyId && !input.policyHash) {
|
|
852
|
+
throw new Error("historyId or policyHash is required for rollbackTenantCryptoPolicyV1");
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
856
|
+
"POST",
|
|
857
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/rollback`,
|
|
858
|
+
{
|
|
859
|
+
body: {
|
|
860
|
+
...(input.historyId ? { historyId: input.historyId } : {}),
|
|
861
|
+
...(input.policyHash ? { policyHash: input.policyHash } : {}),
|
|
862
|
+
},
|
|
863
|
+
headers: {
|
|
864
|
+
"If-Match": etag,
|
|
865
|
+
},
|
|
866
|
+
operation: "rollbackTenantCryptoPolicyV1",
|
|
867
|
+
},
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Get the allowed KEM algorithms for a tenant based on their crypto policy.
|
|
873
|
+
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
874
|
+
*/
|
|
875
|
+
async getAllowedKemAlgorithms(tenantId: string): Promise<readonly string[]> {
|
|
876
|
+
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
877
|
+
if (policy.customAllowedKemAlgorithms && policy.customAllowedKemAlgorithms.length > 0) {
|
|
878
|
+
return policy.customAllowedKemAlgorithms;
|
|
879
|
+
}
|
|
880
|
+
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].kemAlgorithms;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Get the allowed signature algorithms for a tenant based on their crypto policy.
|
|
885
|
+
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
886
|
+
*/
|
|
887
|
+
async getAllowedSignatureAlgorithms(tenantId: string): Promise<readonly string[]> {
|
|
888
|
+
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
889
|
+
if (
|
|
890
|
+
policy.customAllowedSignatureAlgorithms &&
|
|
891
|
+
policy.customAllowedSignatureAlgorithms.length > 0
|
|
892
|
+
) {
|
|
893
|
+
return policy.customAllowedSignatureAlgorithms;
|
|
894
|
+
}
|
|
895
|
+
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].signatureAlgorithms;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Get the default KEM algorithm for a tenant based on their crypto policy tier.
|
|
900
|
+
*/
|
|
901
|
+
async getDefaultKemAlgorithm(tenantId: string): Promise<string> {
|
|
902
|
+
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
903
|
+
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultKemAlgorithm;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Get the default signature algorithm for a tenant based on their crypto policy tier.
|
|
908
|
+
*/
|
|
909
|
+
async getDefaultSignatureAlgorithm(tenantId: string): Promise<string> {
|
|
910
|
+
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
911
|
+
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultSignatureAlgorithm;
|
|
912
|
+
}
|
|
356
913
|
}
|
|
357
914
|
|
|
358
915
|
export * from "./observability.js";
|