@qnsp/tenant-sdk 0.2.0 → 0.3.1
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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +51 -0
- package/LICENSE +21 -7
- package/README.md +17 -51
- package/dist/index.d.ts +116 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +409 -16
- package/dist/index.js.map +1 -1
- package/package.json +58 -39
- package/src/index.test.ts +73 -1
- package/src/index.ts +565 -17
- package/tsconfig.tsbuildinfo +1 -1
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
2
|
|
|
3
|
+
import { activateSdk, type SdkActivationConfig } from "@qnsp/sdk-activation";
|
|
4
|
+
|
|
3
5
|
import type {
|
|
4
6
|
TenantClientTelemetry,
|
|
5
7
|
TenantClientTelemetryConfig,
|
|
@@ -17,7 +19,7 @@ import { validateUUID } from "./validation.js";
|
|
|
17
19
|
|
|
18
20
|
export interface TenantClientConfig {
|
|
19
21
|
readonly baseUrl: string;
|
|
20
|
-
readonly apiKey
|
|
22
|
+
readonly apiKey: string;
|
|
21
23
|
readonly timeoutMs?: number;
|
|
22
24
|
readonly telemetry?: TenantClientTelemetry | TenantClientTelemetryConfig;
|
|
23
25
|
readonly maxRetries?: number;
|
|
@@ -34,12 +36,102 @@ type InternalTenantClientConfig = {
|
|
|
34
36
|
|
|
35
37
|
export type TenantStatus = "active" | "suspended" | "pending" | "deleted";
|
|
36
38
|
|
|
39
|
+
export type HsmMode = "none" | "supported" | "required";
|
|
40
|
+
|
|
37
41
|
/**
|
|
38
42
|
* Crypto policy tier determines which PQC algorithms are allowed.
|
|
39
43
|
* Maps to tenant_crypto_policies.policy_tier in tenant-service.
|
|
40
44
|
*/
|
|
41
45
|
export type CryptoPolicyTier = "default" | "strict" | "maximum" | "government";
|
|
42
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Crypto policy v1 uses profiles + tiers (evidence-first model).
|
|
49
|
+
*/
|
|
50
|
+
export type QnspTier =
|
|
51
|
+
| "TIER0_LEGACY"
|
|
52
|
+
| "TIER1_APPROVED"
|
|
53
|
+
| "TIER2_HIGH_ASSURANCE"
|
|
54
|
+
| "TIER3_DIVERSITY"
|
|
55
|
+
| "TIER4_EXPERIMENTAL";
|
|
56
|
+
|
|
57
|
+
export type TenantType =
|
|
58
|
+
| "FREE_FOREVER"
|
|
59
|
+
| "DEV_STARTER"
|
|
60
|
+
| "DEV_PRO"
|
|
61
|
+
| "DEV_ELITE"
|
|
62
|
+
| "BUSINESS_TEAM"
|
|
63
|
+
| "BUSINESS_ADVANCED"
|
|
64
|
+
| "BUSINESS_ELITE"
|
|
65
|
+
| "ENTERPRISE_STANDARD"
|
|
66
|
+
| "ENTERPRISE_PRO"
|
|
67
|
+
| "ENTERPRISE_ELITE"
|
|
68
|
+
| "PUBLIC_SECTOR";
|
|
69
|
+
|
|
70
|
+
export type CryptoProfileId =
|
|
71
|
+
| "gov-high-assurance"
|
|
72
|
+
| "defense-long-life-data"
|
|
73
|
+
| "financial-hybrid-pqc"
|
|
74
|
+
| "research-eval";
|
|
75
|
+
|
|
76
|
+
export type TenantCryptoPolicyV1Action = "CREATE" | "UPDATE" | "ROLLBACK";
|
|
77
|
+
|
|
78
|
+
export interface CryptoPolicyV1 {
|
|
79
|
+
readonly version: "v1";
|
|
80
|
+
readonly tenantType: TenantType;
|
|
81
|
+
readonly profile: CryptoProfileId;
|
|
82
|
+
readonly enabledTiers: readonly QnspTier[];
|
|
83
|
+
readonly tier0Expiry?: string;
|
|
84
|
+
readonly tier4Acknowledgement?: {
|
|
85
|
+
readonly nonCompliant: true;
|
|
86
|
+
readonly noProductionSecrets: true;
|
|
87
|
+
readonly approvedBy: string;
|
|
88
|
+
readonly approvedAt: string;
|
|
89
|
+
};
|
|
90
|
+
readonly overrides?: {
|
|
91
|
+
readonly allowFalcon?: boolean;
|
|
92
|
+
};
|
|
93
|
+
readonly requirements: {
|
|
94
|
+
readonly fipsAligned: boolean;
|
|
95
|
+
readonly evidenceRequired: boolean;
|
|
96
|
+
readonly cryptoAgilityMetadataRequired: boolean;
|
|
97
|
+
readonly statefulLifecycleGuards: boolean;
|
|
98
|
+
readonly downgradeDetection: boolean;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface TenantCryptoPolicyV1Record {
|
|
103
|
+
readonly id: string;
|
|
104
|
+
readonly tenantId: string;
|
|
105
|
+
readonly version: "v1";
|
|
106
|
+
readonly policy: CryptoPolicyV1;
|
|
107
|
+
readonly policyHash: string;
|
|
108
|
+
readonly etag: string;
|
|
109
|
+
readonly createdAt: string;
|
|
110
|
+
readonly createdByPrincipal: string;
|
|
111
|
+
readonly createdByIp: string | null;
|
|
112
|
+
readonly updatedAt: string;
|
|
113
|
+
readonly updatedByPrincipal: string;
|
|
114
|
+
readonly updatedByIp: string | null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface TenantCryptoPolicyV1HistoryRecord {
|
|
118
|
+
readonly id: string;
|
|
119
|
+
readonly tenantId: string;
|
|
120
|
+
readonly version: "v1";
|
|
121
|
+
readonly policy: CryptoPolicyV1;
|
|
122
|
+
readonly policyHash: string;
|
|
123
|
+
readonly action: TenantCryptoPolicyV1Action;
|
|
124
|
+
readonly reason: string | null;
|
|
125
|
+
readonly changedAt: string;
|
|
126
|
+
readonly changedByPrincipal: string;
|
|
127
|
+
readonly changedByIp: string | null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface TenantCryptoPolicyV1HistoryResponse {
|
|
131
|
+
readonly tenantId: string;
|
|
132
|
+
readonly items: readonly TenantCryptoPolicyV1HistoryRecord[];
|
|
133
|
+
}
|
|
134
|
+
|
|
43
135
|
/**
|
|
44
136
|
* Tenant crypto policy configuration.
|
|
45
137
|
*/
|
|
@@ -80,48 +172,272 @@ export interface TierAlgorithmConfig {
|
|
|
80
172
|
|
|
81
173
|
/**
|
|
82
174
|
* Default algorithms per crypto policy tier.
|
|
175
|
+
* These match the definitions in packages/security/src/crypto-policy.ts
|
|
176
|
+
* and determine which algorithms appear in the portal's Generate Key dropdown.
|
|
177
|
+
*
|
|
178
|
+
* default: All supported PQC algorithms (NIST-finalized + candidates via liboqs)
|
|
179
|
+
* strict: NIST-finalized/selected at higher security levels
|
|
180
|
+
* maximum: Highest-security NIST-finalized only
|
|
181
|
+
* government: FIPS-finalized only (no draft standards)
|
|
83
182
|
*/
|
|
84
183
|
export const CRYPTO_POLICY_ALGORITHMS: Record<CryptoPolicyTier, TierAlgorithmConfig> = {
|
|
85
184
|
default: {
|
|
86
|
-
kemAlgorithms: [
|
|
87
|
-
|
|
185
|
+
kemAlgorithms: [
|
|
186
|
+
// FIPS 203 — ML-KEM (NIST finalized)
|
|
187
|
+
"kyber-512",
|
|
188
|
+
"kyber-768",
|
|
189
|
+
"kyber-1024",
|
|
190
|
+
// HQC (NIST selected March 2025)
|
|
191
|
+
"hqc-128",
|
|
192
|
+
"hqc-192",
|
|
193
|
+
"hqc-256",
|
|
194
|
+
// BIKE (NIST Round 4 candidate)
|
|
195
|
+
"bike-l1",
|
|
196
|
+
"bike-l3",
|
|
197
|
+
"bike-l5",
|
|
198
|
+
// Classic McEliece (ISO standard)
|
|
199
|
+
"mceliece-348864",
|
|
200
|
+
"mceliece-460896",
|
|
201
|
+
"mceliece-6688128",
|
|
202
|
+
"mceliece-6960119",
|
|
203
|
+
"mceliece-8192128",
|
|
204
|
+
// FrodoKEM (ISO standard)
|
|
205
|
+
"frodokem-640-aes",
|
|
206
|
+
"frodokem-640-shake",
|
|
207
|
+
"frodokem-976-aes",
|
|
208
|
+
"frodokem-976-shake",
|
|
209
|
+
"frodokem-1344-aes",
|
|
210
|
+
"frodokem-1344-shake",
|
|
211
|
+
// NTRU (lattice-based)
|
|
212
|
+
"ntru-hps-2048-509",
|
|
213
|
+
"ntru-hps-2048-677",
|
|
214
|
+
"ntru-hps-4096-821",
|
|
215
|
+
"ntru-hps-4096-1229",
|
|
216
|
+
"ntru-hrss-701",
|
|
217
|
+
"ntru-hrss-1373",
|
|
218
|
+
// NTRU-Prime
|
|
219
|
+
"sntrup761",
|
|
220
|
+
],
|
|
221
|
+
signatureAlgorithms: [
|
|
222
|
+
// FIPS 204 — ML-DSA (NIST finalized)
|
|
223
|
+
"dilithium-2",
|
|
224
|
+
"dilithium-3",
|
|
225
|
+
"dilithium-5",
|
|
226
|
+
// FIPS 205 — SLH-DSA (NIST finalized, SHA-2 variants)
|
|
227
|
+
"sphincs-sha2-128f-simple",
|
|
228
|
+
"sphincs-sha2-128s-simple",
|
|
229
|
+
"sphincs-sha2-192f-simple",
|
|
230
|
+
"sphincs-sha2-192s-simple",
|
|
231
|
+
"sphincs-sha2-256f-simple",
|
|
232
|
+
"sphincs-sha2-256s-simple",
|
|
233
|
+
// FIPS 205 — SLH-DSA (NIST finalized, SHAKE variants)
|
|
234
|
+
"sphincs-shake-128f-simple",
|
|
235
|
+
"sphincs-shake-128s-simple",
|
|
236
|
+
"sphincs-shake-192f-simple",
|
|
237
|
+
"sphincs-shake-192s-simple",
|
|
238
|
+
"sphincs-shake-256f-simple",
|
|
239
|
+
"sphincs-shake-256s-simple",
|
|
240
|
+
// FN-DSA / Falcon (FIPS 206 draft)
|
|
241
|
+
"falcon-512",
|
|
242
|
+
"falcon-1024",
|
|
243
|
+
// MAYO (NIST Additional Signatures Round 2)
|
|
244
|
+
"mayo-1",
|
|
245
|
+
"mayo-2",
|
|
246
|
+
"mayo-3",
|
|
247
|
+
"mayo-5",
|
|
248
|
+
// CROSS (NIST Additional Signatures Round 2)
|
|
249
|
+
"cross-rsdp-128-balanced",
|
|
250
|
+
"cross-rsdp-128-fast",
|
|
251
|
+
"cross-rsdp-128-small",
|
|
252
|
+
"cross-rsdp-192-balanced",
|
|
253
|
+
"cross-rsdp-192-fast",
|
|
254
|
+
"cross-rsdp-192-small",
|
|
255
|
+
"cross-rsdp-256-balanced",
|
|
256
|
+
"cross-rsdp-256-fast",
|
|
257
|
+
"cross-rsdp-256-small",
|
|
258
|
+
"cross-rsdpg-128-balanced",
|
|
259
|
+
"cross-rsdpg-128-fast",
|
|
260
|
+
"cross-rsdpg-128-small",
|
|
261
|
+
"cross-rsdpg-192-balanced",
|
|
262
|
+
"cross-rsdpg-192-fast",
|
|
263
|
+
"cross-rsdpg-192-small",
|
|
264
|
+
"cross-rsdpg-256-balanced",
|
|
265
|
+
"cross-rsdpg-256-fast",
|
|
266
|
+
"cross-rsdpg-256-small",
|
|
267
|
+
// UOV (NIST Additional Signatures Round 2)
|
|
268
|
+
"ov-Is",
|
|
269
|
+
"ov-Ip",
|
|
270
|
+
"ov-III",
|
|
271
|
+
"ov-V",
|
|
272
|
+
"ov-Is-pkc",
|
|
273
|
+
"ov-Ip-pkc",
|
|
274
|
+
"ov-III-pkc",
|
|
275
|
+
"ov-V-pkc",
|
|
276
|
+
"ov-Is-pkc-skc",
|
|
277
|
+
"ov-Ip-pkc-skc",
|
|
278
|
+
"ov-III-pkc-skc",
|
|
279
|
+
"ov-V-pkc-skc",
|
|
280
|
+
// SNOVA (NIST Additional Signatures Round 2)
|
|
281
|
+
"snova-24-5-4",
|
|
282
|
+
"snova-24-5-4-shake",
|
|
283
|
+
"snova-24-5-4-esk",
|
|
284
|
+
"snova-24-5-4-shake-esk",
|
|
285
|
+
"snova-25-8-3",
|
|
286
|
+
"snova-37-17-2",
|
|
287
|
+
"snova-37-8-4",
|
|
288
|
+
"snova-24-5-5",
|
|
289
|
+
"snova-56-25-2",
|
|
290
|
+
"snova-49-11-3",
|
|
291
|
+
"snova-60-10-4",
|
|
292
|
+
"snova-29-6-5",
|
|
293
|
+
],
|
|
88
294
|
defaultKemAlgorithm: "kyber-768",
|
|
89
295
|
defaultSignatureAlgorithm: "dilithium-3",
|
|
90
296
|
},
|
|
91
297
|
strict: {
|
|
92
|
-
kemAlgorithms: ["kyber-768", "kyber-1024"],
|
|
93
|
-
signatureAlgorithms: [
|
|
298
|
+
kemAlgorithms: ["kyber-768", "kyber-1024", "hqc-192", "hqc-256"],
|
|
299
|
+
signatureAlgorithms: [
|
|
300
|
+
"dilithium-3",
|
|
301
|
+
"dilithium-5",
|
|
302
|
+
"falcon-1024",
|
|
303
|
+
"sphincs-sha2-256f-simple",
|
|
304
|
+
"sphincs-sha2-256s-simple",
|
|
305
|
+
"sphincs-shake-256f-simple",
|
|
306
|
+
"sphincs-shake-256s-simple",
|
|
307
|
+
],
|
|
94
308
|
defaultKemAlgorithm: "kyber-768",
|
|
95
309
|
defaultSignatureAlgorithm: "dilithium-3",
|
|
96
310
|
},
|
|
97
311
|
maximum: {
|
|
98
|
-
kemAlgorithms: ["kyber-1024"],
|
|
99
|
-
signatureAlgorithms: [
|
|
312
|
+
kemAlgorithms: ["kyber-1024", "hqc-256"],
|
|
313
|
+
signatureAlgorithms: [
|
|
314
|
+
"dilithium-5",
|
|
315
|
+
"falcon-1024",
|
|
316
|
+
"sphincs-sha2-256f-simple",
|
|
317
|
+
"sphincs-shake-256f-simple",
|
|
318
|
+
],
|
|
100
319
|
defaultKemAlgorithm: "kyber-1024",
|
|
101
320
|
defaultSignatureAlgorithm: "dilithium-5",
|
|
102
321
|
},
|
|
103
322
|
government: {
|
|
104
323
|
kemAlgorithms: ["kyber-1024"],
|
|
105
|
-
signatureAlgorithms: ["dilithium-5", "sphincs-shake-256f-simple"],
|
|
324
|
+
signatureAlgorithms: ["dilithium-5", "sphincs-sha2-256f-simple", "sphincs-shake-256f-simple"],
|
|
106
325
|
defaultKemAlgorithm: "kyber-1024",
|
|
107
326
|
defaultSignatureAlgorithm: "dilithium-5",
|
|
108
327
|
},
|
|
109
328
|
};
|
|
110
329
|
|
|
111
330
|
/**
|
|
112
|
-
* Mapping from internal algorithm names to NIST
|
|
331
|
+
* Mapping from internal algorithm names to NIST/standards display names.
|
|
332
|
+
* Covers all 90 PQC algorithms supported by QNSP.
|
|
333
|
+
* Canonical source: @qnsp/cryptography pqc-standards.ts ALGORITHM_NIST_NAMES
|
|
113
334
|
*/
|
|
114
335
|
export const ALGORITHM_TO_NIST: Record<string, string> = {
|
|
336
|
+
// FIPS 203 — ML-KEM
|
|
115
337
|
"kyber-512": "ML-KEM-512",
|
|
116
338
|
"kyber-768": "ML-KEM-768",
|
|
117
339
|
"kyber-1024": "ML-KEM-1024",
|
|
340
|
+
// FIPS 204 — ML-DSA
|
|
118
341
|
"dilithium-2": "ML-DSA-44",
|
|
119
342
|
"dilithium-3": "ML-DSA-65",
|
|
120
343
|
"dilithium-5": "ML-DSA-87",
|
|
121
|
-
|
|
122
|
-
"
|
|
344
|
+
// FIPS 205 — SLH-DSA (SHA-2 variants)
|
|
345
|
+
"sphincs-sha2-128f-simple": "SLH-DSA-SHA2-128f",
|
|
346
|
+
"sphincs-sha2-128s-simple": "SLH-DSA-SHA2-128s",
|
|
347
|
+
"sphincs-sha2-192f-simple": "SLH-DSA-SHA2-192f",
|
|
348
|
+
"sphincs-sha2-192s-simple": "SLH-DSA-SHA2-192s",
|
|
349
|
+
"sphincs-sha2-256f-simple": "SLH-DSA-SHA2-256f",
|
|
350
|
+
"sphincs-sha2-256s-simple": "SLH-DSA-SHA2-256s",
|
|
351
|
+
// FIPS 205 — SLH-DSA (SHAKE variants)
|
|
123
352
|
"sphincs-shake-128f-simple": "SLH-DSA-SHAKE-128f",
|
|
353
|
+
"sphincs-shake-128s-simple": "SLH-DSA-SHAKE-128s",
|
|
354
|
+
"sphincs-shake-192f-simple": "SLH-DSA-SHAKE-192f",
|
|
355
|
+
"sphincs-shake-192s-simple": "SLH-DSA-SHAKE-192s",
|
|
124
356
|
"sphincs-shake-256f-simple": "SLH-DSA-SHAKE-256f",
|
|
357
|
+
"sphincs-shake-256s-simple": "SLH-DSA-SHAKE-256s",
|
|
358
|
+
// FN-DSA (FIPS 206 draft)
|
|
359
|
+
"falcon-512": "FN-DSA-512",
|
|
360
|
+
"falcon-1024": "FN-DSA-1024",
|
|
361
|
+
// HQC (NIST selected March 2025)
|
|
362
|
+
"hqc-128": "HQC-128",
|
|
363
|
+
"hqc-192": "HQC-192",
|
|
364
|
+
"hqc-256": "HQC-256",
|
|
365
|
+
// BIKE (NIST Round 4)
|
|
366
|
+
"bike-l1": "BIKE-L1",
|
|
367
|
+
"bike-l3": "BIKE-L3",
|
|
368
|
+
"bike-l5": "BIKE-L5",
|
|
369
|
+
// Classic McEliece (ISO standard)
|
|
370
|
+
"mceliece-348864": "Classic-McEliece-348864",
|
|
371
|
+
"mceliece-460896": "Classic-McEliece-460896",
|
|
372
|
+
"mceliece-6688128": "Classic-McEliece-6688128",
|
|
373
|
+
"mceliece-6960119": "Classic-McEliece-6960119",
|
|
374
|
+
"mceliece-8192128": "Classic-McEliece-8192128",
|
|
375
|
+
// FrodoKEM (ISO standard)
|
|
376
|
+
"frodokem-640-aes": "FrodoKEM-640-AES",
|
|
377
|
+
"frodokem-640-shake": "FrodoKEM-640-SHAKE",
|
|
378
|
+
"frodokem-976-aes": "FrodoKEM-976-AES",
|
|
379
|
+
"frodokem-976-shake": "FrodoKEM-976-SHAKE",
|
|
380
|
+
"frodokem-1344-aes": "FrodoKEM-1344-AES",
|
|
381
|
+
"frodokem-1344-shake": "FrodoKEM-1344-SHAKE",
|
|
382
|
+
// NTRU (lattice-based, re-added in liboqs 0.15)
|
|
383
|
+
"ntru-hps-2048-509": "NTRU-HPS-2048-509",
|
|
384
|
+
"ntru-hps-2048-677": "NTRU-HPS-2048-677",
|
|
385
|
+
"ntru-hps-4096-821": "NTRU-HPS-4096-821",
|
|
386
|
+
"ntru-hps-4096-1229": "NTRU-HPS-4096-1229",
|
|
387
|
+
"ntru-hrss-701": "NTRU-HRSS-701",
|
|
388
|
+
"ntru-hrss-1373": "NTRU-HRSS-1373",
|
|
389
|
+
// NTRU-Prime
|
|
390
|
+
sntrup761: "sntrup761",
|
|
391
|
+
// MAYO (NIST Additional Signatures Round 2)
|
|
392
|
+
"mayo-1": "MAYO-1",
|
|
393
|
+
"mayo-2": "MAYO-2",
|
|
394
|
+
"mayo-3": "MAYO-3",
|
|
395
|
+
"mayo-5": "MAYO-5",
|
|
396
|
+
// CROSS (NIST Additional Signatures Round 2)
|
|
397
|
+
"cross-rsdp-128-balanced": "CROSS-RSDP-128-balanced",
|
|
398
|
+
"cross-rsdp-128-fast": "CROSS-RSDP-128-fast",
|
|
399
|
+
"cross-rsdp-128-small": "CROSS-RSDP-128-small",
|
|
400
|
+
"cross-rsdp-192-balanced": "CROSS-RSDP-192-balanced",
|
|
401
|
+
"cross-rsdp-192-fast": "CROSS-RSDP-192-fast",
|
|
402
|
+
"cross-rsdp-192-small": "CROSS-RSDP-192-small",
|
|
403
|
+
"cross-rsdp-256-balanced": "CROSS-RSDP-256-balanced",
|
|
404
|
+
"cross-rsdp-256-fast": "CROSS-RSDP-256-fast",
|
|
405
|
+
"cross-rsdp-256-small": "CROSS-RSDP-256-small",
|
|
406
|
+
"cross-rsdpg-128-balanced": "CROSS-RSDPG-128-balanced",
|
|
407
|
+
"cross-rsdpg-128-fast": "CROSS-RSDPG-128-fast",
|
|
408
|
+
"cross-rsdpg-128-small": "CROSS-RSDPG-128-small",
|
|
409
|
+
"cross-rsdpg-192-balanced": "CROSS-RSDPG-192-balanced",
|
|
410
|
+
"cross-rsdpg-192-fast": "CROSS-RSDPG-192-fast",
|
|
411
|
+
"cross-rsdpg-192-small": "CROSS-RSDPG-192-small",
|
|
412
|
+
"cross-rsdpg-256-balanced": "CROSS-RSDPG-256-balanced",
|
|
413
|
+
"cross-rsdpg-256-fast": "CROSS-RSDPG-256-fast",
|
|
414
|
+
"cross-rsdpg-256-small": "CROSS-RSDPG-256-small",
|
|
415
|
+
// UOV (NIST Additional Signatures Round 2)
|
|
416
|
+
"ov-Is": "UOV-Is",
|
|
417
|
+
"ov-Ip": "UOV-Ip",
|
|
418
|
+
"ov-III": "UOV-III",
|
|
419
|
+
"ov-V": "UOV-V",
|
|
420
|
+
"ov-Is-pkc": "UOV-Is-pkc",
|
|
421
|
+
"ov-Ip-pkc": "UOV-Ip-pkc",
|
|
422
|
+
"ov-III-pkc": "UOV-III-pkc",
|
|
423
|
+
"ov-V-pkc": "UOV-V-pkc",
|
|
424
|
+
"ov-Is-pkc-skc": "UOV-Is-pkc-skc",
|
|
425
|
+
"ov-Ip-pkc-skc": "UOV-Ip-pkc-skc",
|
|
426
|
+
"ov-III-pkc-skc": "UOV-III-pkc-skc",
|
|
427
|
+
"ov-V-pkc-skc": "UOV-V-pkc-skc",
|
|
428
|
+
// SNOVA (NIST Additional Signatures Round 2, liboqs 0.14+)
|
|
429
|
+
"snova-24-5-4": "SNOVA-24-5-4",
|
|
430
|
+
"snova-24-5-4-shake": "SNOVA-24-5-4-SHAKE",
|
|
431
|
+
"snova-24-5-4-esk": "SNOVA-24-5-4-ESK",
|
|
432
|
+
"snova-24-5-4-shake-esk": "SNOVA-24-5-4-SHAKE-ESK",
|
|
433
|
+
"snova-25-8-3": "SNOVA-25-8-3",
|
|
434
|
+
"snova-37-17-2": "SNOVA-37-17-2",
|
|
435
|
+
"snova-37-8-4": "SNOVA-37-8-4",
|
|
436
|
+
"snova-24-5-5": "SNOVA-24-5-5",
|
|
437
|
+
"snova-56-25-2": "SNOVA-56-25-2",
|
|
438
|
+
"snova-49-11-3": "SNOVA-49-11-3",
|
|
439
|
+
"snova-60-10-4": "SNOVA-60-10-4",
|
|
440
|
+
"snova-29-6-5": "SNOVA-29-6-5",
|
|
125
441
|
};
|
|
126
442
|
|
|
127
443
|
/**
|
|
@@ -174,6 +490,7 @@ export interface Tenant {
|
|
|
174
490
|
readonly plan: string;
|
|
175
491
|
readonly region: string;
|
|
176
492
|
readonly complianceTags: readonly string[];
|
|
493
|
+
readonly hsmMode: HsmMode;
|
|
177
494
|
readonly metadata: Record<string, unknown>;
|
|
178
495
|
readonly security: TenantSecurityEnvelope;
|
|
179
496
|
readonly domains: readonly TenantDomain[];
|
|
@@ -187,6 +504,7 @@ export interface CreateTenantRequest {
|
|
|
187
504
|
readonly plan?: string;
|
|
188
505
|
readonly region?: string;
|
|
189
506
|
readonly complianceTags?: readonly string[];
|
|
507
|
+
readonly hsmMode?: HsmMode;
|
|
190
508
|
readonly metadata?: Record<string, unknown>;
|
|
191
509
|
readonly domains?: readonly {
|
|
192
510
|
readonly domain: string;
|
|
@@ -200,6 +518,7 @@ export interface UpdateTenantRequest {
|
|
|
200
518
|
readonly plan?: string;
|
|
201
519
|
readonly status?: TenantStatus;
|
|
202
520
|
readonly complianceTags?: readonly string[];
|
|
521
|
+
readonly hsmMode?: HsmMode;
|
|
203
522
|
readonly metadata?: Record<string, unknown>;
|
|
204
523
|
readonly security: TenantSecurityEnvelope;
|
|
205
524
|
readonly signature?: TenantSignature;
|
|
@@ -223,17 +542,48 @@ export class TenantClient {
|
|
|
223
542
|
private readonly config: InternalTenantClientConfig;
|
|
224
543
|
private readonly telemetry: TenantClientTelemetry | null;
|
|
225
544
|
private readonly targetService: string;
|
|
545
|
+
private activationPromise: Promise<void> | null = null;
|
|
546
|
+
private readonly activationConfig: SdkActivationConfig;
|
|
547
|
+
|
|
548
|
+
private async ensureActivated(): Promise<void> {
|
|
549
|
+
if (!this.activationPromise) {
|
|
550
|
+
this.activationPromise = activateSdk(this.activationConfig).then(() => undefined);
|
|
551
|
+
}
|
|
552
|
+
await this.activationPromise;
|
|
553
|
+
}
|
|
226
554
|
|
|
227
555
|
constructor(config: TenantClientConfig) {
|
|
556
|
+
if (!config.apiKey || config.apiKey.trim().length === 0) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
"QNSP Tenant SDK: apiKey is required. " +
|
|
559
|
+
"Get your free API key at https://cloud.qnsp.cuilabs.io/signup — " +
|
|
560
|
+
"no credit card required (FREE tier: 10 GB storage, 50,000 API calls/month). " +
|
|
561
|
+
"Docs: https://docs.qnsp.cuilabs.io/sdk/tenant-sdk",
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
228
565
|
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
229
566
|
|
|
230
|
-
// Enforce HTTPS in production (allow HTTP
|
|
567
|
+
// Enforce HTTPS in production (allow HTTP for localhost in development and
|
|
568
|
+
// for internal service-mesh hostnames — e.g. *.internal — which are on a
|
|
569
|
+
// private VPC network and do not require TLS termination at the transport layer).
|
|
231
570
|
if (!baseUrl.startsWith("https://")) {
|
|
232
571
|
const isLocalhost =
|
|
233
572
|
baseUrl.startsWith("http://localhost") || baseUrl.startsWith("http://127.0.0.1");
|
|
573
|
+
let isInternalService = false;
|
|
574
|
+
try {
|
|
575
|
+
const parsed = new URL(baseUrl);
|
|
576
|
+
isInternalService =
|
|
577
|
+
parsed.protocol === "http:" &&
|
|
578
|
+
(parsed.hostname.endsWith(".internal") ||
|
|
579
|
+
parsed.hostname === "localhost" ||
|
|
580
|
+
parsed.hostname === "127.0.0.1");
|
|
581
|
+
} catch {
|
|
582
|
+
// ignore; invalid URL will be caught later by fetch
|
|
583
|
+
}
|
|
234
584
|
const isDevelopment =
|
|
235
585
|
process.env["NODE_ENV"] === "development" || process.env["NODE_ENV"] === "test";
|
|
236
|
-
if (!isLocalhost || !isDevelopment) {
|
|
586
|
+
if ((!isLocalhost || !isDevelopment) && !isInternalService) {
|
|
237
587
|
throw new Error(
|
|
238
588
|
"baseUrl must use HTTPS in production. HTTP is only allowed for localhost in development.",
|
|
239
589
|
);
|
|
@@ -242,7 +592,7 @@ export class TenantClient {
|
|
|
242
592
|
|
|
243
593
|
this.config = {
|
|
244
594
|
baseUrl,
|
|
245
|
-
apiKey: config.apiKey
|
|
595
|
+
apiKey: config.apiKey,
|
|
246
596
|
timeoutMs: config.timeoutMs ?? 30_000,
|
|
247
597
|
maxRetries: config.maxRetries ?? 3,
|
|
248
598
|
retryDelayMs: config.retryDelayMs ?? 1_000,
|
|
@@ -259,6 +609,13 @@ export class TenantClient {
|
|
|
259
609
|
} catch {
|
|
260
610
|
this.targetService = "tenant-service";
|
|
261
611
|
}
|
|
612
|
+
|
|
613
|
+
this.activationConfig = {
|
|
614
|
+
apiKey: config.apiKey,
|
|
615
|
+
sdkId: "tenant-sdk",
|
|
616
|
+
sdkVersion: "0.3.0",
|
|
617
|
+
platformUrl: config.baseUrl,
|
|
618
|
+
};
|
|
262
619
|
}
|
|
263
620
|
|
|
264
621
|
private async request<T>(method: string, path: string, options?: RequestOptions): Promise<T> {
|
|
@@ -277,9 +634,7 @@ export class TenantClient {
|
|
|
277
634
|
...options?.headers,
|
|
278
635
|
};
|
|
279
636
|
|
|
280
|
-
|
|
281
|
-
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
282
|
-
}
|
|
637
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
283
638
|
|
|
284
639
|
const controller = new AbortController();
|
|
285
640
|
const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
@@ -386,6 +741,7 @@ export class TenantClient {
|
|
|
386
741
|
* Requires PQC-signed security envelope and optional signature.
|
|
387
742
|
*/
|
|
388
743
|
async createTenant(request: CreateTenantRequest): Promise<Tenant> {
|
|
744
|
+
await this.ensureActivated();
|
|
389
745
|
// Validation is handled by the service, but we validate format here for early feedback
|
|
390
746
|
return this.request<Tenant>("POST", "/tenant/v1/tenants", {
|
|
391
747
|
body: {
|
|
@@ -394,6 +750,7 @@ export class TenantClient {
|
|
|
394
750
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
395
751
|
...(request.region !== undefined ? { region: request.region } : {}),
|
|
396
752
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
753
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
397
754
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
398
755
|
...(request.domains !== undefined ? { domains: request.domains } : {}),
|
|
399
756
|
security: request.security,
|
|
@@ -409,12 +766,14 @@ export class TenantClient {
|
|
|
409
766
|
*/
|
|
410
767
|
async updateTenant(id: string, request: UpdateTenantRequest): Promise<Tenant> {
|
|
411
768
|
validateUUID(id, "id");
|
|
769
|
+
await this.ensureActivated();
|
|
412
770
|
|
|
413
771
|
return this.request<Tenant>("PATCH", `/tenant/v1/tenants/${id}`, {
|
|
414
772
|
body: {
|
|
415
773
|
...(request.plan !== undefined ? { plan: request.plan } : {}),
|
|
416
774
|
...(request.status !== undefined ? { status: request.status } : {}),
|
|
417
775
|
...(request.complianceTags !== undefined ? { complianceTags: request.complianceTags } : {}),
|
|
776
|
+
...(request.hsmMode !== undefined ? { hsmMode: request.hsmMode } : {}),
|
|
418
777
|
...(request.metadata !== undefined ? { metadata: request.metadata } : {}),
|
|
419
778
|
security: request.security,
|
|
420
779
|
...(request.signature !== undefined ? { signature: request.signature } : {}),
|
|
@@ -429,6 +788,7 @@ export class TenantClient {
|
|
|
429
788
|
*/
|
|
430
789
|
async getTenant(id: string): Promise<Tenant> {
|
|
431
790
|
validateUUID(id, "id");
|
|
791
|
+
await this.ensureActivated();
|
|
432
792
|
|
|
433
793
|
return this.request<Tenant>("GET", `/tenant/v1/tenants/${id}`, {
|
|
434
794
|
operation: "getTenant",
|
|
@@ -443,6 +803,7 @@ export class TenantClient {
|
|
|
443
803
|
readonly limit?: number;
|
|
444
804
|
readonly cursor?: string;
|
|
445
805
|
}): Promise<ListTenantsResponse> {
|
|
806
|
+
await this.ensureActivated();
|
|
446
807
|
const params = new URLSearchParams();
|
|
447
808
|
if (options?.limit !== undefined) {
|
|
448
809
|
params.set("limit", String(options.limit));
|
|
@@ -465,12 +826,53 @@ export class TenantClient {
|
|
|
465
826
|
*/
|
|
466
827
|
async getTenantCryptoPolicy(tenantId: string): Promise<TenantCryptoPolicy> {
|
|
467
828
|
validateUUID(tenantId, "tenantId");
|
|
829
|
+
await this.ensureActivated();
|
|
468
830
|
|
|
469
831
|
return this.request<TenantCryptoPolicy>("GET", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
470
832
|
operation: "getTenantCryptoPolicy",
|
|
471
833
|
});
|
|
472
834
|
}
|
|
473
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Get the v1 crypto policy for a tenant (profiles + tiers model).
|
|
838
|
+
* If no policy exists, a default policy is created and returned.
|
|
839
|
+
*/
|
|
840
|
+
async getTenantCryptoPolicyV1(tenantId: string): Promise<TenantCryptoPolicyV1Record> {
|
|
841
|
+
validateUUID(tenantId, "tenantId");
|
|
842
|
+
await this.ensureActivated();
|
|
843
|
+
|
|
844
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
845
|
+
"GET",
|
|
846
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
847
|
+
{
|
|
848
|
+
operation: "getTenantCryptoPolicyV1",
|
|
849
|
+
},
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* List v1 crypto policy history entries.
|
|
855
|
+
*/
|
|
856
|
+
async listTenantCryptoPolicyV1History(
|
|
857
|
+
tenantId: string,
|
|
858
|
+
options?: { readonly limit?: number },
|
|
859
|
+
): Promise<TenantCryptoPolicyV1HistoryResponse> {
|
|
860
|
+
validateUUID(tenantId, "tenantId");
|
|
861
|
+
await this.ensureActivated();
|
|
862
|
+
const params = new URLSearchParams();
|
|
863
|
+
if (options?.limit !== undefined) {
|
|
864
|
+
params.set("limit", String(options.limit));
|
|
865
|
+
}
|
|
866
|
+
const query = params.toString();
|
|
867
|
+
const path = query
|
|
868
|
+
? `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history?${query}`
|
|
869
|
+
: `/tenant/v1/tenants/${tenantId}/crypto-policy-v1/history`;
|
|
870
|
+
|
|
871
|
+
return this.request<TenantCryptoPolicyV1HistoryResponse>("GET", path, {
|
|
872
|
+
operation: "listTenantCryptoPolicyV1History",
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
474
876
|
/**
|
|
475
877
|
* Create or update the crypto policy for a tenant.
|
|
476
878
|
* Sets the policy tier and optional custom algorithm restrictions.
|
|
@@ -480,6 +882,7 @@ export class TenantClient {
|
|
|
480
882
|
policy: TenantCryptoPolicyInput,
|
|
481
883
|
): Promise<TenantCryptoPolicy> {
|
|
482
884
|
validateUUID(tenantId, "tenantId");
|
|
885
|
+
await this.ensureActivated();
|
|
483
886
|
|
|
484
887
|
return this.request<TenantCryptoPolicy>("PUT", `/tenant/v1/tenants/${tenantId}/crypto-policy`, {
|
|
485
888
|
body: {
|
|
@@ -499,11 +902,150 @@ export class TenantClient {
|
|
|
499
902
|
});
|
|
500
903
|
}
|
|
501
904
|
|
|
905
|
+
/**
|
|
906
|
+
* Update the v1 crypto policy for a tenant (requires If-Match with current ETag).
|
|
907
|
+
*/
|
|
908
|
+
async updateTenantCryptoPolicyV1(
|
|
909
|
+
tenantId: string,
|
|
910
|
+
policy: CryptoPolicyV1,
|
|
911
|
+
etag: string,
|
|
912
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
913
|
+
validateUUID(tenantId, "tenantId");
|
|
914
|
+
await this.ensureActivated();
|
|
915
|
+
if (!etag) {
|
|
916
|
+
throw new Error("etag is required for updateTenantCryptoPolicyV1");
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
920
|
+
"PUT",
|
|
921
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1`,
|
|
922
|
+
{
|
|
923
|
+
body: { policy },
|
|
924
|
+
headers: {
|
|
925
|
+
"If-Match": etag,
|
|
926
|
+
},
|
|
927
|
+
operation: "updateTenantCryptoPolicyV1",
|
|
928
|
+
},
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Enable Tier0 legacy algorithms (time-bounded) for a tenant.
|
|
934
|
+
*/
|
|
935
|
+
async enableTier0Legacy(
|
|
936
|
+
tenantId: string,
|
|
937
|
+
input: { readonly expiry: string },
|
|
938
|
+
etag: string,
|
|
939
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
940
|
+
validateUUID(tenantId, "tenantId");
|
|
941
|
+
await this.ensureActivated();
|
|
942
|
+
if (!etag) {
|
|
943
|
+
throw new Error("etag is required for enableTier0Legacy");
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
947
|
+
"POST",
|
|
948
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/enable`,
|
|
949
|
+
{
|
|
950
|
+
body: { expiry: input.expiry },
|
|
951
|
+
headers: {
|
|
952
|
+
"If-Match": etag,
|
|
953
|
+
},
|
|
954
|
+
operation: "enableTier0Legacy",
|
|
955
|
+
},
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Disable Tier0 legacy algorithms for a tenant.
|
|
961
|
+
*/
|
|
962
|
+
async disableTier0Legacy(tenantId: string, etag: string): Promise<TenantCryptoPolicyV1Record> {
|
|
963
|
+
validateUUID(tenantId, "tenantId");
|
|
964
|
+
await this.ensureActivated();
|
|
965
|
+
if (!etag) {
|
|
966
|
+
throw new Error("etag is required for disableTier0Legacy");
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
970
|
+
"POST",
|
|
971
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier0/disable`,
|
|
972
|
+
{
|
|
973
|
+
body: {},
|
|
974
|
+
headers: {
|
|
975
|
+
"If-Match": etag,
|
|
976
|
+
},
|
|
977
|
+
operation: "disableTier0Legacy",
|
|
978
|
+
},
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Enable Tier4 experimental algorithms with acknowledgement.
|
|
984
|
+
*/
|
|
985
|
+
async enableTier4Experimental(
|
|
986
|
+
tenantId: string,
|
|
987
|
+
input: { readonly approvedBy: string },
|
|
988
|
+
etag: string,
|
|
989
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
990
|
+
validateUUID(tenantId, "tenantId");
|
|
991
|
+
await this.ensureActivated();
|
|
992
|
+
if (!etag) {
|
|
993
|
+
throw new Error("etag is required for enableTier4Experimental");
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
997
|
+
"POST",
|
|
998
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/tier4/enable`,
|
|
999
|
+
{
|
|
1000
|
+
body: { approvedBy: input.approvedBy },
|
|
1001
|
+
headers: {
|
|
1002
|
+
"If-Match": etag,
|
|
1003
|
+
},
|
|
1004
|
+
operation: "enableTier4Experimental",
|
|
1005
|
+
},
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Roll back the v1 crypto policy to a previous history record or policy hash.
|
|
1011
|
+
*/
|
|
1012
|
+
async rollbackTenantCryptoPolicyV1(
|
|
1013
|
+
tenantId: string,
|
|
1014
|
+
input: { readonly historyId?: string; readonly policyHash?: string },
|
|
1015
|
+
etag: string,
|
|
1016
|
+
): Promise<TenantCryptoPolicyV1Record> {
|
|
1017
|
+
validateUUID(tenantId, "tenantId");
|
|
1018
|
+
await this.ensureActivated();
|
|
1019
|
+
if (!etag) {
|
|
1020
|
+
throw new Error("etag is required for rollbackTenantCryptoPolicyV1");
|
|
1021
|
+
}
|
|
1022
|
+
if (!input.historyId && !input.policyHash) {
|
|
1023
|
+
throw new Error("historyId or policyHash is required for rollbackTenantCryptoPolicyV1");
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
return this.request<TenantCryptoPolicyV1Record>(
|
|
1027
|
+
"POST",
|
|
1028
|
+
`/tenant/v1/tenants/${tenantId}/crypto-policy-v1/rollback`,
|
|
1029
|
+
{
|
|
1030
|
+
body: {
|
|
1031
|
+
...(input.historyId ? { historyId: input.historyId } : {}),
|
|
1032
|
+
...(input.policyHash ? { policyHash: input.policyHash } : {}),
|
|
1033
|
+
},
|
|
1034
|
+
headers: {
|
|
1035
|
+
"If-Match": etag,
|
|
1036
|
+
},
|
|
1037
|
+
operation: "rollbackTenantCryptoPolicyV1",
|
|
1038
|
+
},
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
502
1042
|
/**
|
|
503
1043
|
* Get the allowed KEM algorithms for a tenant based on their crypto policy.
|
|
504
1044
|
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
505
1045
|
*/
|
|
506
1046
|
async getAllowedKemAlgorithms(tenantId: string): Promise<readonly string[]> {
|
|
1047
|
+
validateUUID(tenantId, "tenantId");
|
|
1048
|
+
await this.ensureActivated();
|
|
507
1049
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
508
1050
|
if (policy.customAllowedKemAlgorithms && policy.customAllowedKemAlgorithms.length > 0) {
|
|
509
1051
|
return policy.customAllowedKemAlgorithms;
|
|
@@ -516,6 +1058,8 @@ export class TenantClient {
|
|
|
516
1058
|
* Convenience method that fetches the policy and returns the allowed algorithms.
|
|
517
1059
|
*/
|
|
518
1060
|
async getAllowedSignatureAlgorithms(tenantId: string): Promise<readonly string[]> {
|
|
1061
|
+
validateUUID(tenantId, "tenantId");
|
|
1062
|
+
await this.ensureActivated();
|
|
519
1063
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
520
1064
|
if (
|
|
521
1065
|
policy.customAllowedSignatureAlgorithms &&
|
|
@@ -530,6 +1074,8 @@ export class TenantClient {
|
|
|
530
1074
|
* Get the default KEM algorithm for a tenant based on their crypto policy tier.
|
|
531
1075
|
*/
|
|
532
1076
|
async getDefaultKemAlgorithm(tenantId: string): Promise<string> {
|
|
1077
|
+
validateUUID(tenantId, "tenantId");
|
|
1078
|
+
await this.ensureActivated();
|
|
533
1079
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
534
1080
|
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultKemAlgorithm;
|
|
535
1081
|
}
|
|
@@ -538,6 +1084,8 @@ export class TenantClient {
|
|
|
538
1084
|
* Get the default signature algorithm for a tenant based on their crypto policy tier.
|
|
539
1085
|
*/
|
|
540
1086
|
async getDefaultSignatureAlgorithm(tenantId: string): Promise<string> {
|
|
1087
|
+
validateUUID(tenantId, "tenantId");
|
|
1088
|
+
await this.ensureActivated();
|
|
541
1089
|
const policy = await this.getTenantCryptoPolicy(tenantId);
|
|
542
1090
|
return CRYPTO_POLICY_ALGORITHMS[policy.policyTier].defaultSignatureAlgorithm;
|
|
543
1091
|
}
|