@eqtylab/explorer 0.0.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/.env.api +6 -0
- package/.env.iframe +2 -0
- package/.env.manifest +2 -0
- package/.envrc +1 -0
- package/.eslintrc.cjs +17 -0
- package/.github/workflows/ci.yml +46 -0
- package/.github/workflows/publish.yml +44 -0
- package/.husky/pre-push +2 -0
- package/.prettierignore +2 -0
- package/.prettierrc.json +13 -0
- package/LICENSE +201 -0
- package/README.md +84 -0
- package/flake.lock +61 -0
- package/flake.nix +23 -0
- package/index.html +13 -0
- package/package.json +93 -0
- package/postcss.config.js +5 -0
- package/public/logos/eci.svg +23 -0
- package/public/logos/eqty.svg +3 -0
- package/public/manifests/B-200-merged.json +773 -0
- package/public/manifests/amd-sev.json +426 -0
- package/public/manifests/anchors.json +609 -0
- package/public/manifests/association-no-metadata.json +1393 -0
- package/public/manifests/association.json +1402 -0
- package/public/manifests/eqty-docker.json +562 -0
- package/public/manifests/governance.json +1324 -0
- package/public/manifests/intel-tdx-with-nvidia.json +441 -0
- package/public/manifests/intel-tdx.json +404 -0
- package/public/manifests/iroh-collections.json +1285 -0
- package/public/manifests/model-signing.json +1536 -0
- package/public/manifests/multiple-registrations.json +129 -0
- package/public/manifests/no-vcs.json +114 -0
- package/public/manifests/storage.json +294 -0
- package/public/manifests/sub-graphs.json +1 -0
- package/src/animations/variants.ts +52 -0
- package/src/app/apiClient/integrityService/fetchAttributes.ts +29 -0
- package/src/app/apiClient/integrityService/fetchManifest.ts +28 -0
- package/src/app/apiClient/integrityService/types.ts +12 -0
- package/src/app/components/integrityService/IntegrityService.tsx +105 -0
- package/src/app/components/integrityService/components/AuthButton.tsx +26 -0
- package/src/app/components/integrityService/components/FiltersModal.tsx +214 -0
- package/src/app/components/integrityService/components/FiltersToggle.tsx +28 -0
- package/src/app/components/manifestViewer/ManifestViewer.tsx +90 -0
- package/src/app/components/manifestViewer/components/ManifestButtons.tsx +215 -0
- package/src/app/config.json +23 -0
- package/src/app/config.ts +28 -0
- package/src/app/pages/index.tsx +34 -0
- package/src/app/stores/manifestStore.ts +19 -0
- package/src/app/utils/appMode.ts +37 -0
- package/src/assets/fonts/TWKLausanne-300.woff +0 -0
- package/src/assets/fonts/TWKLausanne-300.woff2 +0 -0
- package/src/assets/fonts/TWKLausanne-400.woff +0 -0
- package/src/assets/fonts/TWKLausanne-400.woff2 +0 -0
- package/src/assets/fonts/TWKLausanne-500.woff +0 -0
- package/src/assets/fonts/TWKLausanne-500.woff2 +0 -0
- package/src/assets/fonts/TWKLausanne-600.woff +0 -0
- package/src/assets/fonts/TWKLausanne-600.woff2 +0 -0
- package/src/assets/vectors/eqtylab.svg +6 -0
- package/src/components/app-header.tsx +61 -0
- package/src/components/dark-mode-switch.tsx +16 -0
- package/src/components/ui/accordion.tsx +47 -0
- package/src/components/ui/certificate-chain.tsx +60 -0
- package/src/components/ui/certificate-code.tsx +39 -0
- package/src/components/ui/details.tsx +20 -0
- package/src/components/ui/display-code.tsx +47 -0
- package/src/components/ui/panel-label.tsx +12 -0
- package/src/components/ui/verify-state-badge.tsx +32 -0
- package/src/explorer-component/LineageExplorer.tsx +89 -0
- package/src/explorer-component/components/CustomEdge.tsx +28 -0
- package/src/explorer-component/components/LineageFlow.tsx +220 -0
- package/src/explorer-component/components/NodeIconLoader.tsx +26 -0
- package/src/explorer-component/components/nodes/ComputationNode.tsx +56 -0
- package/src/explorer-component/components/nodes/CustomNode.tsx +112 -0
- package/src/explorer-component/components/nodes/index.ts +4 -0
- package/src/explorer-component/components/nodes/nodes.tsx +144 -0
- package/src/explorer-component/components/nodes/types.ts +167 -0
- package/src/explorer-component/components/sidebar/SectionList.tsx +54 -0
- package/src/explorer-component/components/sidebar/SectionTitle.tsx +10 -0
- package/src/explorer-component/components/sidebar/Sidebar.tsx +70 -0
- package/src/explorer-component/components/sidebar/SidebarWrapper.tsx +76 -0
- package/src/explorer-component/components/sidebar/sections/CustomSection.tsx +238 -0
- package/src/explorer-component/components/sidebar/sections/FileViewer.tsx +229 -0
- package/src/explorer-component/components/sidebar/sections/GovernanceSection.tsx +52 -0
- package/src/explorer-component/components/sidebar/sections/RegistrationSection.tsx +254 -0
- package/src/explorer-component/components/sidebar/sections/SigstoreSection.tsx +37 -0
- package/src/explorer-component/components/sidebar/sections/StorageSection.tsx +73 -0
- package/src/explorer-component/components/sidebar/sections/custom/IrohCollectionsView.tsx +139 -0
- package/src/explorer-component/components/sidebar/sections/registration/VerificationIcon.tsx +108 -0
- package/src/explorer-component/components/sidebar/sections/registration/actor/ActorBadge.tsx +62 -0
- package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AMDLogo.tsx +40 -0
- package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AzureLogo.tsx +65 -0
- package/src/explorer-component/components/sidebar/sections/registration/actor/svg/IntelLogo.tsx +24 -0
- package/src/explorer-component/components/sidebar/sections/registration/actor/svg/NvidiaLogo.tsx +27 -0
- package/src/explorer-component/components/sidebar/sections/registration/verifierClient.ts +13 -0
- package/src/explorer-component/icons/AttributionIcon.svg +6 -0
- package/src/explorer-component/icons/BenchmarkIcon.svg +6 -0
- package/src/explorer-component/icons/BenchmarkResultIcon.svg +6 -0
- package/src/explorer-component/icons/CertificateIcon.svg +6 -0
- package/src/explorer-component/icons/CodeIcon.svg +6 -0
- package/src/explorer-component/icons/ComputationIcon.svg +6 -0
- package/src/explorer-component/icons/DataIcon.svg +6 -0
- package/src/explorer-component/icons/DatabaseIcon.svg +6 -0
- package/src/explorer-component/icons/DatasetIcon.svg +6 -0
- package/src/explorer-component/icons/DeltaLakeIcon.svg +7 -0
- package/src/explorer-component/icons/DocumentIcon.svg +6 -0
- package/src/explorer-component/icons/MediaIcon.svg +6 -0
- package/src/explorer-component/icons/ModelIcon.svg +6 -0
- package/src/explorer-component/icons/TeamIcon.svg +3 -0
- package/src/explorer-component/icons/TokenIcon.svg +6 -0
- package/src/explorer-component/icons/UnknownIcon.svg +6 -0
- package/src/explorer-component/icons/index.ts +35 -0
- package/src/explorer-component/index.ts +5 -0
- package/src/explorer-component/integrityTypes/AnchorTypes.ts +31 -0
- package/src/explorer-component/integrityTypes/StatementTypes.ts +333 -0
- package/src/explorer-component/integrityTypes/VCTypes.ts +85 -0
- package/src/explorer-component/integrityTypes/types.ts +3 -0
- package/src/explorer-component/utils/config.ts +163 -0
- package/src/explorer-component/utils/formatTimestamp.ts +5 -0
- package/src/explorer-component/utils/generateGraph.ts +271 -0
- package/src/explorer-component/utils/layoutGraph.ts +148 -0
- package/src/explorer-component/utils/resolveIrohBlobs.ts +117 -0
- package/src/explorer-component/utils/statementHelpers.ts +252 -0
- package/src/explorer-component/utils/stripPrefix.ts +4 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/index.ts +3 -0
- package/src/lib/resource-types.ts +31 -0
- package/src/lib/use-escape-key.tsx +16 -0
- package/src/lib/utils.ts +19 -0
- package/src/main.css +82 -0
- package/src/main.tsx +39 -0
- package/src/v-comp-viewer/components/container-did-details.tsx +53 -0
- package/src/v-comp-viewer/components/system-did-details.tsx +356 -0
- package/src/v-comp-viewer/useVComp.ts +196 -0
- package/src/v-comp-viewer/utils.tsx +824 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +33 -0
- package/tsconfig.node.json +10 -0
- package/vercel.json +13 -0
- package/vite.app.config.ts +48 -0
- package/vite.lib.config.ts +61 -0
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
// @ts-expect-error - No type declarations available for this module
|
|
2
|
+
import vc_parse from "@eqtylab/vcomp-verify-js/common/vc_parse.js";
|
|
3
|
+
// @ts-expect-error - No type declarations available for this module
|
|
4
|
+
import TEEAttestation from "@eqtylab/vcomp-verify-js/common/TEEAttestation.js";
|
|
5
|
+
// @ts-expect-error - No type declarations available for this module
|
|
6
|
+
import { checkChain, parseChain, getPubKeyRaw, getCertificateInfo } from "@eqtylab/vcomp-verify-js/x509/certificate.js";
|
|
7
|
+
// @ts-expect-error - No type declarations available for this module
|
|
8
|
+
import intel_tdx from "@eqtylab/vcomp-verify-js/intel/intel_tdx.js";
|
|
9
|
+
// @ts-expect-error - No type declarations available for this module
|
|
10
|
+
import nvidia from "@eqtylab/vcomp-verify-js/nvidia/nvidia.js";
|
|
11
|
+
// @ts-expect-error - No type declarations available for this module
|
|
12
|
+
import amd_sev_snp from "@eqtylab/vcomp-verify-js/amd/amd-sev-snp.js";
|
|
13
|
+
// @ts-expect-error - No type declarations available for this module
|
|
14
|
+
import azure from "@eqtylab/vcomp-verify-js/azure/azure.js";
|
|
15
|
+
// @ts-expect-error - No type declarations available for this module
|
|
16
|
+
import TPM from "@eqtylab/vcomp-verify-js/common/TPM.js";
|
|
17
|
+
// @ts-expect-error - No type declarations available for this module
|
|
18
|
+
import { cidGenerator } from "@eqtylab/vcomp-verify-js/common/cidGenerator.js";
|
|
19
|
+
// @ts-expect-error - No type declarations available for this module
|
|
20
|
+
import UKI from "@eqtylab/vcomp-verify-js/common/UKI.js";
|
|
21
|
+
|
|
22
|
+
export type Validated = "valid" | "partial" | "invalid" | "";
|
|
23
|
+
|
|
24
|
+
// Upload handling types
|
|
25
|
+
export type UploadKey = "manifest" | "compose" | "cmdline" | "ovmf" | "initrd" | "kernel";
|
|
26
|
+
|
|
27
|
+
export type UploadValue =
|
|
28
|
+
| { kind: "manifest"; value: unknown }
|
|
29
|
+
| { kind: Exclude<UploadKey, "manifest">; value: Uint8Array };
|
|
30
|
+
|
|
31
|
+
export type UploadsMap = Partial<Record<UploadKey, UploadValue["value"]>>;
|
|
32
|
+
|
|
33
|
+
export interface UploadVerification {
|
|
34
|
+
// Azure UKI-only derived fields (not present in ParsedManifest)
|
|
35
|
+
azureUKIPcrLog?: string;
|
|
36
|
+
azureUKIPcrHash: {
|
|
37
|
+
value: string;
|
|
38
|
+
validated: Validated;
|
|
39
|
+
};
|
|
40
|
+
azureUKIPcr: {
|
|
41
|
+
value: string;
|
|
42
|
+
validated: Validated;
|
|
43
|
+
};
|
|
44
|
+
azureUKIKernel?: string;
|
|
45
|
+
azureUKICmdLine?: string;
|
|
46
|
+
azureUKIRootFSHash?: string;
|
|
47
|
+
azureUKIInitRDLate?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface VCompContainerInfo {
|
|
51
|
+
did: {
|
|
52
|
+
value: string;
|
|
53
|
+
validated: Validated;
|
|
54
|
+
};
|
|
55
|
+
signedBy: {
|
|
56
|
+
value: string;
|
|
57
|
+
validated: Validated;
|
|
58
|
+
};
|
|
59
|
+
dockerComposeId: {
|
|
60
|
+
value: string;
|
|
61
|
+
validated: Validated;
|
|
62
|
+
};
|
|
63
|
+
dockerImages: {
|
|
64
|
+
images: Array<{
|
|
65
|
+
name: string;
|
|
66
|
+
sha256: string;
|
|
67
|
+
}>;
|
|
68
|
+
validated: Validated;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface VCompSystemInfo {
|
|
73
|
+
did: {
|
|
74
|
+
value: string;
|
|
75
|
+
validated: Validated;
|
|
76
|
+
};
|
|
77
|
+
cpu: {
|
|
78
|
+
report: {
|
|
79
|
+
value: string | null;
|
|
80
|
+
validated: Validated;
|
|
81
|
+
};
|
|
82
|
+
reportParsed: unknown | null;
|
|
83
|
+
type: "intel" | "amd" | "azure" | null;
|
|
84
|
+
measurements: {
|
|
85
|
+
mrtd: {
|
|
86
|
+
value: string;
|
|
87
|
+
validated: Validated;
|
|
88
|
+
};
|
|
89
|
+
rtmr0: {
|
|
90
|
+
value: string;
|
|
91
|
+
validated: Validated;
|
|
92
|
+
};
|
|
93
|
+
rtmr1: {
|
|
94
|
+
value: string;
|
|
95
|
+
validated: Validated;
|
|
96
|
+
};
|
|
97
|
+
rtmr2: { value: string; validated: Validated };
|
|
98
|
+
mrconfigid?: string;
|
|
99
|
+
mrownerconfig?: string;
|
|
100
|
+
mrowner?: string;
|
|
101
|
+
xfam?: string;
|
|
102
|
+
tdattributes?: string;
|
|
103
|
+
rootfs: { value: string; validated: Validated };
|
|
104
|
+
amdMeasurement: {
|
|
105
|
+
value: string;
|
|
106
|
+
validated: Validated;
|
|
107
|
+
};
|
|
108
|
+
amdReportData?: string;
|
|
109
|
+
};
|
|
110
|
+
certificate: {
|
|
111
|
+
x509: string | null;
|
|
112
|
+
chain: unknown[] | null;
|
|
113
|
+
validated: Validated;
|
|
114
|
+
};
|
|
115
|
+
hardwareVerifyUrl: string | null;
|
|
116
|
+
};
|
|
117
|
+
gpu: {
|
|
118
|
+
report: {
|
|
119
|
+
value: string | null;
|
|
120
|
+
validated: Validated;
|
|
121
|
+
};
|
|
122
|
+
reportData: string | null;
|
|
123
|
+
certificate: {
|
|
124
|
+
x509: {
|
|
125
|
+
value: Buffer | null;
|
|
126
|
+
validated: Validated;
|
|
127
|
+
};
|
|
128
|
+
chain: unknown[] | null;
|
|
129
|
+
};
|
|
130
|
+
hardwareVerifyUrl: string | null;
|
|
131
|
+
};
|
|
132
|
+
azure: {
|
|
133
|
+
bootLog: {
|
|
134
|
+
value: string | null;
|
|
135
|
+
};
|
|
136
|
+
bootLogHash: {
|
|
137
|
+
value: string | null;
|
|
138
|
+
validated: Validated;
|
|
139
|
+
};
|
|
140
|
+
tpmAKCertificate: {
|
|
141
|
+
value: string | null;
|
|
142
|
+
validated: Validated;
|
|
143
|
+
};
|
|
144
|
+
tpmLog: string | null;
|
|
145
|
+
tpmQuote: {
|
|
146
|
+
value: string | null;
|
|
147
|
+
validated: Validated;
|
|
148
|
+
};
|
|
149
|
+
tpmQuoteSignature: {
|
|
150
|
+
value: string | null;
|
|
151
|
+
validated: Validated;
|
|
152
|
+
};
|
|
153
|
+
tpmPCRHash: {
|
|
154
|
+
value: string | null;
|
|
155
|
+
validated: Validated;
|
|
156
|
+
};
|
|
157
|
+
tpmNotaryHex: {
|
|
158
|
+
value: string | null;
|
|
159
|
+
validated: Validated;
|
|
160
|
+
};
|
|
161
|
+
tpmNotaryDID: {
|
|
162
|
+
value: string | null;
|
|
163
|
+
validated: Validated;
|
|
164
|
+
};
|
|
165
|
+
azureTPMAKKey: {
|
|
166
|
+
value: string | null;
|
|
167
|
+
validated: Validated;
|
|
168
|
+
};
|
|
169
|
+
azureAKTPMPubKey: {
|
|
170
|
+
value: string | null;
|
|
171
|
+
validated: Validated;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface ParsedManifest {
|
|
177
|
+
// map of did keys to container info
|
|
178
|
+
containers: Record<string, VCompContainerInfo>;
|
|
179
|
+
// map of did keys to system info
|
|
180
|
+
systems: Record<string, VCompSystemInfo>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function bufferToHex(buffer: Buffer): string {
|
|
184
|
+
return Array.from(new Uint8Array(buffer))
|
|
185
|
+
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
186
|
+
.join("");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Read a browser File into a Uint8Array
|
|
191
|
+
*/
|
|
192
|
+
async function readFileAsUint8Array(file: File): Promise<Uint8Array> {
|
|
193
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
194
|
+
return new Uint8Array(arrayBuffer);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Rough YAML detection similar to the legacy main.js
|
|
199
|
+
*/
|
|
200
|
+
function isLikelyYaml(text: string): boolean {
|
|
201
|
+
return /^[\s-]*[a-zA-Z0-9_-]+:/m.test(text);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Parse Linux kernel cmdline bytes into key=value pairs
|
|
206
|
+
*/
|
|
207
|
+
export function parseKernelArgs(cmdlineBytes: Uint8Array): Record<string, string> {
|
|
208
|
+
const decoder = new TextDecoder("ascii");
|
|
209
|
+
const input = decoder.decode(cmdlineBytes);
|
|
210
|
+
const result: Record<string, string> = {};
|
|
211
|
+
input.split(/\s+/).forEach((pair) => {
|
|
212
|
+
const index = pair.indexOf("=");
|
|
213
|
+
if (index !== -1) {
|
|
214
|
+
const key = pair.substring(0, index);
|
|
215
|
+
const value = pair.substring(index + 1);
|
|
216
|
+
result[key] = value;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Detect upload kind(s) from a File and return detected values
|
|
224
|
+
* Mirrors the order and checks from main.js's parseFile
|
|
225
|
+
*/
|
|
226
|
+
export async function detectUploadsFromFile(file: File): Promise<UploadValue[]> {
|
|
227
|
+
const out: UploadValue[] = [];
|
|
228
|
+
const uint8Array = await readFileAsUint8Array(file);
|
|
229
|
+
|
|
230
|
+
// JSON manifest detection (first byte '{')
|
|
231
|
+
if (uint8Array[0] === 0x7b) {
|
|
232
|
+
try {
|
|
233
|
+
const jsonString = new TextDecoder().decode(uint8Array);
|
|
234
|
+
const jsonObject = JSON.parse(jsonString);
|
|
235
|
+
out.push({ kind: "manifest", value: jsonObject });
|
|
236
|
+
// When manifest is detected, legacy logic resets other parsed states.
|
|
237
|
+
// We still return below to also allow other detections if needed.
|
|
238
|
+
} catch (_e) {
|
|
239
|
+
// Not valid JSON, continue
|
|
240
|
+
}
|
|
241
|
+
} else if (uint8Array.length < 1_000_000) {
|
|
242
|
+
// Text-sized file: YAML or cmdline
|
|
243
|
+
const textContent = new TextDecoder().decode(uint8Array);
|
|
244
|
+
if (isLikelyYaml(textContent)) {
|
|
245
|
+
out.push({ kind: "compose", value: uint8Array });
|
|
246
|
+
} else {
|
|
247
|
+
out.push({ kind: "cmdline", value: uint8Array });
|
|
248
|
+
}
|
|
249
|
+
} else if (uint8Array.length < 5_000_000) {
|
|
250
|
+
// Likely OVMF firmware blob
|
|
251
|
+
out.push({ kind: "ovmf", value: uint8Array });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Gzip magic = initrd
|
|
255
|
+
if (uint8Array[0] === 0x1f && uint8Array[1] === 0x8b) {
|
|
256
|
+
out.push({ kind: "initrd", value: uint8Array });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 'MZ' magic (0x4d 0x5a) heuristic for kernel image here
|
|
260
|
+
if (uint8Array[0] === 0x4d && uint8Array[1] === 0x5a) {
|
|
261
|
+
out.push({ kind: "kernel", value: uint8Array });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Verify uploaded blobs against parsed manifest measurements where feasible.
|
|
269
|
+
* Returns simple booleans that a component can use to decorate the UI.
|
|
270
|
+
*/
|
|
271
|
+
export async function verifyUploads(uploads: UploadsMap, parsed: ParsedManifest): Promise<UploadVerification> {
|
|
272
|
+
const result: UploadVerification = {
|
|
273
|
+
// Azure UKI
|
|
274
|
+
azureUKIPcrLog: "",
|
|
275
|
+
azureUKIPcrHash: {
|
|
276
|
+
value: "",
|
|
277
|
+
validated: "",
|
|
278
|
+
},
|
|
279
|
+
azureUKIPcr: {
|
|
280
|
+
value: "",
|
|
281
|
+
validated: "",
|
|
282
|
+
},
|
|
283
|
+
azureUKIKernel: "",
|
|
284
|
+
azureUKICmdLine: "",
|
|
285
|
+
azureUKIRootFSHash: "",
|
|
286
|
+
azureUKIInitRDLate: "",
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
for (const key in parsed.systems) {
|
|
290
|
+
// Only Intel TDX verifications are implemented to mirror main.js
|
|
291
|
+
if (parsed.systems[key].cpu.type === "intel") {
|
|
292
|
+
const intel = new intel_tdx();
|
|
293
|
+
|
|
294
|
+
if (uploads.ovmf) {
|
|
295
|
+
const calculated_mrtd = await intel.calcMrtd(uploads.ovmf as Uint8Array);
|
|
296
|
+
if (parsed.systems[key].cpu.measurements.mrtd.value === calculated_mrtd) {
|
|
297
|
+
parsed.systems[key].cpu.measurements.mrtd.validated = "valid";
|
|
298
|
+
} else {
|
|
299
|
+
parsed.systems[key].cpu.measurements.mrtd.validated = "invalid";
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const useGpu = false; // Unknown here; legacy code toggles based on GPU report presence
|
|
303
|
+
const calculated_rtmr0 = intel.calcRtmr0(uploads.ovmf as Uint8Array, useGpu ? 1 : 0);
|
|
304
|
+
if (parsed.systems[key].cpu.measurements.rtmr0.value === calculated_rtmr0) {
|
|
305
|
+
parsed.systems[key].cpu.measurements.rtmr0.validated = "valid";
|
|
306
|
+
} else {
|
|
307
|
+
parsed.systems[key].cpu.measurements.rtmr0.validated = "invalid";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (uploads.cmdline && uploads.initrd) {
|
|
312
|
+
const calculated_rtmr2 = intel.calcRtmr2(uploads.cmdline as Uint8Array, uploads.initrd as Uint8Array);
|
|
313
|
+
|
|
314
|
+
if (parsed.systems[key].cpu.measurements.rtmr2.value == calculated_rtmr2) {
|
|
315
|
+
parsed.systems[key].cpu.measurements.rtmr2.validated = "valid";
|
|
316
|
+
} else {
|
|
317
|
+
parsed.systems[key].cpu.measurements.rtmr2.validated = "invalid";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const cmdParse = parseKernelArgs(uploads.cmdline as Uint8Array);
|
|
321
|
+
|
|
322
|
+
if (parsed.systems[key].cpu.measurements.rootfs.value == cmdParse.rootfs) {
|
|
323
|
+
parsed.systems[key].cpu.measurements.rootfs.validated = "valid";
|
|
324
|
+
} else {
|
|
325
|
+
parsed.systems[key].cpu.measurements.rootfs.validated = "invalid";
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (uploads.kernel && uploads.initrd) {
|
|
330
|
+
const calculated_rtmr1 = await intel.calcRtmr1(
|
|
331
|
+
uploads.kernel as Uint8Array,
|
|
332
|
+
uploads.initrd as Uint8Array,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (parsed.systems[key].cpu.measurements.rtmr1.value == calculated_rtmr1) {
|
|
336
|
+
parsed.systems[key].cpu.measurements.rtmr1.validated = "valid";
|
|
337
|
+
} else {
|
|
338
|
+
parsed.systems[key].cpu.measurements.rtmr1.validated = "invalid";
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Azure UKI: compare PCR11 hash with TPM PCR hash from Azure report
|
|
344
|
+
if (parsed.systems[key].cpu.type === "azure" && uploads.kernel) {
|
|
345
|
+
const U = uploads.kernel as Uint8Array;
|
|
346
|
+
const ukiFile = new UKI(U);
|
|
347
|
+
const pcrLog = ukiFile.PCR;
|
|
348
|
+
let pcrLogText = "";
|
|
349
|
+
pcrLog.PCR_LOG.forEach((item: string) => {
|
|
350
|
+
pcrLogText = pcrLogText + item + "\n";
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
result.azureUKIPcrLog = pcrLogText;
|
|
354
|
+
result.azureUKIPcr.value = pcrLog.PCR;
|
|
355
|
+
result.azureUKIKernel = ukiFile.UKI_MODEL[".linux"]["sha256"];
|
|
356
|
+
result.azureUKICmdLine = ukiFile.UKI_MODEL[".cmdline"]["sha256"];
|
|
357
|
+
result.azureUKIRootFSHash = ukiFile.UKI_MODEL[".cmdline"]["extra"]["rootfs"];
|
|
358
|
+
result.azureUKIInitRDLate = ukiFile.UKI_MODEL[".initrd"]["extra"]["late"];
|
|
359
|
+
|
|
360
|
+
result.azureUKIPcrHash.value = TPM.hash_pcr_sha256(pcrLog.PCR);
|
|
361
|
+
|
|
362
|
+
if (result.azureUKIPcrHash.value === parsed.systems[key].azure.tpmPCRHash.value) {
|
|
363
|
+
result.azureUKIPcrHash.validated = "valid";
|
|
364
|
+
parsed.systems[key].azure.tpmPCRHash.validated = "valid";
|
|
365
|
+
result.azureUKIPcr.validated = "valid";
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Docker Compose CID verification
|
|
371
|
+
if (uploads.compose) {
|
|
372
|
+
const calculatedCid = await cidGenerator.fromArrayBuffer(uploads.compose as unknown as ArrayBuffer);
|
|
373
|
+
|
|
374
|
+
for (const key in parsed.containers) {
|
|
375
|
+
const container = parsed.containers[key];
|
|
376
|
+
if (container.dockerComposeId.value === calculatedCid) {
|
|
377
|
+
container.dockerComposeId.validated = "valid";
|
|
378
|
+
} else {
|
|
379
|
+
container.dockerComposeId.validated = "invalid";
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function parse(MANIFEST: unknown): Promise<ParsedManifest> {
|
|
387
|
+
const vc = new vc_parse(MANIFEST);
|
|
388
|
+
|
|
389
|
+
// Initialize result object
|
|
390
|
+
const result: ParsedManifest = {
|
|
391
|
+
containers: {},
|
|
392
|
+
systems: {},
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Container parsing
|
|
396
|
+
const ContainerDID = vc.getContainerDID();
|
|
397
|
+
|
|
398
|
+
for (let i = 0; i < ContainerDID.length; i++) {
|
|
399
|
+
const statement = ContainerDID[i];
|
|
400
|
+
const didKey = statement["did"];
|
|
401
|
+
|
|
402
|
+
const images = statement["vcomp"]["image"].map((image: unknown) => ({
|
|
403
|
+
name: (image as { name: string }).name,
|
|
404
|
+
sha256: (image as { sha256: string }).sha256.replace(/^sha256:/, ""),
|
|
405
|
+
}));
|
|
406
|
+
|
|
407
|
+
const vcs = vc.getCredentialRegistration(statement["@id"])[0];
|
|
408
|
+
const vcValidated = vc.validateVC(vcs);
|
|
409
|
+
|
|
410
|
+
result.containers[didKey] = {
|
|
411
|
+
did: {
|
|
412
|
+
value: didKey,
|
|
413
|
+
validated: vcValidated ? "valid" : "invalid",
|
|
414
|
+
},
|
|
415
|
+
signedBy: {
|
|
416
|
+
value: ContainerDID[i]["registeredBy"],
|
|
417
|
+
validated: vcValidated ? "valid" : "invalid",
|
|
418
|
+
},
|
|
419
|
+
dockerComposeId: {
|
|
420
|
+
value: ContainerDID[i]["vcomp"]["compose"].replace(/^urn:cid:/, ""),
|
|
421
|
+
validated: vcValidated ? "partial" : "invalid",
|
|
422
|
+
},
|
|
423
|
+
dockerImages: {
|
|
424
|
+
images,
|
|
425
|
+
validated: vcValidated ? "partial" : "invalid",
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const NotaryDID = vc.getNotaryDIDRegistrations();
|
|
431
|
+
|
|
432
|
+
for (let i = 0; i < NotaryDID.length; i++) {
|
|
433
|
+
const statement = NotaryDID[i];
|
|
434
|
+
const didKey = statement["did"];
|
|
435
|
+
|
|
436
|
+
result.systems[didKey] = {
|
|
437
|
+
did: {
|
|
438
|
+
value: didKey,
|
|
439
|
+
validated: "",
|
|
440
|
+
},
|
|
441
|
+
cpu: {
|
|
442
|
+
report: {
|
|
443
|
+
value: null,
|
|
444
|
+
validated: "",
|
|
445
|
+
},
|
|
446
|
+
reportParsed: null,
|
|
447
|
+
type: null,
|
|
448
|
+
measurements: {
|
|
449
|
+
mrtd: {
|
|
450
|
+
value: "",
|
|
451
|
+
validated: "",
|
|
452
|
+
},
|
|
453
|
+
rtmr0: {
|
|
454
|
+
value: "",
|
|
455
|
+
validated: "",
|
|
456
|
+
},
|
|
457
|
+
rtmr1: {
|
|
458
|
+
value: "",
|
|
459
|
+
validated: "",
|
|
460
|
+
},
|
|
461
|
+
rtmr2: {
|
|
462
|
+
value: "",
|
|
463
|
+
validated: "",
|
|
464
|
+
},
|
|
465
|
+
rootfs: {
|
|
466
|
+
value: "",
|
|
467
|
+
validated: "",
|
|
468
|
+
},
|
|
469
|
+
amdMeasurement: {
|
|
470
|
+
value: "",
|
|
471
|
+
validated: "",
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
certificate: {
|
|
475
|
+
x509: null,
|
|
476
|
+
chain: null,
|
|
477
|
+
validated: "",
|
|
478
|
+
},
|
|
479
|
+
hardwareVerifyUrl: null,
|
|
480
|
+
},
|
|
481
|
+
gpu: {
|
|
482
|
+
report: {
|
|
483
|
+
value: null,
|
|
484
|
+
validated: "",
|
|
485
|
+
},
|
|
486
|
+
reportData: null,
|
|
487
|
+
certificate: {
|
|
488
|
+
x509: {
|
|
489
|
+
value: null,
|
|
490
|
+
validated: "",
|
|
491
|
+
},
|
|
492
|
+
chain: null,
|
|
493
|
+
},
|
|
494
|
+
hardwareVerifyUrl: null,
|
|
495
|
+
},
|
|
496
|
+
azure: {
|
|
497
|
+
bootLog: {
|
|
498
|
+
value: null,
|
|
499
|
+
},
|
|
500
|
+
bootLogHash: {
|
|
501
|
+
value: null,
|
|
502
|
+
validated: "",
|
|
503
|
+
},
|
|
504
|
+
tpmAKCertificate: {
|
|
505
|
+
value: null,
|
|
506
|
+
validated: "",
|
|
507
|
+
},
|
|
508
|
+
tpmLog: null,
|
|
509
|
+
tpmQuote: {
|
|
510
|
+
value: null,
|
|
511
|
+
validated: "",
|
|
512
|
+
},
|
|
513
|
+
tpmQuoteSignature: {
|
|
514
|
+
value: null,
|
|
515
|
+
validated: "",
|
|
516
|
+
},
|
|
517
|
+
tpmPCRHash: {
|
|
518
|
+
value: null,
|
|
519
|
+
validated: "",
|
|
520
|
+
},
|
|
521
|
+
tpmNotaryHex: {
|
|
522
|
+
value: null,
|
|
523
|
+
validated: "",
|
|
524
|
+
},
|
|
525
|
+
tpmNotaryDID: {
|
|
526
|
+
value: null,
|
|
527
|
+
validated: "",
|
|
528
|
+
},
|
|
529
|
+
azureTPMAKKey: {
|
|
530
|
+
value: null,
|
|
531
|
+
validated: "",
|
|
532
|
+
},
|
|
533
|
+
azureAKTPMPubKey: {
|
|
534
|
+
value: null,
|
|
535
|
+
validated: "",
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// Find the Evidence for the system notary
|
|
541
|
+
let systemNotarySubject = null;
|
|
542
|
+
let systemVC = [];
|
|
543
|
+
|
|
544
|
+
if (statement["@id"]) {
|
|
545
|
+
systemNotarySubject = statement["@id"];
|
|
546
|
+
systemVC = vc.getCredentialRegistration(systemNotarySubject);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
let CPU_REPORT: Buffer | null = null;
|
|
550
|
+
let GPU_REPORT: Buffer | null = null;
|
|
551
|
+
let GPU_CHAIN: Buffer | null = null;
|
|
552
|
+
let AZURE_REPORT: unknown = null;
|
|
553
|
+
let CPU_REPORT_PARSED: unknown = null;
|
|
554
|
+
const URL: { [key: string]: string } = {};
|
|
555
|
+
|
|
556
|
+
// Loop through registrations looking for Attestations
|
|
557
|
+
for (let i = 0; i < systemVC.length; i++) {
|
|
558
|
+
if (systemVC[i]["credential"]["evidence"]["type"]) {
|
|
559
|
+
const report_cid = systemVC[i]["credential"]["evidence"]["report"];
|
|
560
|
+
const REPORT = vc.getBloblByCID(report_cid);
|
|
561
|
+
|
|
562
|
+
const cert_cid = systemVC[i]["credential"]["evidence"]["certificateChain"];
|
|
563
|
+
let CHAIN: Buffer | null = null;
|
|
564
|
+
if (cert_cid) {
|
|
565
|
+
CHAIN = vc.getBloblByCID(cert_cid);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// NVIDIA evidence
|
|
569
|
+
if (systemVC[i]["credential"]["evidence"]["type"].includes("EqtyVCompNvidiaCcV0Evidence")) {
|
|
570
|
+
GPU_REPORT = REPORT;
|
|
571
|
+
GPU_CHAIN = CHAIN;
|
|
572
|
+
|
|
573
|
+
if (GPU_CHAIN) {
|
|
574
|
+
const endMarker = "-----END CERTIFICATE-----";
|
|
575
|
+
const parts = GPU_CHAIN.toString("utf8").split(endMarker);
|
|
576
|
+
const firstCert = parts[0] + endMarker;
|
|
577
|
+
const res = Buffer.concat([REPORT, Buffer.from(firstCert)]);
|
|
578
|
+
|
|
579
|
+
URL["nvidia"] =
|
|
580
|
+
"https://hardware.verify.eqtylab.io/nvidia.html?report=" +
|
|
581
|
+
encodeURIComponent(res.toString("base64")) +
|
|
582
|
+
"&did=" +
|
|
583
|
+
didKey;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Intel Evidence
|
|
588
|
+
if (systemVC[i]["credential"]["evidence"]["type"].includes("EqtyVCompIntelTdxV0Evidence")) {
|
|
589
|
+
CPU_REPORT = REPORT;
|
|
590
|
+
URL["CPU"] =
|
|
591
|
+
"https://hardware.verify.eqtylab.io/intel.html?report=" +
|
|
592
|
+
encodeURIComponent(REPORT.toString("base64")) +
|
|
593
|
+
"&did=" +
|
|
594
|
+
didKey;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// AMD Evidence
|
|
598
|
+
if (systemVC[i]["credential"]["evidence"]["type"].includes("EqtyVCompAmdSevV1Evidence")) {
|
|
599
|
+
CPU_REPORT = REPORT;
|
|
600
|
+
URL["CPU"] =
|
|
601
|
+
"https://hardware.verify.eqtylab.io/amd.html?report=" +
|
|
602
|
+
encodeURIComponent(REPORT.toString("base64")) +
|
|
603
|
+
"&did=" +
|
|
604
|
+
didKey;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Azure Evidence
|
|
608
|
+
if (systemVC[i]["credential"]["evidence"]["type"].includes("EqtyVCompAzureV1Evidence")) {
|
|
609
|
+
CPU_REPORT = REPORT;
|
|
610
|
+
|
|
611
|
+
AZURE_REPORT = {
|
|
612
|
+
azureBootLog: vc.getBloblByCID(systemVC[i]["credential"]["evidence"]["azureBootLog"]),
|
|
613
|
+
tpmAKCertificate: vc.getBloblByCID(systemVC[i]["credential"]["evidence"]["tpmAKCertificate"]),
|
|
614
|
+
tpmLog: vc.getBloblByCID(systemVC[i]["credential"]["evidence"]["tpmLog"]),
|
|
615
|
+
tpmQuote: vc.getBloblByCID(systemVC[i]["credential"]["evidence"]["tpmQuote"]),
|
|
616
|
+
tpmQuoteSignature: vc.getBloblByCID(systemVC[i]["credential"]["evidence"]["tpmQuoteSignature"]),
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
if (TEEAttestation.reportType(REPORT) == "INTEL") {
|
|
620
|
+
URL["CPU"] =
|
|
621
|
+
"https://hardware.verify.eqtylab.io/intel.html?report=" +
|
|
622
|
+
encodeURIComponent(REPORT.toString("base64"));
|
|
623
|
+
} else {
|
|
624
|
+
URL["CPU"] =
|
|
625
|
+
"https://hardware.verify.eqtylab.io/?report=" +
|
|
626
|
+
encodeURIComponent(REPORT.toString("base64"));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Process CPU Report
|
|
633
|
+
if (CPU_REPORT) {
|
|
634
|
+
result.systems[didKey].cpu.report.value = CPU_REPORT.toString("base64");
|
|
635
|
+
result.systems[didKey].cpu.hardwareVerifyUrl = URL["CPU"];
|
|
636
|
+
|
|
637
|
+
const CPU_REPORT_BYTE = Buffer.from(CPU_REPORT);
|
|
638
|
+
const REPORT_DATA = TEEAttestation.parse(CPU_REPORT_BYTE);
|
|
639
|
+
CPU_REPORT_PARSED = REPORT_DATA;
|
|
640
|
+
result.systems[didKey].cpu.reportParsed = REPORT_DATA;
|
|
641
|
+
|
|
642
|
+
if (REPORT_DATA.TYPE == "amd") {
|
|
643
|
+
result.systems[didKey].cpu.type = "amd";
|
|
644
|
+
result.systems[didKey].cpu.measurements.amdMeasurement.value = bufferToHex(REPORT_DATA.MEASUREMENT);
|
|
645
|
+
result.systems[didKey].cpu.measurements.amdReportData = (
|
|
646
|
+
CPU_REPORT_PARSED as { EQTY_USER_DATA: Buffer }
|
|
647
|
+
).EQTY_USER_DATA.toString("hex");
|
|
648
|
+
} else if (REPORT_DATA.TYPE == "intel") {
|
|
649
|
+
result.systems[didKey].cpu.type = "intel";
|
|
650
|
+
result.systems[didKey].cpu.measurements.mrtd.value = bufferToHex(REPORT_DATA.MRTD);
|
|
651
|
+
result.systems[didKey].cpu.measurements.rtmr0.value = bufferToHex(REPORT_DATA.RTMR0);
|
|
652
|
+
result.systems[didKey].cpu.measurements.rtmr1.value = bufferToHex(REPORT_DATA.RTMR1);
|
|
653
|
+
result.systems[didKey].cpu.measurements.rtmr2.value = bufferToHex(REPORT_DATA.RTMR2);
|
|
654
|
+
result.systems[didKey].cpu.measurements.mrconfigid = bufferToHex(REPORT_DATA.MRCONFIGID);
|
|
655
|
+
result.systems[didKey].cpu.measurements.mrownerconfig = bufferToHex(REPORT_DATA.MROWNERCONFIG);
|
|
656
|
+
result.systems[didKey].cpu.measurements.mrowner = bufferToHex(REPORT_DATA.MROWNER);
|
|
657
|
+
result.systems[didKey].cpu.measurements.xfam = bufferToHex(REPORT_DATA.XFAM);
|
|
658
|
+
result.systems[didKey].cpu.measurements.tdattributes = bufferToHex(REPORT_DATA.TDATTRIBUTES);
|
|
659
|
+
result.systems[didKey].cpu.measurements.rootfs.value = REPORT_DATA.REPORT_DATA_ROOTFS_HASH_HEX;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Process certificate
|
|
663
|
+
result.systems[didKey].cpu.certificate.x509 = REPORT_DATA.CERT;
|
|
664
|
+
const cert_chain_status = checkChain(REPORT_DATA.CERT);
|
|
665
|
+
if (cert_chain_status) {
|
|
666
|
+
result.systems[didKey].cpu.certificate.chain = parseChain(REPORT_DATA.CERT);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Validate attestation
|
|
670
|
+
if (REPORT_DATA.TYPE == "amd") {
|
|
671
|
+
const amd = new amd_sev_snp();
|
|
672
|
+
if (await amd.verify(CPU_REPORT)) {
|
|
673
|
+
result.systems[didKey].cpu.report.validated = "valid";
|
|
674
|
+
result.systems[didKey].cpu.certificate.validated = "valid";
|
|
675
|
+
result.systems[didKey].cpu.measurements.amdMeasurement.validated = "valid";
|
|
676
|
+
} else {
|
|
677
|
+
result.systems[didKey].cpu.report.validated = "invalid";
|
|
678
|
+
result.systems[didKey].cpu.certificate.validated = "invalid";
|
|
679
|
+
result.systems[didKey].cpu.measurements.amdMeasurement.validated = "invalid";
|
|
680
|
+
}
|
|
681
|
+
} else if (REPORT_DATA.TYPE == "intel") {
|
|
682
|
+
const intel = new intel_tdx();
|
|
683
|
+
if (await intel.verify(CPU_REPORT)) {
|
|
684
|
+
result.systems[didKey].cpu.report.validated = "valid";
|
|
685
|
+
result.systems[didKey].cpu.certificate.validated = "valid";
|
|
686
|
+
|
|
687
|
+
result.systems[didKey].cpu.measurements.mrtd.validated = "partial";
|
|
688
|
+
result.systems[didKey].cpu.measurements.rtmr0.validated = "partial";
|
|
689
|
+
result.systems[didKey].cpu.measurements.rtmr1.validated = "partial";
|
|
690
|
+
result.systems[didKey].cpu.measurements.rtmr2.validated = "partial";
|
|
691
|
+
result.systems[didKey].cpu.measurements.rootfs.validated = "partial";
|
|
692
|
+
} else {
|
|
693
|
+
result.systems[didKey].cpu.report.validated = "invalid";
|
|
694
|
+
result.systems[didKey].cpu.certificate.validated = "invalid";
|
|
695
|
+
result.systems[didKey].did.validated = "invalid";
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Process GPU Report
|
|
701
|
+
if (GPU_REPORT) {
|
|
702
|
+
result.systems[didKey].gpu.report.value = GPU_REPORT.toString("base64");
|
|
703
|
+
result.systems[didKey].gpu.hardwareVerifyUrl = URL["nvidia"];
|
|
704
|
+
|
|
705
|
+
const nvidia_report = new nvidia();
|
|
706
|
+
const REPORT = await nvidia_report.parse_nvidia_attestation(GPU_REPORT);
|
|
707
|
+
result.systems[didKey].gpu.reportData = REPORT.nonce;
|
|
708
|
+
|
|
709
|
+
if (GPU_CHAIN) {
|
|
710
|
+
result.systems[didKey].gpu.certificate.x509.value = GPU_CHAIN;
|
|
711
|
+
|
|
712
|
+
if (await nvidia_report.verify(GPU_REPORT, GPU_CHAIN)) {
|
|
713
|
+
result.systems[didKey].gpu.report.validated = "valid";
|
|
714
|
+
result.systems[didKey].gpu.certificate.x509.validated = "valid";
|
|
715
|
+
} else {
|
|
716
|
+
result.systems[didKey].gpu.report.validated = "invalid";
|
|
717
|
+
result.systems[didKey].gpu.certificate.x509.validated = "invalid";
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const GPU_CHAIN_PEM = new TextDecoder().decode(GPU_CHAIN);
|
|
721
|
+
const cert_chain_status = checkChain(GPU_CHAIN_PEM);
|
|
722
|
+
if (cert_chain_status) {
|
|
723
|
+
result.systems[didKey].gpu.certificate.chain = parseChain(GPU_CHAIN_PEM);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Process Azure Report
|
|
729
|
+
if (AZURE_REPORT) {
|
|
730
|
+
result.systems[didKey].cpu.type = "azure";
|
|
731
|
+
const azureReport = AZURE_REPORT as {
|
|
732
|
+
azureBootLog: Buffer;
|
|
733
|
+
tpmAKCertificate: Buffer;
|
|
734
|
+
tpmLog: Buffer;
|
|
735
|
+
tpmQuote: Buffer;
|
|
736
|
+
tpmQuoteSignature: Buffer;
|
|
737
|
+
};
|
|
738
|
+
result.systems[didKey].azure.bootLog.value = azureReport.azureBootLog?.toString("base64") || null;
|
|
739
|
+
result.systems[didKey].azure.tpmAKCertificate.value =
|
|
740
|
+
azureReport.tpmAKCertificate?.toString("base64") || null;
|
|
741
|
+
result.systems[didKey].azure.tpmLog = azureReport.tpmLog?.toString("base64") || null;
|
|
742
|
+
result.systems[didKey].azure.tpmQuote.value = azureReport.tpmQuote?.toString("base64") || null;
|
|
743
|
+
result.systems[didKey].azure.tpmQuoteSignature.value =
|
|
744
|
+
azureReport.tpmQuoteSignature?.toString("base64") || null;
|
|
745
|
+
|
|
746
|
+
// Read Measurement from attestation report
|
|
747
|
+
result.systems[didKey].azure.bootLogHash.value =
|
|
748
|
+
(CPU_REPORT_PARSED as { EQTY_USER_DATA?: string })?.EQTY_USER_DATA?.substring(0, 64) || null;
|
|
749
|
+
|
|
750
|
+
// Extract vTPM key from Azure Boot Logs
|
|
751
|
+
const bootlog = azure.parseBootLogs(azureReport.azureBootLog);
|
|
752
|
+
result.systems[didKey].azure.azureTPMAKKey.value = bootlog.AKPubKey;
|
|
753
|
+
|
|
754
|
+
// Calculate attestation from azureBootLog
|
|
755
|
+
if (result.systems[didKey].azure.bootLogHash.value == bootlog.bootLogsHash) {
|
|
756
|
+
result.systems[didKey].azure.bootLogHash.validated = "valid";
|
|
757
|
+
} else {
|
|
758
|
+
result.systems[didKey].azure.bootLogHash.validated = "invalid";
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Get public key
|
|
762
|
+
const certinfo = getCertificateInfo(azureReport.tpmAKCertificate);
|
|
763
|
+
result.systems[didKey].azure.azureAKTPMPubKey.value = getPubKeyRaw(certinfo.pubkey);
|
|
764
|
+
|
|
765
|
+
// Validate TPMQuote and signatures
|
|
766
|
+
const isTPMQuoteValid = TPM.verify_tpm(
|
|
767
|
+
azureReport.tpmAKCertificate,
|
|
768
|
+
azureReport.tpmQuoteSignature,
|
|
769
|
+
azureReport.tpmQuote,
|
|
770
|
+
);
|
|
771
|
+
|
|
772
|
+
if (isTPMQuoteValid) {
|
|
773
|
+
result.systems[didKey].azure.tpmAKCertificate.validated = "valid";
|
|
774
|
+
result.systems[didKey].azure.tpmQuote.validated = "valid";
|
|
775
|
+
result.systems[didKey].azure.tpmQuoteSignature.validated = "valid";
|
|
776
|
+
|
|
777
|
+
if (
|
|
778
|
+
result.systems[didKey].azure.azureTPMAKKey.value ===
|
|
779
|
+
result.systems[didKey].azure.azureAKTPMPubKey.value
|
|
780
|
+
) {
|
|
781
|
+
result.systems[didKey].azure.azureTPMAKKey.validated = "valid";
|
|
782
|
+
result.systems[didKey].azure.azureAKTPMPubKey.validated = "valid";
|
|
783
|
+
} else {
|
|
784
|
+
result.systems[didKey].azure.azureTPMAKKey.validated = "invalid";
|
|
785
|
+
result.systems[didKey].azure.azureAKTPMPubKey.validated = "invalid";
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
result.systems[didKey].azure.tpmAKCertificate.validated = "invalid";
|
|
789
|
+
result.systems[didKey].azure.tpmQuote.validated = "invalid";
|
|
790
|
+
result.systems[didKey].azure.tpmQuoteSignature.validated = "invalid";
|
|
791
|
+
result.systems[didKey].azure.azureTPMAKKey.validated = "invalid";
|
|
792
|
+
result.systems[didKey].azure.azureAKTPMPubKey.validated = "invalid";
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Parse TPM Quote
|
|
796
|
+
const quoteData = TPM.parseQuoteData(azureReport.tpmQuote);
|
|
797
|
+
result.systems[didKey].azure.tpmPCRHash.value = bufferToHex(quoteData.pcrData);
|
|
798
|
+
result.systems[didKey].azure.tpmNotaryHex.value = bufferToHex(quoteData.notaryKey);
|
|
799
|
+
result.systems[didKey].azure.tpmNotaryDID.value = vc_parse
|
|
800
|
+
.pubkey2did(bufferToHex(quoteData.notaryKey))
|
|
801
|
+
.replace(/^did:key:/, "");
|
|
802
|
+
|
|
803
|
+
if (result.systems[didKey].did.value == result.systems[didKey].azure.tpmNotaryDID.value) {
|
|
804
|
+
result.systems[didKey].did.validated = "valid";
|
|
805
|
+
result.systems[didKey].azure.tpmNotaryDID.validated = "valid";
|
|
806
|
+
result.systems[didKey].azure.tpmNotaryHex.validated = "valid";
|
|
807
|
+
} else {
|
|
808
|
+
result.systems[didKey].did.validated = "invalid";
|
|
809
|
+
result.systems[didKey].azure.tpmNotaryDID.validated = "invalid";
|
|
810
|
+
result.systems[didKey].azure.tpmNotaryHex.validated = "invalid";
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Validate system DID
|
|
814
|
+
result.systems[didKey].did.validated = "valid";
|
|
815
|
+
} else {
|
|
816
|
+
//##TODO## check if the systemDID is valid from the CPU Report
|
|
817
|
+
result.systems[didKey].did.validated = "valid"; // Default to validated if no Azure report
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export { parse };
|