@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.
Files changed (140) hide show
  1. package/.env.api +6 -0
  2. package/.env.iframe +2 -0
  3. package/.env.manifest +2 -0
  4. package/.envrc +1 -0
  5. package/.eslintrc.cjs +17 -0
  6. package/.github/workflows/ci.yml +46 -0
  7. package/.github/workflows/publish.yml +44 -0
  8. package/.husky/pre-push +2 -0
  9. package/.prettierignore +2 -0
  10. package/.prettierrc.json +13 -0
  11. package/LICENSE +201 -0
  12. package/README.md +84 -0
  13. package/flake.lock +61 -0
  14. package/flake.nix +23 -0
  15. package/index.html +13 -0
  16. package/package.json +93 -0
  17. package/postcss.config.js +5 -0
  18. package/public/logos/eci.svg +23 -0
  19. package/public/logos/eqty.svg +3 -0
  20. package/public/manifests/B-200-merged.json +773 -0
  21. package/public/manifests/amd-sev.json +426 -0
  22. package/public/manifests/anchors.json +609 -0
  23. package/public/manifests/association-no-metadata.json +1393 -0
  24. package/public/manifests/association.json +1402 -0
  25. package/public/manifests/eqty-docker.json +562 -0
  26. package/public/manifests/governance.json +1324 -0
  27. package/public/manifests/intel-tdx-with-nvidia.json +441 -0
  28. package/public/manifests/intel-tdx.json +404 -0
  29. package/public/manifests/iroh-collections.json +1285 -0
  30. package/public/manifests/model-signing.json +1536 -0
  31. package/public/manifests/multiple-registrations.json +129 -0
  32. package/public/manifests/no-vcs.json +114 -0
  33. package/public/manifests/storage.json +294 -0
  34. package/public/manifests/sub-graphs.json +1 -0
  35. package/src/animations/variants.ts +52 -0
  36. package/src/app/apiClient/integrityService/fetchAttributes.ts +29 -0
  37. package/src/app/apiClient/integrityService/fetchManifest.ts +28 -0
  38. package/src/app/apiClient/integrityService/types.ts +12 -0
  39. package/src/app/components/integrityService/IntegrityService.tsx +105 -0
  40. package/src/app/components/integrityService/components/AuthButton.tsx +26 -0
  41. package/src/app/components/integrityService/components/FiltersModal.tsx +214 -0
  42. package/src/app/components/integrityService/components/FiltersToggle.tsx +28 -0
  43. package/src/app/components/manifestViewer/ManifestViewer.tsx +90 -0
  44. package/src/app/components/manifestViewer/components/ManifestButtons.tsx +215 -0
  45. package/src/app/config.json +23 -0
  46. package/src/app/config.ts +28 -0
  47. package/src/app/pages/index.tsx +34 -0
  48. package/src/app/stores/manifestStore.ts +19 -0
  49. package/src/app/utils/appMode.ts +37 -0
  50. package/src/assets/fonts/TWKLausanne-300.woff +0 -0
  51. package/src/assets/fonts/TWKLausanne-300.woff2 +0 -0
  52. package/src/assets/fonts/TWKLausanne-400.woff +0 -0
  53. package/src/assets/fonts/TWKLausanne-400.woff2 +0 -0
  54. package/src/assets/fonts/TWKLausanne-500.woff +0 -0
  55. package/src/assets/fonts/TWKLausanne-500.woff2 +0 -0
  56. package/src/assets/fonts/TWKLausanne-600.woff +0 -0
  57. package/src/assets/fonts/TWKLausanne-600.woff2 +0 -0
  58. package/src/assets/vectors/eqtylab.svg +6 -0
  59. package/src/components/app-header.tsx +61 -0
  60. package/src/components/dark-mode-switch.tsx +16 -0
  61. package/src/components/ui/accordion.tsx +47 -0
  62. package/src/components/ui/certificate-chain.tsx +60 -0
  63. package/src/components/ui/certificate-code.tsx +39 -0
  64. package/src/components/ui/details.tsx +20 -0
  65. package/src/components/ui/display-code.tsx +47 -0
  66. package/src/components/ui/panel-label.tsx +12 -0
  67. package/src/components/ui/verify-state-badge.tsx +32 -0
  68. package/src/explorer-component/LineageExplorer.tsx +89 -0
  69. package/src/explorer-component/components/CustomEdge.tsx +28 -0
  70. package/src/explorer-component/components/LineageFlow.tsx +220 -0
  71. package/src/explorer-component/components/NodeIconLoader.tsx +26 -0
  72. package/src/explorer-component/components/nodes/ComputationNode.tsx +56 -0
  73. package/src/explorer-component/components/nodes/CustomNode.tsx +112 -0
  74. package/src/explorer-component/components/nodes/index.ts +4 -0
  75. package/src/explorer-component/components/nodes/nodes.tsx +144 -0
  76. package/src/explorer-component/components/nodes/types.ts +167 -0
  77. package/src/explorer-component/components/sidebar/SectionList.tsx +54 -0
  78. package/src/explorer-component/components/sidebar/SectionTitle.tsx +10 -0
  79. package/src/explorer-component/components/sidebar/Sidebar.tsx +70 -0
  80. package/src/explorer-component/components/sidebar/SidebarWrapper.tsx +76 -0
  81. package/src/explorer-component/components/sidebar/sections/CustomSection.tsx +238 -0
  82. package/src/explorer-component/components/sidebar/sections/FileViewer.tsx +229 -0
  83. package/src/explorer-component/components/sidebar/sections/GovernanceSection.tsx +52 -0
  84. package/src/explorer-component/components/sidebar/sections/RegistrationSection.tsx +254 -0
  85. package/src/explorer-component/components/sidebar/sections/SigstoreSection.tsx +37 -0
  86. package/src/explorer-component/components/sidebar/sections/StorageSection.tsx +73 -0
  87. package/src/explorer-component/components/sidebar/sections/custom/IrohCollectionsView.tsx +139 -0
  88. package/src/explorer-component/components/sidebar/sections/registration/VerificationIcon.tsx +108 -0
  89. package/src/explorer-component/components/sidebar/sections/registration/actor/ActorBadge.tsx +62 -0
  90. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AMDLogo.tsx +40 -0
  91. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AzureLogo.tsx +65 -0
  92. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/IntelLogo.tsx +24 -0
  93. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/NvidiaLogo.tsx +27 -0
  94. package/src/explorer-component/components/sidebar/sections/registration/verifierClient.ts +13 -0
  95. package/src/explorer-component/icons/AttributionIcon.svg +6 -0
  96. package/src/explorer-component/icons/BenchmarkIcon.svg +6 -0
  97. package/src/explorer-component/icons/BenchmarkResultIcon.svg +6 -0
  98. package/src/explorer-component/icons/CertificateIcon.svg +6 -0
  99. package/src/explorer-component/icons/CodeIcon.svg +6 -0
  100. package/src/explorer-component/icons/ComputationIcon.svg +6 -0
  101. package/src/explorer-component/icons/DataIcon.svg +6 -0
  102. package/src/explorer-component/icons/DatabaseIcon.svg +6 -0
  103. package/src/explorer-component/icons/DatasetIcon.svg +6 -0
  104. package/src/explorer-component/icons/DeltaLakeIcon.svg +7 -0
  105. package/src/explorer-component/icons/DocumentIcon.svg +6 -0
  106. package/src/explorer-component/icons/MediaIcon.svg +6 -0
  107. package/src/explorer-component/icons/ModelIcon.svg +6 -0
  108. package/src/explorer-component/icons/TeamIcon.svg +3 -0
  109. package/src/explorer-component/icons/TokenIcon.svg +6 -0
  110. package/src/explorer-component/icons/UnknownIcon.svg +6 -0
  111. package/src/explorer-component/icons/index.ts +35 -0
  112. package/src/explorer-component/index.ts +5 -0
  113. package/src/explorer-component/integrityTypes/AnchorTypes.ts +31 -0
  114. package/src/explorer-component/integrityTypes/StatementTypes.ts +333 -0
  115. package/src/explorer-component/integrityTypes/VCTypes.ts +85 -0
  116. package/src/explorer-component/integrityTypes/types.ts +3 -0
  117. package/src/explorer-component/utils/config.ts +163 -0
  118. package/src/explorer-component/utils/formatTimestamp.ts +5 -0
  119. package/src/explorer-component/utils/generateGraph.ts +271 -0
  120. package/src/explorer-component/utils/layoutGraph.ts +148 -0
  121. package/src/explorer-component/utils/resolveIrohBlobs.ts +117 -0
  122. package/src/explorer-component/utils/statementHelpers.ts +252 -0
  123. package/src/explorer-component/utils/stripPrefix.ts +4 -0
  124. package/src/hooks/use-mobile.tsx +19 -0
  125. package/src/index.ts +3 -0
  126. package/src/lib/resource-types.ts +31 -0
  127. package/src/lib/use-escape-key.tsx +16 -0
  128. package/src/lib/utils.ts +19 -0
  129. package/src/main.css +82 -0
  130. package/src/main.tsx +39 -0
  131. package/src/v-comp-viewer/components/container-did-details.tsx +53 -0
  132. package/src/v-comp-viewer/components/system-did-details.tsx +356 -0
  133. package/src/v-comp-viewer/useVComp.ts +196 -0
  134. package/src/v-comp-viewer/utils.tsx +824 -0
  135. package/src/vite-env.d.ts +1 -0
  136. package/tsconfig.json +33 -0
  137. package/tsconfig.node.json +10 -0
  138. package/vercel.json +13 -0
  139. package/vite.app.config.ts +48 -0
  140. 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 };