@super-protocol/pki-attestation-engine 0.0.0-nvidia-local-verifier
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/dist/attestationType.d.ts +4 -0
- package/dist/attestationType.js +61 -0
- package/dist/azure.d.ts +40 -0
- package/dist/azure.js +128 -0
- package/dist/certificateGenerator.d.ts +34 -0
- package/dist/certificateGenerator.js +347 -0
- package/dist/challengeProvider/challengeNvidiaCCHelper.d.ts +33 -0
- package/dist/challengeProvider/challengeNvidiaCCHelper.js +254 -0
- package/dist/challengeProvider/challengeProvider.d.ts +6 -0
- package/dist/challengeProvider/challengeProvider.js +3 -0
- package/dist/challengeProvider/challengeProviderAzure.d.ts +11 -0
- package/dist/challengeProvider/challengeProviderAzure.js +60 -0
- package/dist/challengeProvider/challengeProviderCertificate.d.ts +15 -0
- package/dist/challengeProvider/challengeProviderCertificate.js +82 -0
- package/dist/challengeProvider/challengeProviderSevSnp.d.ts +10 -0
- package/dist/challengeProvider/challengeProviderSevSnp.js +55 -0
- package/dist/challengeProvider/challengeProviderTdx.d.ts +11 -0
- package/dist/challengeProvider/challengeProviderTdx.js +60 -0
- package/dist/challengeProvider/challengeProviderToken.d.ts +9 -0
- package/dist/challengeProvider/challengeProviderToken.js +22 -0
- package/dist/challengeProvider/challengeProviderUntrusted.d.ts +9 -0
- package/dist/challengeProvider/challengeProviderUntrusted.js +22 -0
- package/dist/challengeProvider/index.d.ts +8 -0
- package/dist/challengeProvider/index.js +25 -0
- package/dist/challengeValidator/challengeValidator.d.ts +18 -0
- package/dist/challengeValidator/challengeValidator.js +3 -0
- package/dist/challengeValidator/challengeValidatorAzure.d.ts +29 -0
- package/dist/challengeValidator/challengeValidatorAzure.js +67 -0
- package/dist/challengeValidator/challengeValidatorCertificate.d.ts +9 -0
- package/dist/challengeValidator/challengeValidatorCertificate.js +55 -0
- package/dist/challengeValidator/challengeValidatorNvidia.d.ts +10 -0
- package/dist/challengeValidator/challengeValidatorNvidia.js +246 -0
- package/dist/challengeValidator/challengeValidatorSevSnp.d.ts +13 -0
- package/dist/challengeValidator/challengeValidatorSevSnp.js +142 -0
- package/dist/challengeValidator/challengeValidatorTdx.d.ts +8 -0
- package/dist/challengeValidator/challengeValidatorTdx.js +58 -0
- package/dist/challengeValidator/challengeValidatorToken.d.ts +8 -0
- package/dist/challengeValidator/challengeValidatorToken.js +38 -0
- package/dist/challengeValidator/index.d.ts +9 -0
- package/dist/challengeValidator/index.js +37 -0
- package/dist/challengeValidator/schemas.d.ts +13 -0
- package/dist/challengeValidator/schemas.js +21 -0
- package/dist/challengeValidator/validatorConfig.d.ts +28 -0
- package/dist/challengeValidator/validatorConfig.js +3 -0
- package/dist/challengeValidator/wasmVerificationPolicy.d.ts +60 -0
- package/dist/challengeValidator/wasmVerificationPolicy.js +123 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +24 -0
- package/dist/mrEnclaveSignature.d.ts +9 -0
- package/dist/mrEnclaveSignature.js +32 -0
- package/dist/swarmCertValidation.d.ts +6 -0
- package/dist/swarmCertValidation.js +52 -0
- package/package.json +32 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ChallengeType } from '@super-protocol/pki-common';
|
|
2
|
+
export type DetectAttestationType = ChallengeType.TDX | ChallengeType.TDXGOOGLE | ChallengeType.TDXAZURE | ChallengeType.SEVSNP | ChallengeType.SEVSNPAZURE | ChallengeType.Untrusted;
|
|
3
|
+
export declare const isGoogleCloudVm: () => Promise<boolean>;
|
|
4
|
+
export declare const detectAttestationType: () => Promise<DetectAttestationType>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectAttestationType = exports.isGoogleCloudVm = void 0;
|
|
4
|
+
const pki_common_1 = require("@super-protocol/pki-common");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const azure_1 = require("./azure");
|
|
7
|
+
const GCP_METADATA_URL = 'http://169.254.169.254/computeMetadata/v1/instance/id';
|
|
8
|
+
const GCP_METADATA_TIMEOUT_MS = 500;
|
|
9
|
+
const isGoogleCloudVm = async () => {
|
|
10
|
+
try {
|
|
11
|
+
const response = await fetch(GCP_METADATA_URL, {
|
|
12
|
+
headers: {
|
|
13
|
+
'Metadata-Flavor': 'Google',
|
|
14
|
+
},
|
|
15
|
+
signal: AbortSignal.timeout(GCP_METADATA_TIMEOUT_MS),
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const instanceId = await response.text();
|
|
21
|
+
return instanceId.trim().length > 0;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.isGoogleCloudVm = isGoogleCloudVm;
|
|
28
|
+
const detectAttestationType = async () => {
|
|
29
|
+
try {
|
|
30
|
+
// Must run before the device checks: on Azure the Hyper-V
|
|
31
|
+
// isolation type takes precedence over /dev/sev-guest.
|
|
32
|
+
const azureType = (0, azure_1.detectAzureAttestationType)();
|
|
33
|
+
if (azureType) {
|
|
34
|
+
return azureType;
|
|
35
|
+
}
|
|
36
|
+
const tdxPath = '/dev/tdx_guest';
|
|
37
|
+
const sevPath = '/dev/sev-guest';
|
|
38
|
+
if ((0, fs_1.existsSync)(tdxPath)) {
|
|
39
|
+
const stats = (0, fs_1.statSync)(tdxPath);
|
|
40
|
+
if (stats.isCharacterDevice()) {
|
|
41
|
+
return (await (0, exports.isGoogleCloudVm)())
|
|
42
|
+
? pki_common_1.ChallengeType.TDXGOOGLE
|
|
43
|
+
: pki_common_1.ChallengeType.TDX;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if ((0, fs_1.existsSync)(sevPath)) {
|
|
47
|
+
const stats = (0, fs_1.statSync)(sevPath);
|
|
48
|
+
if (stats.isCharacterDevice()) {
|
|
49
|
+
return (await (0, exports.isGoogleCloudVm)())
|
|
50
|
+
? pki_common_1.ChallengeType.Untrusted
|
|
51
|
+
: pki_common_1.ChallengeType.SEVSNP;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// If we can't check devices, fall back to untrusted
|
|
57
|
+
}
|
|
58
|
+
return pki_common_1.ChallengeType.Untrusted;
|
|
59
|
+
};
|
|
60
|
+
exports.detectAttestationType = detectAttestationType;
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXR0ZXN0YXRpb25UeXBlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2F0dGVzdGF0aW9uVHlwZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSwyREFBMkQ7QUFDM0QsMkJBQTBDO0FBQzFDLG1DQUFxRDtBQVVyRCxNQUFNLGdCQUFnQixHQUNsQix1REFBdUQsQ0FBQztBQUM1RCxNQUFNLHVCQUF1QixHQUFHLEdBQUcsQ0FBQztBQUU3QixNQUFNLGVBQWUsR0FBRyxLQUFLLElBQXNCLEVBQUU7SUFDeEQsSUFBSSxDQUFDO1FBQ0QsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsZ0JBQWdCLEVBQUU7WUFDM0MsT0FBTyxFQUFFO2dCQUNMLGlCQUFpQixFQUFFLFFBQVE7YUFDOUI7WUFDRCxNQUFNLEVBQUUsV0FBVyxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQztTQUN2RCxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxDQUFDO1lBQ2YsT0FBTyxLQUFLLENBQUM7UUFDakIsQ0FBQztRQUVELE1BQU0sVUFBVSxHQUFHLE1BQU0sUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3pDLE9BQU8sVUFBVSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNMLE9BQU8sS0FBSyxDQUFDO0lBQ2pCLENBQUM7QUFDTCxDQUFDLENBQUM7QUFsQlcsUUFBQSxlQUFlLG1CQWtCMUI7QUFFSyxNQUFNLHFCQUFxQixHQUM5QixLQUFLLElBQW9DLEVBQUU7SUFDdkMsSUFBSSxDQUFDO1FBQ0QsMERBQTBEO1FBQzFELHVEQUF1RDtRQUN2RCxNQUFNLFNBQVMsR0FBRyxJQUFBLGtDQUEwQixHQUFFLENBQUM7UUFDL0MsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNaLE9BQU8sU0FBUyxDQUFDO1FBQ3JCLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQztRQUNqQyxNQUFNLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQztRQUVqQyxJQUFJLElBQUEsZUFBVSxFQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDdEIsTUFBTSxLQUFLLEdBQUcsSUFBQSxhQUFRLEVBQUMsT0FBTyxDQUFDLENBQUM7WUFDaEMsSUFBSSxLQUFLLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDO2dCQUM1QixPQUFPLENBQUMsTUFBTSxJQUFBLHVCQUFlLEdBQUUsQ0FBQztvQkFDNUIsQ0FBQyxDQUFDLDBCQUFhLENBQUMsU0FBUztvQkFDekIsQ0FBQyxDQUFDLDBCQUFhLENBQUMsR0FBRyxDQUFDO1lBQzVCLENBQUM7UUFDTCxDQUFDO1FBRUQsSUFBSSxJQUFBLGVBQVUsRUFBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sS0FBSyxHQUFHLElBQUEsYUFBUSxFQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2hDLElBQUksS0FBSyxDQUFDLGlCQUFpQixFQUFFLEVBQUUsQ0FBQztnQkFDNUIsT0FBTyxDQUFDLE1BQU0sSUFBQSx1QkFBZSxHQUFFLENBQUM7b0JBQzVCLENBQUMsQ0FBQywwQkFBYSxDQUFDLFNBQVM7b0JBQ3pCLENBQUMsQ0FBQywwQkFBYSxDQUFDLE1BQU0sQ0FBQztZQUMvQixDQUFDO1FBQ0wsQ0FBQztJQUNMLENBQUM7SUFBQyxNQUFNLENBQUM7UUFDTCxvREFBb0Q7SUFDeEQsQ0FBQztJQUVELE9BQU8sMEJBQWEsQ0FBQyxTQUFTLENBQUM7QUFDbkMsQ0FBQyxDQUFDO0FBbkNPLFFBQUEscUJBQXFCLHlCQW1DNUIifQ==
|
package/dist/azure.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ChallengeType } from '@super-protocol/pki-common';
|
|
2
|
+
export type AzureAttestationType = ChallengeType.TDXAZURE | ChallengeType.SEVSNPAZURE;
|
|
3
|
+
export declare enum HvIsolationType {
|
|
4
|
+
None = 0,
|
|
5
|
+
VBS = 1,
|
|
6
|
+
SNP = 2,
|
|
7
|
+
TDX = 3
|
|
8
|
+
}
|
|
9
|
+
export type CpuidRegisters = [
|
|
10
|
+
eax: number,
|
|
11
|
+
ebx: number,
|
|
12
|
+
ecx: number,
|
|
13
|
+
edx: number
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Returns the Hyper-V isolation type from the interface leaf (0x40000000)
|
|
17
|
+
* and the isolation config leaf (0x4000000C), or undefined when this is not
|
|
18
|
+
* a Hyper-V guest.
|
|
19
|
+
*/
|
|
20
|
+
export declare const parseHvIsolationType: (interfaceRegs: CpuidRegisters | undefined, isolationRegs: CpuidRegisters | undefined) => number | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Azure confidential VMs run behind a paravisor, so the guest is given none
|
|
23
|
+
* of the usual TEE interfaces: no /dev/tdx_guest, no TSM provider, no
|
|
24
|
+
* tdx_guest CPUID flag, no TDEL ACPI table, and CPUID leaf 0x21 reads back as
|
|
25
|
+
* zeroes. The instance metadata service does not name the TEE either. What
|
|
26
|
+
* the hypervisor does report is its isolation type, which is exactly what the
|
|
27
|
+
* kernel itself uses to decide it is running under TDX or SEV-SNP.
|
|
28
|
+
*/
|
|
29
|
+
export declare const getHvIsolationType: () => number | undefined;
|
|
30
|
+
export declare const parseIsAzureChassisAssetTag: (tag: string) => boolean;
|
|
31
|
+
export declare const isAzureVm: () => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Azure confidential VM and the TEE backing it, detected offline: no
|
|
34
|
+
* network, no timeouts.
|
|
35
|
+
*
|
|
36
|
+
* NOT verified on a live SEV-SNP guest: the isolation type is read exactly
|
|
37
|
+
* like the TDX one, but only the TDX value has been confirmed against real
|
|
38
|
+
* hardware so far.
|
|
39
|
+
*/
|
|
40
|
+
export declare const detectAzureAttestationType: () => AzureAttestationType | undefined;
|
package/dist/azure.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectAzureAttestationType = exports.isAzureVm = exports.parseIsAzureChassisAssetTag = exports.getHvIsolationType = exports.parseHvIsolationType = exports.HvIsolationType = void 0;
|
|
4
|
+
const pki_common_1 = require("@super-protocol/pki-common");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
// Azure stamps every VM with this chassis asset tag, so the platform can be
|
|
7
|
+
// identified offline, without the instance metadata service.
|
|
8
|
+
const DMI_CHASSIS_ASSET_TAG = '/sys/class/dmi/id/chassis_asset_tag';
|
|
9
|
+
const AZURE_CHASSIS_ASSET_TAG = '7783-7084-3265-9085-8269-3286-77';
|
|
10
|
+
// Hyper-V reports the TEE backing an isolated guest through its own CPUID
|
|
11
|
+
// leaves. This is what the kernel reads (hv_get_isolation_type()), and on
|
|
12
|
+
// Azure it is the only precise source: see getHvIsolationType() below.
|
|
13
|
+
const CPUID_DEVICE = '/dev/cpu/0/cpuid';
|
|
14
|
+
const HV_CPUID_INTERFACE = 0x40000000;
|
|
15
|
+
const HV_CPUID_ISOLATION_CONFIG = 0x4000000c;
|
|
16
|
+
const HV_SIGNATURE = 'Microsoft Hv';
|
|
17
|
+
const HV_ISOLATION_TYPE_MASK = 0xf;
|
|
18
|
+
var HvIsolationType;
|
|
19
|
+
(function (HvIsolationType) {
|
|
20
|
+
HvIsolationType[HvIsolationType["None"] = 0] = "None";
|
|
21
|
+
HvIsolationType[HvIsolationType["VBS"] = 1] = "VBS";
|
|
22
|
+
HvIsolationType[HvIsolationType["SNP"] = 2] = "SNP";
|
|
23
|
+
HvIsolationType[HvIsolationType["TDX"] = 3] = "TDX";
|
|
24
|
+
})(HvIsolationType || (exports.HvIsolationType = HvIsolationType = {}));
|
|
25
|
+
/** Read one CPUID leaf through /dev/cpu/0/cpuid (needs CONFIG_X86_CPUID). */
|
|
26
|
+
const readCpuid = (leaf, subleaf = 0) => {
|
|
27
|
+
let fd;
|
|
28
|
+
try {
|
|
29
|
+
fd = (0, fs_1.openSync)(CPUID_DEVICE, 'r');
|
|
30
|
+
const buffer = Buffer.alloc(16);
|
|
31
|
+
const position = (BigInt(subleaf) << 32n) | BigInt(leaf);
|
|
32
|
+
if ((0, fs_1.readSync)(fd, buffer, 0, 16, position) !== 16) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
buffer.readUInt32LE(0),
|
|
37
|
+
buffer.readUInt32LE(4),
|
|
38
|
+
buffer.readUInt32LE(8),
|
|
39
|
+
buffer.readUInt32LE(12),
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
if (fd !== undefined) {
|
|
47
|
+
try {
|
|
48
|
+
(0, fs_1.closeSync)(fd);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// ignore
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Returns the Hyper-V isolation type from the interface leaf (0x40000000)
|
|
58
|
+
* and the isolation config leaf (0x4000000C), or undefined when this is not
|
|
59
|
+
* a Hyper-V guest.
|
|
60
|
+
*/
|
|
61
|
+
const parseHvIsolationType = (interfaceRegs, isolationRegs) => {
|
|
62
|
+
if (!interfaceRegs) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const [maxLeaf, ebx, ecx, edx] = interfaceRegs;
|
|
66
|
+
const signature = Buffer.alloc(12);
|
|
67
|
+
signature.writeUInt32LE(ebx, 0);
|
|
68
|
+
signature.writeUInt32LE(ecx, 4);
|
|
69
|
+
signature.writeUInt32LE(edx, 8);
|
|
70
|
+
if (signature.toString('latin1') !== HV_SIGNATURE) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (maxLeaf < HV_CPUID_ISOLATION_CONFIG || !isolationRegs) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
return isolationRegs[1] & HV_ISOLATION_TYPE_MASK;
|
|
77
|
+
};
|
|
78
|
+
exports.parseHvIsolationType = parseHvIsolationType;
|
|
79
|
+
/**
|
|
80
|
+
* Azure confidential VMs run behind a paravisor, so the guest is given none
|
|
81
|
+
* of the usual TEE interfaces: no /dev/tdx_guest, no TSM provider, no
|
|
82
|
+
* tdx_guest CPUID flag, no TDEL ACPI table, and CPUID leaf 0x21 reads back as
|
|
83
|
+
* zeroes. The instance metadata service does not name the TEE either. What
|
|
84
|
+
* the hypervisor does report is its isolation type, which is exactly what the
|
|
85
|
+
* kernel itself uses to decide it is running under TDX or SEV-SNP.
|
|
86
|
+
*/
|
|
87
|
+
const getHvIsolationType = () => {
|
|
88
|
+
const interfaceRegs = readCpuid(HV_CPUID_INTERFACE);
|
|
89
|
+
if (!interfaceRegs) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
return (0, exports.parseHvIsolationType)(interfaceRegs, readCpuid(HV_CPUID_ISOLATION_CONFIG));
|
|
93
|
+
};
|
|
94
|
+
exports.getHvIsolationType = getHvIsolationType;
|
|
95
|
+
const parseIsAzureChassisAssetTag = (tag) => tag.replace(/\0/g, '').trim() === AZURE_CHASSIS_ASSET_TAG;
|
|
96
|
+
exports.parseIsAzureChassisAssetTag = parseIsAzureChassisAssetTag;
|
|
97
|
+
const isAzureVm = () => {
|
|
98
|
+
try {
|
|
99
|
+
return (0, exports.parseIsAzureChassisAssetTag)((0, fs_1.readFileSync)(DMI_CHASSIS_ASSET_TAG, 'latin1'));
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
exports.isAzureVm = isAzureVm;
|
|
106
|
+
/**
|
|
107
|
+
* Azure confidential VM and the TEE backing it, detected offline: no
|
|
108
|
+
* network, no timeouts.
|
|
109
|
+
*
|
|
110
|
+
* NOT verified on a live SEV-SNP guest: the isolation type is read exactly
|
|
111
|
+
* like the TDX one, but only the TDX value has been confirmed against real
|
|
112
|
+
* hardware so far.
|
|
113
|
+
*/
|
|
114
|
+
const detectAzureAttestationType = () => {
|
|
115
|
+
if (!(0, exports.isAzureVm)()) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
switch ((0, exports.getHvIsolationType)()) {
|
|
119
|
+
case HvIsolationType.TDX:
|
|
120
|
+
return pki_common_1.ChallengeType.TDXAZURE;
|
|
121
|
+
case HvIsolationType.SNP:
|
|
122
|
+
return pki_common_1.ChallengeType.SEVSNPAZURE;
|
|
123
|
+
default:
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
exports.detectAzureAttestationType = detectAzureAttestationType;
|
|
128
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp1cmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYXp1cmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsMkRBQTJEO0FBQzNELDJCQUFpRTtBQU1qRSw0RUFBNEU7QUFDNUUsNkRBQTZEO0FBQzdELE1BQU0scUJBQXFCLEdBQUcscUNBQXFDLENBQUM7QUFDcEUsTUFBTSx1QkFBdUIsR0FBRyxrQ0FBa0MsQ0FBQztBQUVuRSwwRUFBMEU7QUFDMUUsMEVBQTBFO0FBQzFFLHVFQUF1RTtBQUN2RSxNQUFNLFlBQVksR0FBRyxrQkFBa0IsQ0FBQztBQUN4QyxNQUFNLGtCQUFrQixHQUFHLFVBQVUsQ0FBQztBQUN0QyxNQUFNLHlCQUF5QixHQUFHLFVBQVUsQ0FBQztBQUM3QyxNQUFNLFlBQVksR0FBRyxjQUFjLENBQUM7QUFDcEMsTUFBTSxzQkFBc0IsR0FBRyxHQUFHLENBQUM7QUFFbkMsSUFBWSxlQUtYO0FBTEQsV0FBWSxlQUFlO0lBQ3ZCLHFEQUFRLENBQUE7SUFDUixtREFBTyxDQUFBO0lBQ1AsbURBQU8sQ0FBQTtJQUNQLG1EQUFPLENBQUE7QUFDWCxDQUFDLEVBTFcsZUFBZSwrQkFBZixlQUFlLFFBSzFCO0FBU0QsNkVBQTZFO0FBQzdFLE1BQU0sU0FBUyxHQUFHLENBQUMsSUFBWSxFQUFFLE9BQU8sR0FBRyxDQUFDLEVBQThCLEVBQUU7SUFDeEUsSUFBSSxFQUFzQixDQUFDO0lBQzNCLElBQUksQ0FBQztRQUNELEVBQUUsR0FBRyxJQUFBLGFBQVEsRUFBQyxZQUFZLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDakMsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNoQyxNQUFNLFFBQVEsR0FBRyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekQsSUFBSSxJQUFBLGFBQVEsRUFBQyxF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
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CustomExtension } from '@super-protocol/sdk-js';
|
|
2
|
+
import { PkiCertParams, CertificateType, CertificateChallengeInfo, Challenge, NetworkType, PKISslCertificate } from '@super-protocol/pki-common';
|
|
3
|
+
export declare const generateRootCertificate: (params: {
|
|
4
|
+
domains: string[];
|
|
5
|
+
privateKeyPem: string;
|
|
6
|
+
publicKeyPem: string;
|
|
7
|
+
challenge: Challenge;
|
|
8
|
+
certParams: PkiCertParams;
|
|
9
|
+
networkType?: NetworkType;
|
|
10
|
+
}) => Promise<string>;
|
|
11
|
+
type GenerateCertificateParams = {
|
|
12
|
+
certType: CertificateType;
|
|
13
|
+
domains: string[];
|
|
14
|
+
caBundle: string;
|
|
15
|
+
signingCertPem: string;
|
|
16
|
+
signingPrivateKeyPem: string;
|
|
17
|
+
certParams: PkiCertParams;
|
|
18
|
+
publicKeyPem: string;
|
|
19
|
+
ocspSigning: boolean;
|
|
20
|
+
validated?: boolean;
|
|
21
|
+
commonName?: string;
|
|
22
|
+
challengeInfo?: CertificateChallengeInfo;
|
|
23
|
+
clientExtensionAttrs?: CustomExtension[];
|
|
24
|
+
vmCertificatePem?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare const generateSubrootCertificate: (params: GenerateCertificateParams) => Promise<PKISslCertificate>;
|
|
27
|
+
export declare const generateEndEntityCertificate: (params: GenerateCertificateParams) => Promise<PKISslCertificate>;
|
|
28
|
+
export declare const getParamsFromCSR: (csrPem: string) => Promise<{
|
|
29
|
+
commonName: string | undefined;
|
|
30
|
+
publicKeyPem: string;
|
|
31
|
+
clientDomains: string[];
|
|
32
|
+
clientExtensionAttrs: CustomExtension[];
|
|
33
|
+
}>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getParamsFromCSR = exports.generateEndEntityCertificate = exports.generateSubrootCertificate = exports.generateRootCertificate = void 0;
|
|
4
|
+
const sdk_js_1 = require("@super-protocol/sdk-js");
|
|
5
|
+
const sp_nvtrust_wrapper_1 = require("@super-protocol/sp-nvtrust-wrapper");
|
|
6
|
+
const net_1 = require("net");
|
|
7
|
+
const pki_common_1 = require("@super-protocol/pki-common");
|
|
8
|
+
const MIN_CERT_LIFETIME_DAYS = 1;
|
|
9
|
+
const MAX_CERT_LIFETIME_DAYS = 18250;
|
|
10
|
+
const DNS_LABEL_REGEX = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
|
|
11
|
+
const isValidDomainName = (value) => {
|
|
12
|
+
const trimmedValue = value.trim();
|
|
13
|
+
if (!trimmedValue || trimmedValue.length > 253) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const isWildcard = trimmedValue.startsWith('*.');
|
|
17
|
+
const hostname = isWildcard ? trimmedValue.slice(2) : trimmedValue;
|
|
18
|
+
if (!isWildcard && hostname.toLowerCase() === 'localhost') {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (!hostname || hostname.endsWith('.') || !hostname.includes('.')) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const labels = hostname.split('.');
|
|
25
|
+
if (labels.some((label) => !DNS_LABEL_REGEX.test(label))) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return !isWildcard || labels.length > 1;
|
|
29
|
+
};
|
|
30
|
+
const isValidDomainOrIp = (value) => {
|
|
31
|
+
const trimmedValue = value.trim();
|
|
32
|
+
return (0, net_1.isIP)(trimmedValue) !== 0 || isValidDomainName(trimmedValue);
|
|
33
|
+
};
|
|
34
|
+
const filterValidDomainOrIpList = (values) => values
|
|
35
|
+
.map((value) => value.trim())
|
|
36
|
+
.filter((value) => value && isValidDomainOrIp(value));
|
|
37
|
+
const addDaysUtc = (base, days) => {
|
|
38
|
+
const next = new Date(base);
|
|
39
|
+
next.setUTCDate(next.getUTCDate() + days);
|
|
40
|
+
return next;
|
|
41
|
+
};
|
|
42
|
+
const resolveNotAfter = (certParams) => {
|
|
43
|
+
const { lifetimeDays } = certParams;
|
|
44
|
+
if (!Number.isInteger(lifetimeDays) ||
|
|
45
|
+
lifetimeDays < MIN_CERT_LIFETIME_DAYS ||
|
|
46
|
+
lifetimeDays > MAX_CERT_LIFETIME_DAYS) {
|
|
47
|
+
throw new Error(`certParams.lifetimeDays must be an integer between ${MIN_CERT_LIFETIME_DAYS} and ${MAX_CERT_LIFETIME_DAYS}`);
|
|
48
|
+
}
|
|
49
|
+
return addDaysUtc(new Date(), lifetimeDays);
|
|
50
|
+
};
|
|
51
|
+
const buildPrincipal = (certParams) => ({
|
|
52
|
+
commonName: certParams.commonName,
|
|
53
|
+
country: certParams.countryName,
|
|
54
|
+
stateName: certParams.stateName,
|
|
55
|
+
localityName: certParams.localityName,
|
|
56
|
+
organization: certParams.organizationName,
|
|
57
|
+
organizationalUnit: certParams.organizationUnitName,
|
|
58
|
+
});
|
|
59
|
+
const getAttestationPayloadBase64FromChallenge = (challenge) => {
|
|
60
|
+
switch (challenge.type) {
|
|
61
|
+
case pki_common_1.ChallengeType.TDX:
|
|
62
|
+
case pki_common_1.ChallengeType.TDXGOOGLE:
|
|
63
|
+
case pki_common_1.ChallengeType.TDXAZURE:
|
|
64
|
+
case pki_common_1.ChallengeType.SEVSNP:
|
|
65
|
+
case pki_common_1.ChallengeType.SEVSNPAZURE:
|
|
66
|
+
return challenge.evidenceBase64;
|
|
67
|
+
default:
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const generateRootCertificate = async (params) => {
|
|
72
|
+
const principal = buildPrincipal(params.certParams);
|
|
73
|
+
const notAfter = resolveNotAfter(params.certParams);
|
|
74
|
+
const customExtensions = [
|
|
75
|
+
{
|
|
76
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_TYPE,
|
|
77
|
+
value: Buffer.from(params.challenge.type, 'ascii'),
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
if (params.networkType) {
|
|
81
|
+
customExtensions.push({
|
|
82
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_NETWORK_TYPE,
|
|
83
|
+
value: Buffer.from(params.networkType, 'ascii'),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const attestationPayloadBase64 = getAttestationPayloadBase64FromChallenge(params.challenge);
|
|
87
|
+
if (attestationPayloadBase64) {
|
|
88
|
+
customExtensions.push({
|
|
89
|
+
oid: pki_common_1.OID_TEE_EVIDENCE,
|
|
90
|
+
value: Buffer.from(attestationPayloadBase64, 'base64'),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const generateCertParams = {
|
|
94
|
+
privateKey: params.privateKeyPem,
|
|
95
|
+
publicKey: params.publicKeyPem,
|
|
96
|
+
subject: principal,
|
|
97
|
+
issuer: principal,
|
|
98
|
+
notAfter,
|
|
99
|
+
dnsNames: params.domains,
|
|
100
|
+
ca: true,
|
|
101
|
+
customExtensions,
|
|
102
|
+
};
|
|
103
|
+
return sdk_js_1.CertificateGenerator.generateCert(generateCertParams);
|
|
104
|
+
};
|
|
105
|
+
exports.generateRootCertificate = generateRootCertificate;
|
|
106
|
+
const SYSTEM_MANAGED_EXTENSION_OIDS = [
|
|
107
|
+
pki_common_1.OID_TEE_EVIDENCE,
|
|
108
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_TYPE,
|
|
109
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_ID,
|
|
110
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_COMMON_ID,
|
|
111
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_VALIDATED,
|
|
112
|
+
pki_common_1.OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU,
|
|
113
|
+
pki_common_1.OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU_INTERCONNECT,
|
|
114
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_SUBTYPE,
|
|
115
|
+
pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_CERTIFICATE_ID,
|
|
116
|
+
];
|
|
117
|
+
const filterClientExtensions = (extensions = []) => extensions.filter((ext) => !SYSTEM_MANAGED_EXTENSION_OIDS.some((oid) => ext.oid.startsWith(oid)));
|
|
118
|
+
const buildNvidiaExtensions = (challengeInfo) => {
|
|
119
|
+
const { challenge, nvidiaVerification } = challengeInfo;
|
|
120
|
+
const hasNvidia = (0, pki_common_1.hasChallengeNvidiaToken)(challenge) ||
|
|
121
|
+
('nvidiaEvidence' in challenge &&
|
|
122
|
+
Boolean(challenge.nvidiaEvidence));
|
|
123
|
+
if (!hasNvidia) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
if (!nvidiaVerification) {
|
|
127
|
+
throw new Error('NVIDIA certificate extension requires the authority verifier result');
|
|
128
|
+
}
|
|
129
|
+
const extracted = sp_nvtrust_wrapper_1.NvtrustWrapper.extractGPUInfoFromToken({
|
|
130
|
+
jwt: nvidiaVerification.gpuToken,
|
|
131
|
+
});
|
|
132
|
+
const gpusInfo = {
|
|
133
|
+
...extracted,
|
|
134
|
+
attestationMode: nvidiaVerification.attestationMode,
|
|
135
|
+
};
|
|
136
|
+
const extensions = [
|
|
137
|
+
{
|
|
138
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU,
|
|
139
|
+
value: Buffer.from(sp_nvtrust_wrapper_1.NvtrustGPUList.encode(gpusInfo).finish()),
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
const { gpuInterconnectStatus, nvswitchToken } = nvidiaVerification;
|
|
143
|
+
if (gpusInfo.gpus.length === 1) {
|
|
144
|
+
if (gpuInterconnectStatus !== undefined ||
|
|
145
|
+
nvswitchToken !== undefined) {
|
|
146
|
+
throw new Error('NVIDIA GPU interconnect data must be omitted for a single GPU');
|
|
147
|
+
}
|
|
148
|
+
return extensions;
|
|
149
|
+
}
|
|
150
|
+
if (gpusInfo.gpus.length === 0) {
|
|
151
|
+
throw new Error('NVIDIA GPU token does not contain any GPU claims');
|
|
152
|
+
}
|
|
153
|
+
if (gpuInterconnectStatus === undefined) {
|
|
154
|
+
throw new Error('NVIDIA GPU interconnect status is required for multiple GPUs');
|
|
155
|
+
}
|
|
156
|
+
const isVerifiedNVSwitchInterconnect = gpuInterconnectStatus ===
|
|
157
|
+
sp_nvtrust_wrapper_1.NvidiaGpuInterconnectStatus.NVIDIA_GPU_INTERCONNECT_NVLINK_SWITCH_ATTESTATION_VERIFIED;
|
|
158
|
+
if (!isVerifiedNVSwitchInterconnect && nvswitchToken !== undefined) {
|
|
159
|
+
throw new Error('NVIDIA NVSwitch token is only allowed for a verified NVLink switch interconnect');
|
|
160
|
+
}
|
|
161
|
+
if (isVerifiedNVSwitchInterconnect && !nvswitchToken) {
|
|
162
|
+
throw new Error('Verified NVIDIA NVLink switch interconnect requires a non-empty NVSwitch token');
|
|
163
|
+
}
|
|
164
|
+
// ChallengeValidatorNvidia verifies this token before certificate issuance.
|
|
165
|
+
const switches = nvswitchToken
|
|
166
|
+
? sp_nvtrust_wrapper_1.NvtrustWrapper.extractNVSwitchInfoFromToken({ jwt: nvswitchToken })
|
|
167
|
+
: [];
|
|
168
|
+
const interconnectInfo = sp_nvtrust_wrapper_1.NvtrustWrapper.createNvidiaGpuInterconnectInfo(gpuInterconnectStatus, switches);
|
|
169
|
+
extensions.push({
|
|
170
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU_INTERCONNECT,
|
|
171
|
+
value: Buffer.from(sp_nvtrust_wrapper_1.NvidiaGpuInterconnectInfo.encode(interconnectInfo).finish()),
|
|
172
|
+
});
|
|
173
|
+
return extensions;
|
|
174
|
+
};
|
|
175
|
+
const buildCertificateChallengeExtensions = (challenge) => {
|
|
176
|
+
if (challenge.type !== pki_common_1.ChallengeType.Certificate) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
const challengeSubtype = sdk_js_1.CertificatesHelper.getExtensionValue(challenge.orderCertificate, pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_TYPE);
|
|
180
|
+
const certificateId = sdk_js_1.CertificatesHelper.getExtensionValue(challenge.orderCertificate, sdk_js_1.constants.OID_CUSTOM_EXTENSION_USER_DATA);
|
|
181
|
+
if (!challengeSubtype || !certificateId) {
|
|
182
|
+
throw new Error(`Challenge certificate is missing required extensions: ${pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_TYPE} or ${sdk_js_1.constants.OID_CUSTOM_EXTENSION_USER_DATA}`);
|
|
183
|
+
}
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_SUBTYPE,
|
|
187
|
+
value: challengeSubtype,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_CERTIFICATE_ID,
|
|
191
|
+
value: certificateId,
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
};
|
|
195
|
+
const buildValidatedExtension = (validated) => {
|
|
196
|
+
if (!validated) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
return [
|
|
200
|
+
{
|
|
201
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_VALIDATED,
|
|
202
|
+
value: Buffer.alloc(0),
|
|
203
|
+
},
|
|
204
|
+
];
|
|
205
|
+
};
|
|
206
|
+
// TODO: Investigate behavior
|
|
207
|
+
// const buildVmGpuExtension = (
|
|
208
|
+
// vmCertificatePem: string | undefined,
|
|
209
|
+
// existingExtensions: CustomExtension[],
|
|
210
|
+
// ): CustomExtension[] => {
|
|
211
|
+
// if (!vmCertificatePem) {
|
|
212
|
+
// return [];
|
|
213
|
+
// }
|
|
214
|
+
//
|
|
215
|
+
// const gpuInfo = CertificatesHelper.getExtensionValue(
|
|
216
|
+
// vmCertificatePem,
|
|
217
|
+
// OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU,
|
|
218
|
+
// );
|
|
219
|
+
//
|
|
220
|
+
// if (
|
|
221
|
+
// gpuInfo &&
|
|
222
|
+
// !existingExtensions.some(
|
|
223
|
+
// (ext) => ext.oid === OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU,
|
|
224
|
+
// )
|
|
225
|
+
// ) {
|
|
226
|
+
// return [
|
|
227
|
+
// {
|
|
228
|
+
// oid: OID_CUSTOM_EXTENSION_NVIDIA_INFO_GPU,
|
|
229
|
+
// value: gpuInfo,
|
|
230
|
+
// },
|
|
231
|
+
// ];
|
|
232
|
+
// }
|
|
233
|
+
//
|
|
234
|
+
// return [];
|
|
235
|
+
// };
|
|
236
|
+
const buildCommonExtensions = (params) => {
|
|
237
|
+
const extensions = [];
|
|
238
|
+
if (params.challengeInfo) {
|
|
239
|
+
const { challenge, idHex, commonIdHex } = params.challengeInfo;
|
|
240
|
+
extensions.push({
|
|
241
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_TYPE,
|
|
242
|
+
value: Buffer.from(challenge.type, 'ascii'),
|
|
243
|
+
});
|
|
244
|
+
if (challenge.type !== pki_common_1.ChallengeType.Token) {
|
|
245
|
+
extensions.push({
|
|
246
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_ID,
|
|
247
|
+
value: Buffer.from(idHex, 'hex'),
|
|
248
|
+
});
|
|
249
|
+
if (commonIdHex) {
|
|
250
|
+
extensions.push({
|
|
251
|
+
oid: pki_common_1.OID_CUSTOM_EXTENSION_CHALLENGE_COMMON_ID,
|
|
252
|
+
value: Buffer.from(commonIdHex, 'hex'),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (params.certType === pki_common_1.CertificateType.Basic) {
|
|
256
|
+
const attestationPayloadBase64 = getAttestationPayloadBase64FromChallenge(challenge);
|
|
257
|
+
if (attestationPayloadBase64) {
|
|
258
|
+
extensions.push({
|
|
259
|
+
oid: pki_common_1.OID_TEE_EVIDENCE,
|
|
260
|
+
value: Buffer.from(attestationPayloadBase64, 'base64'),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
extensions.push(...buildNvidiaExtensions(params.challengeInfo));
|
|
265
|
+
extensions.push(...buildCertificateChallengeExtensions(challenge));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const filteredClientExtensions = filterClientExtensions(params.clientExtensionAttrs);
|
|
269
|
+
return [...filteredClientExtensions, ...extensions];
|
|
270
|
+
};
|
|
271
|
+
const generateCertificate = async (params) => {
|
|
272
|
+
const signingCertPems = (0, pki_common_1.sortCertsFromLeafToRootWrapper)(params.signingCertPem, (pem) => sdk_js_1.CertificatesHelper.sortCertsFromLeafToRoot(pem).map((c) => sdk_js_1.CertificatesHelper.derToPem(c.cert.toSchema().toBER(false))));
|
|
273
|
+
if (!signingCertPems.length) {
|
|
274
|
+
throw new Error('No valid signing certificates found in the provided signingCertPem');
|
|
275
|
+
}
|
|
276
|
+
const signingLeafPem = signingCertPems[0];
|
|
277
|
+
const signingCert = await sdk_js_1.CertificateGenerator.checkAndParseCert(signingLeafPem);
|
|
278
|
+
const customExtensions = buildCommonExtensions({
|
|
279
|
+
certType: params.certType,
|
|
280
|
+
challengeInfo: params.challengeInfo,
|
|
281
|
+
clientExtensionAttrs: params.clientExtensionAttrs,
|
|
282
|
+
});
|
|
283
|
+
customExtensions.push(...buildValidatedExtension(params.validated));
|
|
284
|
+
// TODO: Investigate behavior
|
|
285
|
+
// customExtensions.push(
|
|
286
|
+
// ...buildVmGpuExtension(params.vmCertificatePem, customExtensions),
|
|
287
|
+
// );
|
|
288
|
+
const domains = params.certType === pki_common_1.CertificateType.Basic
|
|
289
|
+
? filterValidDomainOrIpList(params.domains)
|
|
290
|
+
: [];
|
|
291
|
+
const subject = {
|
|
292
|
+
commonName: params.commonName || params.domains[0] || '',
|
|
293
|
+
country: params.certParams.countryName,
|
|
294
|
+
stateName: params.certParams.stateName,
|
|
295
|
+
localityName: params.certParams.localityName,
|
|
296
|
+
organization: params.certParams.organizationName,
|
|
297
|
+
organizationalUnit: params.certParams.organizationUnitName,
|
|
298
|
+
};
|
|
299
|
+
const notAfter = resolveNotAfter(params.certParams);
|
|
300
|
+
const generateCertParams = {
|
|
301
|
+
privateKey: params.signingPrivateKeyPem,
|
|
302
|
+
publicKey: params.publicKeyPem,
|
|
303
|
+
subject,
|
|
304
|
+
dnsNames: domains,
|
|
305
|
+
ca: params.ca,
|
|
306
|
+
ocspSigning: params.ocspSigning,
|
|
307
|
+
issuer: signingCert.subject,
|
|
308
|
+
notAfter,
|
|
309
|
+
customExtensions,
|
|
310
|
+
};
|
|
311
|
+
if (params.certParams.ocspUrl) {
|
|
312
|
+
generateCertParams.ocspExtension = {
|
|
313
|
+
ocspUrl: params.certParams.ocspUrl,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const certPem = await sdk_js_1.CertificateGenerator.generateCert(generateCertParams);
|
|
317
|
+
const rootCertsPem = params.caBundle === params.signingCertPem
|
|
318
|
+
? params.signingCertPem
|
|
319
|
+
: params.signingCertPem + '\n' + params.caBundle;
|
|
320
|
+
return {
|
|
321
|
+
certPem,
|
|
322
|
+
domains,
|
|
323
|
+
rootCertsPem,
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
const generateSubrootCertificate = async (params) => generateCertificate({
|
|
327
|
+
...params,
|
|
328
|
+
ca: true,
|
|
329
|
+
});
|
|
330
|
+
exports.generateSubrootCertificate = generateSubrootCertificate;
|
|
331
|
+
const generateEndEntityCertificate = async (params) => generateCertificate({
|
|
332
|
+
...params,
|
|
333
|
+
ca: false,
|
|
334
|
+
});
|
|
335
|
+
exports.generateEndEntityCertificate = generateEndEntityCertificate;
|
|
336
|
+
const getParamsFromCSR = async (csrPem) => {
|
|
337
|
+
const parsedCsr = await sdk_js_1.CertificateGenerator.checkAndParseCsr(csrPem);
|
|
338
|
+
const commonName = parsedCsr.subject.match(/CN=([^,]+)/)?.[1]?.trim();
|
|
339
|
+
return {
|
|
340
|
+
commonName,
|
|
341
|
+
publicKeyPem: await sdk_js_1.CryptoKeysTransformer.cryptoKeyToSpkiPem(parsedCsr.publicKey),
|
|
342
|
+
clientDomains: parsedCsr.dnsNames || [],
|
|
343
|
+
clientExtensionAttrs: parsedCsr.extensions || [],
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
exports.getParamsFromCSR = getParamsFromCSR;
|
|
347
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGVHZW5lcmF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2VydGlmaWNhdGVHZW5lcmF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsbURBUWdDO0FBQ2hDLDJFQUs0QztBQUM1Qyw2QkFBMkI7QUFDM0IsMkRBb0JvQztBQUVwQyxNQUFNLHNCQUFzQixHQUFHLENBQUMsQ0FBQztBQUNqQyxNQUFNLHNCQUFzQixHQUFHLEtBQUssQ0FBQztBQUNyQyxNQUFNLGVBQWUsR0FBRyx5Q0FBeUMsQ0FBQztBQUVsRSxNQUFNLGlCQUFpQixHQUFHLENBQUMsS0FBYSxFQUFXLEVBQUU7SUFDakQsTUFBTSxZQUFZLEdBQUcsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ2xDLElBQUksQ0FBQyxZQUFZLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUM3QyxPQUFPLEtBQUssQ0FBQztJQUNqQixDQUFDO0lBRUQsTUFBTSxVQUFVLEdBQUcsWUFBWSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNqRCxNQUFNLFFBQVEsR0FBRyxVQUFVLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQztJQUVuRSxJQUFJLENBQUMsVUFBVSxJQUFJLFFBQVEsQ0FBQyxXQUFXLEVBQUUsS0FBSyxXQUFXLEVBQUUsQ0FBQztRQUN4RCxPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQsSUFBSSxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ2pFLE9BQU8sS0FBSyxDQUFDO0lBQ2pCLENBQUM7SUFFRCxNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FB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
|