@appliedblockchain/silentdatarollup-core 1.0.9 → 1.0.10

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/index.js CHANGED
@@ -63,7 +63,8 @@ __export(index_exports, {
63
63
  getAuthEIP721Types: () => getAuthEIP721Types,
64
64
  getAuthHeaders: () => getAuthHeaders,
65
65
  isSignableContractCall: () => isSignableContractCall,
66
- prepareTypedDataPayload: () => prepareTypedDataPayload
66
+ prepareTypedDataPayload: () => prepareTypedDataPayload,
67
+ validateTdxAttestation: () => validateTdxAttestation
67
68
  });
68
69
  module.exports = __toCommonJS(index_exports);
69
70
 
@@ -74,6 +75,7 @@ var import_ethers4 = require("ethers");
74
75
  // src/constants.ts
75
76
  var import_ethers = require("ethers");
76
77
  var SIGN_RPC_METHODS = [
78
+ "sd_getTransactionsByAddress",
77
79
  "eth_estimateGas",
78
80
  "eth_getProof",
79
81
  "eth_getTransactionByHash",
@@ -111,6 +113,8 @@ var USER_OPERATION_EVENT_HASH = (0, import_ethers.keccak256)(
111
113
  var DEFAULT_DELEGATE_EXPIRES = 10 * 60 * 60;
112
114
  var DELEGATE_EXPIRATION_THRESHOLD_BUFFER = 5;
113
115
  var WHITELISTED_METHODS = [
116
+ "sd_getTransactionsByAddress",
117
+ "sd_getVersion",
114
118
  "eth_blockNumber",
115
119
  "eth_call",
116
120
  "eth_chainId",
@@ -796,6 +800,2543 @@ var SilentDataRollupContract = class extends import_ethers5.Contract {
796
800
  }
797
801
  }
798
802
  };
803
+
804
+ // src/audit/challenge.ts
805
+ var import_ethers7 = require("ethers");
806
+
807
+ // src/audit/quote/validateTdxQuote.ts
808
+ var x5095 = __toESM(require("@peculiar/x509"));
809
+
810
+ // src/audit/quote/quoteStructs.ts
811
+ var intelQuoteV4Version = 4;
812
+ var QuoteVersion = 4;
813
+ var AttestationKeyType = 2;
814
+ var rtmrsCount = 4;
815
+ var RtmrSize = 48;
816
+ var qeSvnSize = 2;
817
+ var pceSvnSize = 2;
818
+ var TeeTcbSvnSize = 16;
819
+ var QeVendorIDSize = 16;
820
+ var userDataSize = 20;
821
+ var MrSeamSize = 48;
822
+ var mrSignerSeamSize = 48;
823
+ var TeeTDX = 129;
824
+ var seamAttributesSize = 8;
825
+ var TdAttributesSize = 8;
826
+ var XfamSize = 8;
827
+ var MrTdSize = 48;
828
+ var MrConfigIDSize = 48;
829
+ var MrOwnerSize = 48;
830
+ var MrOwnerConfigSize = 48;
831
+ var cpuSvnSize = 16;
832
+ var attributesSize = 16;
833
+ var ReportDataSize = 64;
834
+ var headerVersionStart = 0;
835
+ var headerVersionEnd = 2;
836
+ var headerAttestationKeyTypeStart = headerVersionEnd;
837
+ var headerAttestationKeyTypeEnd = 4;
838
+ var headerTeeTypeStart = headerAttestationKeyTypeEnd;
839
+ var headerTeeTypeEnd = 8;
840
+ var headerPceSvnStart = headerTeeTypeEnd;
841
+ var headerPceSvnEnd = 10;
842
+ var headerQeSvnStart = headerPceSvnEnd;
843
+ var headerQeSvnEnd = 12;
844
+ var headerQeVendorIDStart = headerQeSvnEnd;
845
+ var headerQeVendorIDEnd = 28;
846
+ var headerUserDataStart = headerQeVendorIDEnd;
847
+ var tdTeeTcbSvnStart = 0;
848
+ var tdTeeTcbSvnEnd = 16;
849
+ var tdMrSeamStart = tdTeeTcbSvnEnd;
850
+ var tdMrSeamEnd = 64;
851
+ var tdMrSignerSeamStart = tdMrSeamEnd;
852
+ var tdMrSignerSeamEnd = 112;
853
+ var tdSeamAttributesStart = tdMrSignerSeamEnd;
854
+ var tdSeamAttributesEnd = 120;
855
+ var tdAttributesStart = tdSeamAttributesEnd;
856
+ var tdAttributesEnd = 128;
857
+ var tdXfamStart = tdAttributesEnd;
858
+ var tdXfamEnd = 136;
859
+ var tdMrTdStart = tdXfamEnd;
860
+ var tdMrTdEnd = 184;
861
+ var tdMrConfigIDStart = tdMrTdEnd;
862
+ var tdMrConfigIDEnd = 232;
863
+ var tdMrOwnerStart = tdMrConfigIDEnd;
864
+ var tdMrOwnerEnd = 280;
865
+ var tdMrOwnerConfigStart = tdMrOwnerEnd;
866
+ var tdMrOwnerConfigEnd = 328;
867
+ var tdRtmrsStart = tdMrOwnerConfigEnd;
868
+ var tdRtmrsEnd = 520;
869
+ var tdReportDataStart = tdRtmrsEnd;
870
+ var headerSize = 48;
871
+ var tdQuoteBodySize = 584;
872
+ var qeReportSize = 384;
873
+ var qeCPUSvnStart = 0;
874
+ var qeCPUSvnEnd = 16;
875
+ var qeMiscSelectStart = qeCPUSvnEnd;
876
+ var qeMiscSelectEnd = 20;
877
+ var qeReserved1Start = qeMiscSelectEnd;
878
+ var qeReserved1End = 48;
879
+ var qeAttributesStart = qeReserved1End;
880
+ var qeAttributesEnd = 64;
881
+ var qeMrEnclaveStart = qeAttributesEnd;
882
+ var qeMrEnclaveEnd = 96;
883
+ var qeReserved2Start = qeMrEnclaveEnd;
884
+ var qeReserved2End = 128;
885
+ var qeMrSignerStart = qeReserved2End;
886
+ var qeMrSignerEnd = 160;
887
+ var qeReserved3Start = qeMrSignerEnd;
888
+ var qeReserved3End = 256;
889
+ var qeIsvProdIDStart = qeReserved3End;
890
+ var qeIsvProdIDEnd = 258;
891
+ var qeIsvSvnStart = qeIsvProdIDEnd;
892
+ var qeIsvSvnEnd = 260;
893
+ var qeReserved4Start = qeIsvSvnEnd;
894
+ var qeReserved4End = 320;
895
+ var qeReportDataStart = qeReserved4End;
896
+ var qeReportDataEnd = 384;
897
+ var reserved1Size = 28;
898
+ var mrEnclaveSize = 32;
899
+ var reserved2Size = 32;
900
+ var mrSignerSize = 32;
901
+ var reserved3Size = 96;
902
+ var reserved4Size = 60;
903
+ var ErrQuoteV4AuthDataNil = new Error("QuoteV4 authData is nil");
904
+ var ErrQuoteV4Nil = new Error("QuoteV4 is nil");
905
+ var ErrHeaderNil = new Error("header is nil");
906
+ var ErrAttestationKeyType = new Error(
907
+ "attestation key type not supported"
908
+ );
909
+ var ErrTeeType = new Error("TEE type is not TDX");
910
+ var ErrTDQuoteBodyNil = new Error("TD quote body is nil");
911
+ var ErrQeReportNil = new Error("QE Report is nil");
912
+ function checkQuoteV4(q) {
913
+ if (!q) {
914
+ throw ErrQuoteV4Nil;
915
+ }
916
+ checkHeaderSizes(q.header);
917
+ checkTdQuoteBodySizes(q.tdQuoteBody);
918
+ if (q.signedData.signature.length !== 64)
919
+ throw new Error("signature wrong size");
920
+ if (q.signedData.ecdsaAttestationKey.length !== 64)
921
+ throw new Error("attestationKey wrong size");
922
+ }
923
+ function checkHeaderSizes(h) {
924
+ if (h.qeSvn.length !== qeSvnSize) throw new Error("qeSvn wrong size");
925
+ if (h.pceSvn.length !== pceSvnSize) throw new Error("pceSvn wrong size");
926
+ if (h.qeVendorId.length !== QeVendorIDSize)
927
+ throw new Error("qeVendorId wrong size");
928
+ if (h.userData.length !== userDataSize) throw new Error("userData wrong size");
929
+ }
930
+ function checkTdQuoteBodySizes(b) {
931
+ if (b.mrSeam.length !== MrSeamSize) throw new Error("mrSeam wrong size");
932
+ if (b.tdAttributes.length !== TdAttributesSize)
933
+ throw new Error("tdAttributes wrong size");
934
+ if (b.xfam.length !== XfamSize) throw new Error("xfam wrong size");
935
+ if (b.mrTd.length !== MrTdSize) throw new Error("mrTd wrong size");
936
+ if (b.mrConfigId.length !== MrConfigIDSize)
937
+ throw new Error("mrConfigId wrong size");
938
+ if (b.mrOwner.length !== MrOwnerSize) throw new Error("mrOwner wrong size");
939
+ if (b.mrOwnerConfig.length !== MrOwnerConfigSize)
940
+ throw new Error("mrOwnerConfig wrong size");
941
+ if (b.reportData.length !== ReportDataSize)
942
+ throw new Error("reportData wrong size");
943
+ if (b.rtmrs.length !== rtmrsCount) throw new Error("rtmrs count != 4");
944
+ for (const r of b.rtmrs)
945
+ if (r.length !== RtmrSize) throw new Error("rtmr wrong size");
946
+ }
947
+ function HeaderToAbiBytes(header) {
948
+ if (!header) {
949
+ throw ErrHeaderNil;
950
+ }
951
+ checkHeader(header);
952
+ const data = new Uint8Array(headerSize);
953
+ const view = new DataView(data.buffer);
954
+ view.setUint16(headerVersionStart, header.version, true);
955
+ view.setUint16(headerAttestationKeyTypeStart, header.attestationKeyType, true);
956
+ view.setUint32(headerTeeTypeStart, header.teeType, true);
957
+ data.set(header.pceSvn, headerPceSvnStart);
958
+ data.set(header.qeSvn, headerQeSvnStart);
959
+ data.set(header.qeVendorId, headerQeVendorIDStart);
960
+ data.set(header.userData, headerUserDataStart);
961
+ return data;
962
+ }
963
+ function checkHeader(header) {
964
+ if (!header) {
965
+ throw ErrHeaderNil;
966
+ }
967
+ if (header.version >= 1 << 16) {
968
+ throw new Error(
969
+ `version field size must fit in 2 bytes, got ${header.version}`
970
+ );
971
+ }
972
+ if (header.version !== QuoteVersion) {
973
+ throw new Error(`version ${header.version} not supported`);
974
+ }
975
+ if (header.attestationKeyType >= 1 << 16) {
976
+ throw new Error(
977
+ `attestation key type field size must fit in 2 bytes, got ${header.attestationKeyType}`
978
+ );
979
+ }
980
+ if (header.attestationKeyType !== AttestationKeyType) {
981
+ throw ErrAttestationKeyType;
982
+ }
983
+ if (header.teeType !== TeeTDX) {
984
+ throw ErrTeeType;
985
+ }
986
+ if (header.qeSvn.length !== qeSvnSize) {
987
+ throw new Error(
988
+ `qeSvn size is ${header.qeSvn.length} bytes. Expected ${qeSvnSize} bytes`
989
+ );
990
+ }
991
+ if (header.pceSvn.length !== pceSvnSize) {
992
+ throw new Error(
993
+ `pceSvn size is ${header.pceSvn.length} bytes. Expected ${pceSvnSize} bytes`
994
+ );
995
+ }
996
+ if (header.qeVendorId.length !== QeVendorIDSize) {
997
+ throw new Error(
998
+ `qeVendorId size is ${header.qeVendorId.length} bytes. Expected ${QeVendorIDSize} bytes`
999
+ );
1000
+ }
1001
+ if (header.userData.length !== userDataSize) {
1002
+ throw new Error(
1003
+ `user data size is ${header.userData.length} bytes. Expected ${userDataSize} bytes`
1004
+ );
1005
+ }
1006
+ }
1007
+ function TdQuoteBodyToAbiBytes(tdQuoteBody) {
1008
+ if (!tdQuoteBody) {
1009
+ throw ErrTDQuoteBodyNil;
1010
+ }
1011
+ checkTDQuoteBody(tdQuoteBody);
1012
+ const data = new Uint8Array(tdQuoteBodySize);
1013
+ data.set(tdQuoteBody.teeTcbSvn, tdTeeTcbSvnStart);
1014
+ data.set(tdQuoteBody.mrSeam, tdMrSeamStart);
1015
+ data.set(tdQuoteBody.mrSignerSeam, tdMrSignerSeamStart);
1016
+ data.set(tdQuoteBody.seamAttributes, tdSeamAttributesStart);
1017
+ data.set(tdQuoteBody.tdAttributes, tdAttributesStart);
1018
+ data.set(tdQuoteBody.xfam, tdXfamStart);
1019
+ data.set(tdQuoteBody.mrTd, tdMrTdStart);
1020
+ data.set(tdQuoteBody.mrConfigId, tdMrConfigIDStart);
1021
+ data.set(tdQuoteBody.mrOwner, tdMrOwnerStart);
1022
+ data.set(tdQuoteBody.mrOwnerConfig, tdMrOwnerConfigStart);
1023
+ let offset = tdRtmrsStart;
1024
+ for (let i = 0; i < rtmrsCount; i++) {
1025
+ const rtmr = tdQuoteBody.rtmrs[i];
1026
+ if (!rtmr || rtmr.length !== RtmrSize) {
1027
+ throw new Error(`RTMR[${i}] is missing or invalid`);
1028
+ }
1029
+ data.set(rtmr, offset);
1030
+ offset += RtmrSize;
1031
+ }
1032
+ data.set(tdQuoteBody.reportData, tdReportDataStart);
1033
+ return data;
1034
+ }
1035
+ function enclaveReportToAbiBytes(report) {
1036
+ if (!report) {
1037
+ throw ErrQeReportNil;
1038
+ }
1039
+ checkQeReport(report);
1040
+ const data = new Uint8Array(qeReportSize);
1041
+ data.set(report.cpuSvn, qeCPUSvnStart);
1042
+ new DataView(data.buffer).setUint32(
1043
+ qeMiscSelectStart,
1044
+ report.miscSelect,
1045
+ true
1046
+ );
1047
+ data.set(report.reserved1, qeReserved1Start);
1048
+ data.set(report.attributes, qeAttributesStart);
1049
+ data.set(report.mrEnclave, qeMrEnclaveStart);
1050
+ data.set(report.reserved2, qeReserved2Start);
1051
+ data.set(report.mrSigner, qeMrSignerStart);
1052
+ data.set(report.reserved3, qeReserved3Start);
1053
+ new DataView(data.buffer).setUint16(qeIsvProdIDStart, report.isvProdId, true);
1054
+ new DataView(data.buffer).setUint16(qeIsvSvnStart, report.isvSvn, true);
1055
+ data.set(report.reserved4, qeReserved4Start);
1056
+ data.set(report.reportData, qeReportDataStart);
1057
+ return data;
1058
+ }
1059
+ function checkQeReport(report) {
1060
+ if (!report) {
1061
+ throw ErrQeReportNil;
1062
+ }
1063
+ if (report.cpuSvn.length !== cpuSvnSize) {
1064
+ throw new Error(
1065
+ `cpuSvn size is ${report.cpuSvn.length} bytes. Expected ${cpuSvnSize} bytes`
1066
+ );
1067
+ }
1068
+ if (report.reserved1.length !== reserved1Size) {
1069
+ throw new Error(
1070
+ `reserved1 size is ${report.reserved1.length} bytes. Expected ${reserved1Size} bytes`
1071
+ );
1072
+ }
1073
+ if (report.attributes.length !== attributesSize) {
1074
+ throw new Error(
1075
+ `attributes size is ${report.attributes.length} bytes. Expected ${attributesSize} bytes`
1076
+ );
1077
+ }
1078
+ if (report.mrEnclave.length !== mrEnclaveSize) {
1079
+ throw new Error(
1080
+ `mrEnclave size is ${report.mrEnclave.length} bytes. Expected ${mrEnclaveSize} bytes`
1081
+ );
1082
+ }
1083
+ if (report.reserved2.length !== reserved2Size) {
1084
+ throw new Error(
1085
+ `reserved2 size is ${report.reserved2.length} bytes. Expected ${reserved2Size} bytes`
1086
+ );
1087
+ }
1088
+ if (report.mrSigner.length !== mrSignerSize) {
1089
+ throw new Error(
1090
+ `mrSigner size is ${report.mrSigner.length} bytes. Expected ${mrSignerSize} bytes`
1091
+ );
1092
+ }
1093
+ if (report.reserved3.length !== reserved3Size) {
1094
+ throw new Error(
1095
+ `reserved3 size is ${report.reserved3.length} bytes. Expected ${reserved3Size} bytes`
1096
+ );
1097
+ }
1098
+ if (report.isvProdId >= 1 << 16) {
1099
+ throw new Error(`isvProdId must fit in 2 bytes, got ${report.isvProdId}`);
1100
+ }
1101
+ if (report.isvSvn >= 1 << 16) {
1102
+ throw new Error(`isvSvn must fit in 2 bytes, got ${report.isvSvn}`);
1103
+ }
1104
+ if (report.reserved4.length !== reserved4Size) {
1105
+ throw new Error(
1106
+ `reserved4 size is ${report.reserved4.length} bytes. Expected ${reserved4Size} bytes`
1107
+ );
1108
+ }
1109
+ if (report.reportData.length !== ReportDataSize) {
1110
+ throw new Error(
1111
+ `reportData size is ${report.reportData.length} bytes. Expected ${ReportDataSize} bytes`
1112
+ );
1113
+ }
1114
+ }
1115
+ function checkTDQuoteBody(tdQuoteBody) {
1116
+ if (!tdQuoteBody) {
1117
+ throw ErrTDQuoteBodyNil;
1118
+ }
1119
+ if (tdQuoteBody.teeTcbSvn.length !== TeeTcbSvnSize) {
1120
+ throw new Error(
1121
+ `teeTcbSvn size is ${tdQuoteBody.teeTcbSvn.length} bytes. Expected ${TeeTcbSvnSize} bytes`
1122
+ );
1123
+ }
1124
+ if (tdQuoteBody.mrSeam.length !== MrSeamSize) {
1125
+ throw new Error(
1126
+ `mrSeam size is ${tdQuoteBody.mrSeam.length} bytes. Expected ${MrSeamSize} bytes`
1127
+ );
1128
+ }
1129
+ if (tdQuoteBody.mrSignerSeam.length !== mrSignerSeamSize) {
1130
+ throw new Error(
1131
+ `mrSignerSeam size is ${tdQuoteBody.mrSignerSeam.length} bytes. Expected ${mrSignerSeamSize} bytes`
1132
+ );
1133
+ }
1134
+ if (tdQuoteBody.seamAttributes.length !== seamAttributesSize) {
1135
+ throw new Error(
1136
+ `seamAttributes size is ${tdQuoteBody.seamAttributes.length} bytes. Expected ${seamAttributesSize} bytes`
1137
+ );
1138
+ }
1139
+ if (tdQuoteBody.tdAttributes.length !== TdAttributesSize) {
1140
+ throw new Error(
1141
+ `tdAttributes size is ${tdQuoteBody.tdAttributes.length} bytes. Expected ${TdAttributesSize} bytes`
1142
+ );
1143
+ }
1144
+ if (tdQuoteBody.xfam.length !== XfamSize) {
1145
+ throw new Error(
1146
+ `xfam size is ${tdQuoteBody.xfam.length} bytes. Expected ${XfamSize} bytes`
1147
+ );
1148
+ }
1149
+ if (tdQuoteBody.mrTd.length !== MrTdSize) {
1150
+ throw new Error(
1151
+ `mrTd size is ${tdQuoteBody.mrTd.length} bytes. Expected ${MrTdSize} bytes`
1152
+ );
1153
+ }
1154
+ if (tdQuoteBody.mrConfigId.length !== MrConfigIDSize) {
1155
+ throw new Error(
1156
+ `mrConfigId size is ${tdQuoteBody.mrConfigId.length} bytes. Expected ${MrConfigIDSize} bytes`
1157
+ );
1158
+ }
1159
+ if (tdQuoteBody.mrOwner.length !== MrOwnerSize) {
1160
+ throw new Error(
1161
+ `mrOwner size is ${tdQuoteBody.mrOwner.length} bytes. Expected ${MrOwnerSize} bytes`
1162
+ );
1163
+ }
1164
+ if (tdQuoteBody.mrOwnerConfig.length !== MrOwnerConfigSize) {
1165
+ throw new Error(
1166
+ `mrOwnerConfig size is ${tdQuoteBody.mrOwnerConfig.length} bytes. Expected ${MrOwnerConfigSize} bytes`
1167
+ );
1168
+ }
1169
+ if (tdQuoteBody.rtmrs.length !== rtmrsCount) {
1170
+ throw new Error(
1171
+ `rtmrs count is ${tdQuoteBody.rtmrs.length}. Expected ${rtmrsCount}`
1172
+ );
1173
+ }
1174
+ for (let i = 0; i < rtmrsCount; i++) {
1175
+ if (tdQuoteBody.rtmrs[i].length !== RtmrSize) {
1176
+ throw new Error(
1177
+ `rtmr${i} size is ${tdQuoteBody.rtmrs[i].length} bytes. Expected ${RtmrSize} bytes`
1178
+ );
1179
+ }
1180
+ }
1181
+ }
1182
+ function enclaveReportToProto(b) {
1183
+ const data = b.slice();
1184
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1185
+ const enclaveReport = {
1186
+ cpuSvn: data.slice(qeCPUSvnStart, qeCPUSvnEnd),
1187
+ miscSelect: view.getUint32(qeMiscSelectStart, true),
1188
+ reserved1: data.slice(qeReserved1Start, qeReserved1End),
1189
+ attributes: data.slice(qeAttributesStart, qeAttributesEnd),
1190
+ mrEnclave: data.slice(qeMrEnclaveStart, qeMrEnclaveEnd),
1191
+ reserved2: data.slice(qeReserved2Start, qeReserved2End),
1192
+ mrSigner: data.slice(qeMrSignerStart, qeMrSignerEnd),
1193
+ reserved3: data.slice(qeReserved3Start, qeReserved3End),
1194
+ isvProdId: view.getUint16(qeIsvProdIDStart, true),
1195
+ isvSvn: view.getUint16(qeIsvSvnStart, true),
1196
+ reserved4: data.slice(qeReserved4Start, qeReserved4End),
1197
+ reportData: data.slice(qeReportDataStart, qeReportDataEnd)
1198
+ };
1199
+ checkQeReport(enclaveReport);
1200
+ return enclaveReport;
1201
+ }
1202
+
1203
+ // src/audit/quote/base64-utils.ts
1204
+ var asn1js = __toESM(require("asn1js"));
1205
+ var x509 = __toESM(require("@peculiar/x509"));
1206
+ function encodeBase64(data) {
1207
+ let binary = "";
1208
+ for (let i = 0; i < data.byteLength; i++) {
1209
+ binary += String.fromCharCode(data[i]);
1210
+ }
1211
+ return globalThis.btoa(binary);
1212
+ }
1213
+ function leU16(buf, off = 0) {
1214
+ return buf[off] | buf[off + 1] << 8;
1215
+ }
1216
+ function leU32(buf, off = 0) {
1217
+ return (buf[off] | buf[off + 1] << 8 | buf[off + 2] << 16 | buf[off + 3] << 24) >>> 0;
1218
+ }
1219
+ function toBigIntLE(u) {
1220
+ let r = 0n;
1221
+ for (let i = u.length - 1; i >= 0; --i) r = r << 8n | BigInt(u[i]);
1222
+ return r;
1223
+ }
1224
+ function decodeU16(node, name) {
1225
+ if (!(node instanceof asn1js.Integer))
1226
+ throw new Error(`${name} must be INTEGER`);
1227
+ const v = node.valueBlock.valueDec;
1228
+ if (v < 0 || v > 65535) throw new Error(`${name} out of range`);
1229
+ return v;
1230
+ }
1231
+ function decodeU8(node, name) {
1232
+ if (!(node instanceof asn1js.Integer))
1233
+ throw new Error(`${name} must be INTEGER`);
1234
+ const v = node.valueBlock.valueDec;
1235
+ if (v < 0 || v > 255) throw new Error(`${name} out of range`);
1236
+ return v;
1237
+ }
1238
+ async function verifyEcdsaSignature(pubKey, msg, sig) {
1239
+ const jwk = {
1240
+ kty: "EC",
1241
+ crv: "P-256",
1242
+ x: b64url(pubKey.slice(0, 32)),
1243
+ y: b64url(pubKey.slice(32)),
1244
+ ext: true
1245
+ };
1246
+ const key = await x509.cryptoProvider.get().subtle.importKey(
1247
+ "jwk",
1248
+ jwk,
1249
+ { name: "ECDSA", namedCurve: "P-256" },
1250
+ false,
1251
+ ["verify"]
1252
+ );
1253
+ return x509.cryptoProvider.get().subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, sig, msg);
1254
+ }
1255
+ function b64url(data) {
1256
+ return encodeBase64(data).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1257
+ }
1258
+
1259
+ // src/audit/quote/validate.ts
1260
+ var xfamFixed1 = 0x00000003n;
1261
+ var xfamFixed0 = 0x0006dbe7n;
1262
+ var tdAttributesFixed1 = 0x0n;
1263
+ var tdxAttributesSeptVeDisSupport = 1n << 28n;
1264
+ var tdxAttributesPksSupport = 1n << 30n;
1265
+ var tdxAttributesPerfmonSupport = 1n << 63n;
1266
+ var tdAttributesFixed0 = 0x1n | tdxAttributesSeptVeDisSupport | tdxAttributesPksSupport | tdxAttributesPerfmonSupport;
1267
+ var must = {
1268
+ equalBytes(a, b, field) {
1269
+ if (a.length !== b.length) throw new Error(`${field} length mismatch`);
1270
+ for (let i = 0; i < a.length; ++i)
1271
+ if (a[i] !== b[i]) {
1272
+ throw new Error(`${field} mismatch - byte ${i}`);
1273
+ }
1274
+ }
1275
+ };
1276
+ function validateTdxQuoteV4(q, opts) {
1277
+ checkQuoteV4(q);
1278
+ exactByteMatch(q, opts);
1279
+ minVersionCheck(q, opts);
1280
+ validateXfam(q.tdQuoteBody.xfam, xfamFixed1, xfamFixed0);
1281
+ validateTdAttributes(
1282
+ q.tdQuoteBody.tdAttributes,
1283
+ tdAttributesFixed1,
1284
+ tdAttributesFixed0
1285
+ );
1286
+ }
1287
+ function exactByteMatch(q, opts) {
1288
+ const b = q.tdQuoteBody;
1289
+ const h = q.header;
1290
+ const bo = opts.tdQuoteBodyOptions;
1291
+ const ho = opts.headerOptions;
1292
+ const cmp = (name, given, expected, size) => {
1293
+ if (!expected) return;
1294
+ if (size && expected.length !== size)
1295
+ throw new Error(`${name} option must be ${size} bytes`);
1296
+ must.equalBytes(given, expected, name);
1297
+ };
1298
+ cmp("MR_SEAM", b.mrSeam, bo.mrSeam, MrSeamSize);
1299
+ cmp("TD_ATTRIBUTES", b.tdAttributes, bo.tdAttributes, TdAttributesSize);
1300
+ cmp("XFAM", b.xfam, bo.xfam, XfamSize);
1301
+ cmp("MR_TD", b.mrTd, bo.mrTd, MrTdSize);
1302
+ cmp("MR_CONFIG_ID", b.mrConfigId, bo.mrConfigID, MrConfigIDSize);
1303
+ cmp("MR_OWNER", b.mrOwner, bo.mrOwner, MrOwnerSize);
1304
+ cmp(
1305
+ "MR_OWNER_CONFIG",
1306
+ b.mrOwnerConfig,
1307
+ bo.mrOwnerConfig,
1308
+ MrOwnerConfigSize
1309
+ );
1310
+ if (bo.rtmrs) {
1311
+ if (bo.rtmrs.length !== rtmrsCount)
1312
+ throw new Error("RTMR option len != 4");
1313
+ bo.rtmrs.forEach(
1314
+ (exp, i) => cmp(`RTMR[${i}]`, b.rtmrs[i], exp, RtmrSize)
1315
+ );
1316
+ }
1317
+ cmp("REPORT_DATA", b.reportData, bo.reportData, ReportDataSize);
1318
+ cmp("QE_VENDOR_ID", h.qeVendorId, ho.qeVendorID, QeVendorIDSize);
1319
+ }
1320
+ function isSvnHigherOrEqual(quoteSvn, optionSvn) {
1321
+ if (!optionSvn) {
1322
+ return true;
1323
+ }
1324
+ for (let i = 0; i < quoteSvn.length; i++) {
1325
+ if (quoteSvn[i] < optionSvn[i]) {
1326
+ return false;
1327
+ }
1328
+ }
1329
+ return true;
1330
+ }
1331
+ function minVersionCheck(quote, opts) {
1332
+ if (!isSvnHigherOrEqual(
1333
+ quote.tdQuoteBody.teeTcbSvn,
1334
+ opts.tdQuoteBodyOptions?.minimumTeeTcbSvn
1335
+ )) {
1336
+ throw new Error(
1337
+ `TEE TCB security-version number ${toBigIntLE(
1338
+ quote.tdQuoteBody.teeTcbSvn
1339
+ )} is less than the required minimum ${toBigIntLE(
1340
+ opts.tdQuoteBodyOptions.minimumTeeTcbSvn
1341
+ )}`
1342
+ );
1343
+ }
1344
+ const qeSvn = leU16(quote.header.qeSvn);
1345
+ const pceSvn = leU16(quote.header.pceSvn);
1346
+ if (qeSvn < opts.headerOptions.minimumQeSvn) {
1347
+ throw new Error(
1348
+ `QE security-version number ${qeSvn} is less than the required minimum ${opts.headerOptions.minimumQeSvn}`
1349
+ );
1350
+ }
1351
+ if (pceSvn < opts.headerOptions.minimumPceSvn) {
1352
+ throw new Error(
1353
+ `PCE security-version number ${pceSvn} is less than the required minimum ${opts.headerOptions.minimumPceSvn}`
1354
+ );
1355
+ }
1356
+ return;
1357
+ }
1358
+ function validateXfam(value, fixed1, fixed0) {
1359
+ if (value.length === 0) return;
1360
+ if (value.length !== XfamSize) {
1361
+ throw new Error("xfam size is invalid");
1362
+ }
1363
+ const xfam = new DataView(
1364
+ value.buffer,
1365
+ value.byteOffset,
1366
+ value.byteLength
1367
+ ).getBigUint64(0, true);
1368
+ if ((xfam & fixed1) !== fixed1) {
1369
+ throw new Error(
1370
+ `unauthorized xfam 0x${xfam.toString(16)} as xfamFixed1 0x${fixed1.toString(16)} bits are unset`
1371
+ );
1372
+ }
1373
+ if ((xfam & ~fixed0) !== BigInt(0)) {
1374
+ throw new Error(
1375
+ `unauthorized xfam 0x${xfam.toString(16)} as xfamFixed0 0x${fixed0.toString(16)} bits are set`
1376
+ );
1377
+ }
1378
+ }
1379
+ function validateTdAttributes(value, fixed1, fixed0) {
1380
+ if (value.length === 0) return;
1381
+ if (value.length !== TdAttributesSize) {
1382
+ throw new Error("tdAttributes size is invalid");
1383
+ }
1384
+ const tdAttributes = new DataView(
1385
+ value.buffer,
1386
+ value.byteOffset,
1387
+ value.byteLength
1388
+ ).getBigUint64(0, true);
1389
+ if ((tdAttributes & fixed1) !== fixed1) {
1390
+ throw new Error(
1391
+ `unauthorized tdAttributes 0x${tdAttributes.toString(16)} as tdAttributesFixed1 0x${fixed1.toString(16)} bits are unset`
1392
+ );
1393
+ }
1394
+ if ((tdAttributes & ~fixed0) !== 0n) {
1395
+ throw new Error(
1396
+ `unauthorized tdAttributes 0x${tdAttributes.toString(16)} as tdAttributesFixed0 0x${fixed0.toString(16)} bits are set`
1397
+ );
1398
+ }
1399
+ }
1400
+
1401
+ // src/audit/quote/verify.ts
1402
+ var x5094 = __toESM(require("@peculiar/x509"));
1403
+
1404
+ // src/audit/quote/pck.ts
1405
+ var asn1js3 = __toESM(require("asn1js"));
1406
+
1407
+ // src/audit/quote/tcb.ts
1408
+ var asn1js2 = __toESM(require("asn1js"));
1409
+ var x5092 = __toESM(require("@peculiar/x509"));
1410
+ var tcbComponentSize = 16;
1411
+ var tcbSigningPhrase = "Intel SGX TCB Signing";
1412
+ var tcbInfoID = "TDX";
1413
+ var qeIdentityID = "TD_QE";
1414
+ var qeIdentityVersion = 2;
1415
+ var tcbInfoTdxModuleIDPrefix = "TDX_";
1416
+ var rootCertPhrase = "Intel SGX Root CA";
1417
+ var ErrRootCertNil = new Error("root certificate is empty");
1418
+ var ErrCollateralNil = new Error(
1419
+ "collateral received is an empty structure"
1420
+ );
1421
+ var ErrTcbInfoTcbLevelsMissing = new Error(
1422
+ "tcbInfo contains empty TcbLevels"
1423
+ );
1424
+ var ErrQeIdentityTcbLevelsMissing = new Error(
1425
+ "QeIdentity contains empty TcbLevels"
1426
+ );
1427
+ var ErrCrlEmpty = new Error("CRL is empty");
1428
+ var ErrTrustedCertEmpty = new Error("trusted certificate is empty");
1429
+ var ErrTcbStatus = new Error(
1430
+ "unable to find latest status of TCB, it is now OutOfDate"
1431
+ );
1432
+ var tcbInfoVersion = 3;
1433
+ var TcbComponentStatusUpToDate = "UpToDate";
1434
+ var tcbExtensionSize = 18;
1435
+ function sgxTcbComponentOid(component) {
1436
+ return [...sgxTcbComponentOidPrefix, component].join(".");
1437
+ }
1438
+ function extractTcbExtension(components, atcb) {
1439
+ const tcbComponents = new Uint8Array(tcbComponentSize);
1440
+ for (const ext of components) {
1441
+ if (!(ext instanceof asn1js2.Sequence))
1442
+ throw new Error("TCB component is not a SEQUENCE");
1443
+ if (ext.valueBlock.value.length !== 2)
1444
+ throw new Error("AttributeTypeAndValue must have 2 elements");
1445
+ const [oidBlock, rawVal] = ext.valueBlock.value;
1446
+ if (!(oidBlock instanceof asn1js2.ObjectIdentifier))
1447
+ throw new Error("Missing OID in TCB component");
1448
+ const value = unwrapSingleSet(rawVal);
1449
+ const oid = oidBlock.valueBlock.toString();
1450
+ for (let i = 0; i < tcbComponentSize; i++) {
1451
+ if (oid === sgxTcbComponentOid(i + 1)) {
1452
+ tcbComponents[i] = decodeU8(value, `sgxTcbComponent${i + 1}`);
1453
+ break;
1454
+ }
1455
+ }
1456
+ if (oid === OidPCESvn) {
1457
+ atcb.pceSvn = decodeU16(value, "PCESvn");
1458
+ }
1459
+ if (oid === OidCPUSvn) {
1460
+ if (!(value instanceof asn1js2.OctetString))
1461
+ throw new Error("CPUSvn must be an OCTET STRING");
1462
+ const buf = new Uint8Array(value.valueBlock.valueHex);
1463
+ if (buf.length !== cpuSvnSize)
1464
+ throw new Error(
1465
+ `CPUSvn length is ${buf.length}, expected ${cpuSvnSize}`
1466
+ );
1467
+ atcb.cpuSvn = buf;
1468
+ }
1469
+ }
1470
+ atcb.cpuSvnComponents = tcbComponents;
1471
+ }
1472
+ async function verifyTCBinfo(options) {
1473
+ const collateral = options.collateral;
1474
+ if (!collateral) throw ErrCollateralNil;
1475
+ const tcbInfo = collateral.tdxTcbInfo.tcbInfo;
1476
+ const signature = collateral.tdxTcbInfo.signature;
1477
+ if (tcbInfo.id !== tcbInfoID) {
1478
+ throw new Error(
1479
+ `tcbInfo ID "${tcbInfo.id}" does not match with expected ID "${tcbInfoID}"`
1480
+ );
1481
+ }
1482
+ if (tcbInfo.version !== tcbInfoVersion) {
1483
+ throw new Error(
1484
+ `tcbInfo version ${tcbInfo.version} does not match with expected version ${tcbInfoVersion}`
1485
+ );
1486
+ }
1487
+ if (!Array.isArray(tcbInfo.tcbLevels) || tcbInfo.tcbLevels.length === 0) {
1488
+ throw ErrTcbInfoTcbLevelsMissing;
1489
+ }
1490
+ await verifyResponse(
1491
+ tcbSigningPhrase,
1492
+ collateral.tcbInfoIssuerRootCertificate,
1493
+ collateral.tcbInfoIssuerIntermediateCertificate,
1494
+ collateral.tcbInfoBody,
1495
+ signature,
1496
+ collateral.rootCaCrl,
1497
+ options
1498
+ );
1499
+ }
1500
+ async function verifyQeIdentity(options) {
1501
+ const collateral = options.collateral;
1502
+ if (!collateral) throw ErrCollateralNil;
1503
+ const qeIdentity = collateral.qeIdentity.enclaveIdentity;
1504
+ const signature = collateral.qeIdentity.signature;
1505
+ if (qeIdentity.id !== qeIdentityID) {
1506
+ throw new Error(
1507
+ `QeIdentity ID "${qeIdentity.id}" does not match with expected ID "${qeIdentityID}"`
1508
+ );
1509
+ }
1510
+ if (qeIdentity.version !== qeIdentityVersion) {
1511
+ throw new Error(
1512
+ `QeIdentity version ${qeIdentity.version} does not match with expected version ${qeIdentityVersion}`
1513
+ );
1514
+ }
1515
+ if (!Array.isArray(qeIdentity.tcbLevels) || qeIdentity.tcbLevels.length === 0) {
1516
+ throw ErrQeIdentityTcbLevelsMissing;
1517
+ }
1518
+ await verifyResponse(
1519
+ tcbSigningPhrase,
1520
+ collateral.qeIdentityIssuerRootCertificate,
1521
+ collateral.qeIdentityIssuerIntermediateCertificate,
1522
+ collateral.enclaveIdentityBody,
1523
+ signature,
1524
+ collateral.rootCaCrl,
1525
+ options
1526
+ );
1527
+ }
1528
+ async function verifyResponse(signingPhrase, rootCertificate, signingCertificate, rawBody, rawSignature, crl, options) {
1529
+ await validateCertificate(rootCertificate, rootCertificate, "rootCertPhrase");
1530
+ await validateCertificate(signingCertificate, rootCertificate, signingPhrase);
1531
+ const signature = Uint8Array.from(Buffer.from(rawSignature, "hex"));
1532
+ const publicKey = await x5092.cryptoProvider.get().subtle.importKey(
1533
+ "spki",
1534
+ signingCertificate.publicKey.rawData,
1535
+ { name: "ECDSA", namedCurve: "P-256" },
1536
+ false,
1537
+ ["verify"]
1538
+ );
1539
+ const isValid = await x5092.cryptoProvider.get().subtle.verify(
1540
+ { name: "ECDSA", hash: "SHA-256" },
1541
+ publicKey,
1542
+ signature,
1543
+ rawBody
1544
+ );
1545
+ if (!isValid) {
1546
+ throw new Error(
1547
+ "Could not verify response body signature using the signing certificate"
1548
+ );
1549
+ }
1550
+ if (options.checkRevocations) {
1551
+ if (options.getCollateral) {
1552
+ if (!crl) {
1553
+ throw new Error("Missing CRL for revocation check");
1554
+ }
1555
+ await validateCRL(crl, rootCertificate);
1556
+ for (const entry of crl.entries) {
1557
+ if (entry.serialNumber === signingCertificate.serialNumber) {
1558
+ throw new Error(
1559
+ `Signing certificate was revoked at ${entry.revocationDate}`
1560
+ );
1561
+ }
1562
+ }
1563
+ } else {
1564
+ throw new Error("Revocation check is enabled, but GetCollateral is false");
1565
+ }
1566
+ }
1567
+ }
1568
+ async function validateCertificate(cert, parent, expectedPhrase) {
1569
+ if (!cert) throw new Error("certificate is nil");
1570
+ if (!parent) throw new Error("parent certificate is nil");
1571
+ const cnList = cert.subjectName.getField("CN");
1572
+ const subjectCN = cnList.length > 0 ? cnList[0] : null;
1573
+ if (subjectCN !== expectedPhrase) {
1574
+ throw new Error(
1575
+ `"${subjectCN}" is not expected in certificate's subject name. Expected "${expectedPhrase}"`
1576
+ );
1577
+ }
1578
+ const issuerName = cert.issuerName.toString();
1579
+ const parentSubject = parent.subjectName.toString();
1580
+ if (issuerName !== parentSubject) {
1581
+ throw new Error(
1582
+ `Certificate issuer name ("${issuerName}") does not match parent certificate subject name ("${parentSubject}")`
1583
+ );
1584
+ }
1585
+ const verified = await cert.verify({ publicKey: parent });
1586
+ if (!verified) {
1587
+ throw new Error(
1588
+ "Certificate signature verification using parent certificate failed"
1589
+ );
1590
+ }
1591
+ }
1592
+ async function validateCRL(crl, trustedCertificate) {
1593
+ if (!crl) {
1594
+ throw ErrCrlEmpty;
1595
+ }
1596
+ if (!trustedCertificate) {
1597
+ throw ErrTrustedCertEmpty;
1598
+ }
1599
+ if (crl.issuer.toString() !== trustedCertificate.subject.toString()) {
1600
+ throw new Error(
1601
+ `CRL issuer's name "${crl.issuer.toString()}" does not match expected name "${trustedCertificate.subject.toString()}"`
1602
+ );
1603
+ }
1604
+ try {
1605
+ await crl.verify(trustedCertificate);
1606
+ } catch (err) {
1607
+ throw new Error(
1608
+ `CRL signature verification failed using trusted certificate: ${err}`
1609
+ );
1610
+ }
1611
+ }
1612
+ async function verifyTdQuoteBody(tdQuoteBody, options) {
1613
+ const { tcbInfo, pckCertExtensions } = options;
1614
+ if (pckCertExtensions.fmspc !== tcbInfo.fmspc) {
1615
+ throw new Error(
1616
+ `FMSPC mismatch: PCK Certificate(${pckCertExtensions.fmspc}) vs Intel PCS(${tcbInfo.fmspc})`
1617
+ );
1618
+ }
1619
+ if (pckCertExtensions.pceid !== tcbInfo.pceID) {
1620
+ throw new Error(
1621
+ `PCEID mismatch: PCK Certificate(${pckCertExtensions.pceid}) vs Intel PCS(${tcbInfo.pceID})`
1622
+ );
1623
+ }
1624
+ const reportedMrSigner = tdQuoteBody.mrSignerSeam;
1625
+ const expectedMrSigner = tcbInfo.tdxModule.mrsigner.bytes;
1626
+ if (!equalBytes(reportedMrSigner, expectedMrSigner)) {
1627
+ throw new Error(
1628
+ `MRSIGNERSEAM mismatch: quote(${toHex(reportedMrSigner)}) vs PCS(${toHex(expectedMrSigner)})`
1629
+ );
1630
+ }
1631
+ const mask = tcbInfo.tdxModule.attributesMask.bytes;
1632
+ const seamAttrs = tdQuoteBody.seamAttributes;
1633
+ if (mask.length !== seamAttrs.length) {
1634
+ throw new Error(
1635
+ `SeamAttributes length mismatch: quote(${seamAttrs.length}) vs PCS mask(${mask.length})`
1636
+ );
1637
+ }
1638
+ const maskedAttrs = applyMask(mask, seamAttrs);
1639
+ const expectedAttrs = tcbInfo.tdxModule.attributes.bytes;
1640
+ if (!equalBytes(maskedAttrs, expectedAttrs)) {
1641
+ throw new Error(
1642
+ `AttributesMask mismatch: quote(${toHex(maskedAttrs)}) vs PCS(${toHex(expectedAttrs)})`
1643
+ );
1644
+ }
1645
+ checkTcbInfoTcbStatus(tcbInfo, tdQuoteBody, pckCertExtensions);
1646
+ }
1647
+ async function checkTcbInfoTcbStatus(tcbInfo, tdQuoteBody, pckCertExtensions) {
1648
+ const tcbLevels = tcbInfo.tcbLevels;
1649
+ const matchingTcbLevel = getMatchingTcbLevel(
1650
+ tcbLevels,
1651
+ tdQuoteBody,
1652
+ pckCertExtensions.tcb.pceSvn,
1653
+ pckCertExtensions.tcb.cpuSvnComponents
1654
+ );
1655
+ if (!matchingTcbLevel) {
1656
+ throw new Error("Failed to find matching TCB Level");
1657
+ }
1658
+ if (tdQuoteBody.teeTcbSvn[1] > 0) {
1659
+ const matchingTdxModuleTcbLevel = await getMatchingTdxModuleTcbLevel(
1660
+ tcbInfo.tdxModuleIdentities,
1661
+ tdQuoteBody.teeTcbSvn
1662
+ );
1663
+ if (!matchingTdxModuleTcbLevel) {
1664
+ throw new Error("Failed to find matching TDX Module TCB Level");
1665
+ }
1666
+ if (matchingTdxModuleTcbLevel.tcbStatus !== TcbComponentStatusUpToDate) {
1667
+ throw new Error(
1668
+ `TDX Module TCB Status is not "${TcbComponentStatusUpToDate}", found "${matchingTdxModuleTcbLevel.tcbStatus}"`
1669
+ );
1670
+ }
1671
+ return;
1672
+ }
1673
+ if (matchingTcbLevel.tcbStatus !== TcbComponentStatusUpToDate) {
1674
+ throw new Error(
1675
+ `TCB Status is not "${TcbComponentStatusUpToDate}", found "${matchingTcbLevel.tcbStatus}"`
1676
+ );
1677
+ }
1678
+ }
1679
+ async function getMatchingTdxModuleTcbLevel(tcbInfoTdxModuleIdentities, teeTcbSvn) {
1680
+ const tdxModuleVersion = teeTcbSvn.slice(1, 2);
1681
+ const tdxModuleIdentityID = tcbInfoTdxModuleIDPrefix + toHex(tdxModuleVersion);
1682
+ const tdxModuleIsvSvn = teeTcbSvn[0];
1683
+ for (const tdxModuleIdentity of tcbInfoTdxModuleIdentities) {
1684
+ if (tdxModuleIdentityID === tdxModuleIdentity.id) {
1685
+ for (const tcbLevel of tdxModuleIdentity.tcbLevels) {
1686
+ if (tdxModuleIsvSvn >= tcbLevel.tcb.isvsvn) {
1687
+ return tcbLevel;
1688
+ }
1689
+ }
1690
+ throw new Error(
1691
+ `could not find a TDX Module Identity TCB Level matching the TDX Module's ISVSVN (${tdxModuleIsvSvn})`
1692
+ );
1693
+ }
1694
+ }
1695
+ throw new Error(
1696
+ `could not find a TDX Module Identity (${tdxModuleIdentityID}) matching the given TEE TDX version (${toHex(tdxModuleVersion)})`
1697
+ );
1698
+ }
1699
+ function checkQeTcbStatus(tcbLevels, isvSvn) {
1700
+ for (const level of tcbLevels) {
1701
+ if (level.tcb.isvsvn <= isvSvn) {
1702
+ if (level.tcbStatus !== "UpToDate") {
1703
+ throw new Error(
1704
+ `TCB Status is not "UpToDate", found "${level.tcbStatus}"`
1705
+ );
1706
+ }
1707
+ return;
1708
+ }
1709
+ }
1710
+ throw ErrTcbStatus;
1711
+ }
1712
+ function toHex(bytes) {
1713
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1714
+ }
1715
+ function applyMask(a, b) {
1716
+ if (a.length !== b.length) {
1717
+ throw new Error(
1718
+ `applyMask: input lengths differ (a: ${a.length}, b: ${b.length})`
1719
+ );
1720
+ }
1721
+ const result = new Uint8Array(a.length);
1722
+ for (let i = 0; i < a.length; i++) {
1723
+ result[i] = a[i] & b[i];
1724
+ }
1725
+ return result;
1726
+ }
1727
+ function getMatchingTcbLevel(tcbLevels, tdReport, pckCertPceSvn, pckCertCPUSvnComponents) {
1728
+ for (const tcbLevel of tcbLevels) {
1729
+ if (isCPUSvnHigherOrEqual(
1730
+ pckCertCPUSvnComponents,
1731
+ tcbLevel.tcb.sgxTcbcomponents
1732
+ ) && pckCertPceSvn >= tcbLevel.tcb.pcesvn && isTdxTcbSvnHigherOrEqual(
1733
+ tdReport.teeTcbSvn,
1734
+ tcbLevel.tcb.tdxTcbcomponents
1735
+ )) {
1736
+ return tcbLevel;
1737
+ }
1738
+ }
1739
+ throw new Error("no matching TCB level found");
1740
+ }
1741
+ function isCPUSvnHigherOrEqual(pckCertCPUSvnComponents, sgxTcbcomponents) {
1742
+ if (pckCertCPUSvnComponents.length !== sgxTcbcomponents.length) {
1743
+ return false;
1744
+ }
1745
+ for (let i = 0; i < pckCertCPUSvnComponents.length; i++) {
1746
+ if (pckCertCPUSvnComponents[i] < sgxTcbcomponents[i].svn) {
1747
+ return false;
1748
+ }
1749
+ }
1750
+ return true;
1751
+ }
1752
+ function isTdxTcbSvnHigherOrEqual(teeTcbSvn, tdxTcbcomponents) {
1753
+ if (teeTcbSvn.length !== tdxTcbcomponents.length) {
1754
+ return false;
1755
+ }
1756
+ let start = 0;
1757
+ if (teeTcbSvn[1] > 0) {
1758
+ start = 2;
1759
+ }
1760
+ for (let i = start; i < teeTcbSvn.length; i++) {
1761
+ if (teeTcbSvn[i] < tdxTcbcomponents[i].svn) {
1762
+ return false;
1763
+ }
1764
+ }
1765
+ return true;
1766
+ }
1767
+
1768
+ // src/audit/quote/pck.ts
1769
+ var pckCertChainCertificationDataTypeStart = 0;
1770
+ var pckCertChainCertificationDataTypeEnd = 2;
1771
+ var pckCertChainSizeStart = pckCertChainCertificationDataTypeEnd;
1772
+ var pckCertChainSizeEnd = 6;
1773
+ var pckCertChainDataStart = pckCertChainSizeEnd;
1774
+ var pckReportCertificationDataType = 5;
1775
+ var pckCertExtensionSize = 6;
1776
+ var sgxExtensionMinSize = 4;
1777
+ var ppidSize = 16;
1778
+ var pceIDSize = 2;
1779
+ var fmspcSize = 6;
1780
+ var processorIssuerID = "processor";
1781
+ var platformIssuerID = "platform";
1782
+ var processorIssuer = "Intel SGX PCK Processor CA";
1783
+ var pckCertPhrase = "Intel SGX PCK Certificate";
1784
+ var platformIssuer = "Intel SGX PCK Platform CA";
1785
+ var intermediateCertPhrase = "Intel SGX PCK Platform CA";
1786
+ var OidPPID = "1.2.840.113741.1.13.1.1";
1787
+ var OidPCEID = "1.2.840.113741.1.13.1.3";
1788
+ var OidTCB = "1.2.840.113741.1.13.1.2";
1789
+ var OidFMSPC = "1.2.840.113741.1.13.1.4";
1790
+ var OidCPUSvn = "1.2.840.113741.1.13.1.2.18";
1791
+ var OidPCESvn = "1.2.840.113741.1.13.1.2.17";
1792
+ var OidSgxExtension = "1.2.840.113741.1.13.1";
1793
+ var sgxTcbComponentOidPrefix = "1.2.840.113741.1.13.1.2";
1794
+ var ErrPckCertChainNil = new Error("PCK certificate chain is nil");
1795
+ var ErrPCKCertChainInvalid = new Error(
1796
+ "incomplete PCK Certificate chain found, should contain 3 concatenated PEM-formatted 'CERTIFICATE'-type block (PCK Leaf Cert||Intermediate CA Cert||Root CA Cert)"
1797
+ );
1798
+ var ErrRootCaCertExpired = new Error(
1799
+ "root CA certificate in PCK certificate chain has expired"
1800
+ );
1801
+ var ErrIntermediateCertNil = new Error(
1802
+ "intermediate certificate is empty"
1803
+ );
1804
+ var ErrPCKCertNil = new Error("PCK certificate is empty");
1805
+ var ErrRevocationCheckFailed = new Error(
1806
+ "unable to check for certificate revocation as GetCollateral parameter in the options is set to false"
1807
+ );
1808
+ var ErrIntermediateCaCertExpired = new Error(
1809
+ "intermediate CA certificate in PCK certificate chain has expired"
1810
+ );
1811
+ var ErrPckLeafCertExpired = new Error(
1812
+ "PCK leaf certificate in PCK certificate chain has expired"
1813
+ );
1814
+ var ErrPckCertCANil = new Error(
1815
+ "could not find CA from PCK certificate"
1816
+ );
1817
+ var trustedRootCertificate;
1818
+ function unwrapSingleSet(n) {
1819
+ if (n instanceof asn1js3.Set) {
1820
+ if (n.valueBlock.value.length !== 1) {
1821
+ throw new Error("SET wrapper should contain exactly one element");
1822
+ }
1823
+ return n.valueBlock.value[0];
1824
+ }
1825
+ return n;
1826
+ }
1827
+ function pckCertificateChainToProto(b) {
1828
+ const data = b.slice();
1829
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
1830
+ const certificateDataType = view.getUint16(
1831
+ pckCertChainCertificationDataTypeStart,
1832
+ true
1833
+ );
1834
+ const size = view.getUint32(pckCertChainSizeStart, true);
1835
+ const pckCertChain = data.slice(pckCertChainDataStart);
1836
+ const pckCertificateChain = {
1837
+ certificateDataType,
1838
+ size,
1839
+ pckCertChain
1840
+ };
1841
+ checkPCKCertificateChain(pckCertificateChain);
1842
+ return pckCertificateChain;
1843
+ }
1844
+ function checkPCKCertificateChain(chain) {
1845
+ if (!chain) {
1846
+ throw ErrPckCertChainNil;
1847
+ }
1848
+ const { certificateDataType, size, pckCertChain } = chain;
1849
+ if (certificateDataType >= 1 << 16) {
1850
+ throw new Error(
1851
+ `certification data type expected to be 2 bytes, got ${certificateDataType}`
1852
+ );
1853
+ }
1854
+ if (certificateDataType !== pckReportCertificationDataType) {
1855
+ throw new Error(
1856
+ `PCK certificate chain data type invalid, got ${certificateDataType}, expected ${pckReportCertificationDataType}`
1857
+ );
1858
+ }
1859
+ if (size !== pckCertChain.length) {
1860
+ throw new Error(
1861
+ `PCK certificate chain size is ${pckCertChain.length}. Expected size ${size}`
1862
+ );
1863
+ }
1864
+ }
1865
+ function findMatchingExtension(extns, oid) {
1866
+ const match = extns.find((ext) => ext.type === oid);
1867
+ if (!match) {
1868
+ throw new Error(
1869
+ `Unable to find extension with OID ${oid} in PCK certificate`
1870
+ );
1871
+ }
1872
+ return match;
1873
+ }
1874
+ async function pckCertificateExtensions(cert) {
1875
+ if (cert.extensions.length !== pckCertExtensionSize) {
1876
+ throw new Error(
1877
+ `PCK certificate extensions length found ${cert.extensions.length}. Expected ${pckCertExtensionSize}`
1878
+ );
1879
+ }
1880
+ const sgxExt = findMatchingExtension(cert.extensions, OidSgxExtension);
1881
+ if (!sgxExt) {
1882
+ throw new Error(
1883
+ "Could not find SGX extension present in the PCK certificate"
1884
+ );
1885
+ }
1886
+ const { result: inner, offset } = asn1js3.fromBER(sgxExt.value);
1887
+ if (offset === -1 || !(inner instanceof asn1js3.Sequence)) {
1888
+ throw new Error("Expected SGX extension to be a SEQUENCE");
1889
+ }
1890
+ const remaining = sgxExt.value.slice(offset);
1891
+ if (remaining.byteLength !== 0) {
1892
+ throw new Error("SGX extension has trailing bytes");
1893
+ }
1894
+ return extractSgxExtensions(inner.valueBlock.value);
1895
+ }
1896
+ function extractCaFromPckCert(pckCert) {
1897
+ const cnList = pckCert.issuerName.getField("CN");
1898
+ const pckIssuer = cnList.length > 0 ? cnList[0] : null;
1899
+ if (pckIssuer === platformIssuer) {
1900
+ return platformIssuerID;
1901
+ }
1902
+ if (pckIssuer === processorIssuer) {
1903
+ return processorIssuerID;
1904
+ }
1905
+ throw ErrPckCertCANil;
1906
+ }
1907
+ function extractSgxExtensions(extensions) {
1908
+ if (extensions.length < sgxExtensionMinSize) {
1909
+ throw new Error(
1910
+ `SGX Extension has length ${extensions.length}. It should have a minimum length of ${sgxExtensionMinSize}`
1911
+ );
1912
+ }
1913
+ let ppid;
1914
+ let tcb;
1915
+ let pceid;
1916
+ let fmspc;
1917
+ for (const ext of extensions) {
1918
+ if (!(ext instanceof asn1js3.Sequence)) {
1919
+ throw new Error("Expected each SGX extension to be a SEQUENCE");
1920
+ }
1921
+ const bytes = ext.toBER(false);
1922
+ const { result: parsed, offset } = asn1js3.fromBER(bytes);
1923
+ if (offset === -1 || !(parsed instanceof asn1js3.Sequence)) {
1924
+ throw new Error("Could not parse SGX extension entry");
1925
+ }
1926
+ const typeOidBlock = parsed.valueBlock.value[0];
1927
+ if (!(typeOidBlock instanceof asn1js3.ObjectIdentifier)) {
1928
+ throw new Error("Missing Object Identifier in SGX extension entry");
1929
+ }
1930
+ const oid = typeOidBlock.valueBlock.toString();
1931
+ if (oid === OidPPID) {
1932
+ ppid = extractAsn1OctetStringExtension(
1933
+ "PPID",
1934
+ ext,
1935
+ ppidSize
1936
+ );
1937
+ } else if (oid === OidTCB) {
1938
+ tcb = extractAsn1SequenceTcbExtension(ext);
1939
+ } else if (oid === OidPCEID) {
1940
+ pceid = extractAsn1OctetStringExtension(
1941
+ "PCEID",
1942
+ ext,
1943
+ pceIDSize
1944
+ );
1945
+ } else if (oid === OidFMSPC) {
1946
+ fmspc = extractAsn1OctetStringExtension(
1947
+ "FMSPC",
1948
+ ext,
1949
+ fmspcSize
1950
+ );
1951
+ }
1952
+ }
1953
+ if (!ppid || !tcb || !pceid || !fmspc) {
1954
+ throw new Error(
1955
+ "Missing one or more required SGX extensions in PCK certificate"
1956
+ );
1957
+ }
1958
+ return { ppid, tcb, pceid, fmspc };
1959
+ }
1960
+ function extractAsn1OctetStringExtension(name, seq, expectedSize) {
1961
+ if (seq.valueBlock.value.length !== 2) {
1962
+ throw new Error(`${name} extension should have 2 elements`);
1963
+ }
1964
+ const valueNode = unwrapSingleSet(seq.valueBlock.value[1]);
1965
+ if (!(valueNode instanceof asn1js3.OctetString)) {
1966
+ throw new Error(`${name} extension expected OCTET STRING`);
1967
+ }
1968
+ const bytes = new Uint8Array(valueNode.valueBlock.valueHex);
1969
+ if (bytes.length !== expectedSize) {
1970
+ throw new Error(
1971
+ `${name} has length ${bytes.length}, expected ${expectedSize}`
1972
+ );
1973
+ }
1974
+ return Buffer.from(bytes).toString("hex");
1975
+ }
1976
+ function extractAsn1SequenceTcbExtension(ext) {
1977
+ const raw = ext.toBER(false);
1978
+ const { result: outerSeq, offset } = asn1js3.fromBER(raw);
1979
+ if (offset === -1 || !(outerSeq instanceof asn1js3.Sequence)) {
1980
+ throw new Error("Could not parse TCB extension inside SGX extension");
1981
+ }
1982
+ const values = outerSeq.valueBlock.value;
1983
+ if (values.length !== 2) {
1984
+ throw new Error(
1985
+ `TCB extension sequence has ${values.length} elements, expected 2`
1986
+ );
1987
+ }
1988
+ const inner = values[1];
1989
+ const innerRaw = inner.toBER(false);
1990
+ const { result: innerSeq, offset: innerOffset } = asn1js3.fromBER(innerRaw);
1991
+ if (innerOffset === -1 || !(innerSeq instanceof asn1js3.Sequence)) {
1992
+ throw new Error("Could not parse TCB components inside TCB extension");
1993
+ }
1994
+ const components = innerSeq.valueBlock.value;
1995
+ if (components.length !== tcbExtensionSize) {
1996
+ throw new Error(
1997
+ `TCB components length is ${components.length}, expected ${tcbExtensionSize}`
1998
+ );
1999
+ }
2000
+ const tcbv = {};
2001
+ extractTcbExtension(components, tcbv);
2002
+ return tcbv;
2003
+ }
2004
+ async function verifyPCKCertificationChain(options) {
2005
+ const { chain, collateral } = options;
2006
+ if (!chain?.rootCert) throw ErrRootCertNil;
2007
+ if (!chain.intermediateCerts) throw ErrIntermediateCertNil;
2008
+ if (!chain.pckCertificate) throw ErrPCKCertNil;
2009
+ const rootCert = chain.rootCert;
2010
+ const intermediateCert = chain.intermediateCerts;
2011
+ const pckCert = chain.pckCertificate;
2012
+ await validateCertificate2(rootCert, rootCert, rootCertPhrase);
2013
+ await validateCertificate2(intermediateCert, rootCert, intermediateCertPhrase);
2014
+ await validateCertificate2(pckCert, intermediateCert, pckCertPhrase);
2015
+ const opts = x509Options(options.trustedRoots, intermediateCert, options.now);
2016
+ const ok = await pckCert.verify(opts);
2017
+ if (!ok) {
2018
+ throw new Error("error verifying PCK Certificate");
2019
+ }
2020
+ if (options.checkRevocations) {
2021
+ if (!options.getCollateral) throw ErrRevocationCheckFailed;
2022
+ await validateCRL(collateral?.rootCaCrl, rootCert);
2023
+ await validateCRL(collateral?.pckCrl, intermediateCert);
2024
+ if (collateral?.pckCrl?.issuer !== pckCert.issuer) {
2025
+ throw new Error(
2026
+ `issuer "${collateral.pckCrl.issuer}" of PCK CRL does not match PCK Certificate issuer "${pckCert.issuer}"`
2027
+ );
2028
+ }
2029
+ for (const entry of collateral.rootCaCrl?.entries ?? []) {
2030
+ if (intermediateCert.serialNumber === entry.serialNumber) {
2031
+ throw new Error(
2032
+ `Intermediate certificate was revoked at ${entry.revocationDate}`
2033
+ );
2034
+ }
2035
+ }
2036
+ for (const entry of collateral.pckCrl?.entries ?? []) {
2037
+ if (pckCert.serialNumber === entry.serialNumber) {
2038
+ throw new Error(
2039
+ `PCK Leaf certificate was revoked at ${entry.revocationDate}`
2040
+ );
2041
+ }
2042
+ }
2043
+ }
2044
+ await checkCertificateExpiration(chain, options);
2045
+ }
2046
+ async function checkCertificateExpiration(chain, options) {
2047
+ const currentTime = options.now;
2048
+ if (currentTime > chain.rootCert.notAfter) {
2049
+ throw ErrRootCaCertExpired;
2050
+ }
2051
+ if (currentTime > chain.intermediateCerts.notAfter) {
2052
+ throw ErrIntermediateCaCertExpired;
2053
+ }
2054
+ if (currentTime > chain.pckCertificate.notAfter) {
2055
+ throw ErrPckLeafCertExpired;
2056
+ }
2057
+ }
2058
+ async function validateCertificate2(cert, parent, expectedPhrase) {
2059
+ if (!cert) throw new Error("certificate is nil");
2060
+ if (!parent) throw new Error("parent certificate is nil");
2061
+ const cnList = cert.subjectName.getField("CN");
2062
+ const subjectCN = cnList.length > 0 ? cnList[0] : null;
2063
+ if (subjectCN !== expectedPhrase) {
2064
+ throw new Error(
2065
+ `"${subjectCN}" is not expected in certificate's subject name. Expected "${expectedPhrase}"`
2066
+ );
2067
+ }
2068
+ const issuerName = cert.issuerName.toString();
2069
+ const parentSubject = parent.subjectName.toString();
2070
+ if (issuerName !== parentSubject) {
2071
+ throw new Error(
2072
+ `Certificate issuer name ("${issuerName}") does not match parent certificate subject name ("${parentSubject}")`
2073
+ );
2074
+ }
2075
+ const verified = await cert.verify({ publicKey: parent });
2076
+ if (!verified) {
2077
+ throw new Error(
2078
+ "Certificate signature verification using parent certificate failed"
2079
+ );
2080
+ }
2081
+ }
2082
+ function x509Options(trustedRoots, intermediateCert, now) {
2083
+ let publicKeySource;
2084
+ if (intermediateCert) {
2085
+ publicKeySource = intermediateCert;
2086
+ } else if (trustedRoots && trustedRoots.length > 0) {
2087
+ publicKeySource = trustedRoots[0];
2088
+ } else {
2089
+ publicKeySource = trustedRootCertificate;
2090
+ }
2091
+ return {
2092
+ date: now,
2093
+ publicKey: publicKeySource
2094
+ };
2095
+ }
2096
+
2097
+ // src/audit/quote/collateral.ts
2098
+ var x5093 = __toESM(require("@peculiar/x509"));
2099
+ var asn1js4 = __toESM(require("asn1js"));
2100
+ var ErrMissingTcbInfoBody = new Error(
2101
+ "missing tcbInfo body in the collaterals obtained"
2102
+ );
2103
+ var ErrMissingEnclaveIdentityBody = new Error(
2104
+ "missing enclaveIdentity body in the collaterals obtained"
2105
+ );
2106
+ var ErrTcbInfoNil = new Error("tcbInfo is empty in collaterals");
2107
+ var ErrQeIdentityNil = new Error("QeIdentity is empty in collaterals");
2108
+ var ErrMissingTcbInfoSigningCert = new Error(
2109
+ "missing signing certificate in the issuer chain of tcbInfo"
2110
+ );
2111
+ var ErrMissingTcbInfoRootCert = new Error(
2112
+ "missing root certificate in the issuer chain of tcbInfo"
2113
+ );
2114
+ var ErrMissingQeIdentitySigningCert = new Error(
2115
+ "missing signing certificate in the issuer chain of QeIdentity"
2116
+ );
2117
+ var ErrMissingQeIdentityRootCert = new Error(
2118
+ "missing root certificate in the issuer chain of QeIdentity"
2119
+ );
2120
+ var ErrMissingPckCrl = new Error(
2121
+ "missing PCK CRL in the collaterals obtained"
2122
+ );
2123
+ var ErrMissingRootCaCrl = new Error(
2124
+ "missing ROOT CA CRL in the collaterals obtained"
2125
+ );
2126
+ var ErrMissingPCKCrlSigningCert = new Error(
2127
+ "missing signing certificate in the issuer chain of PCK CRL"
2128
+ );
2129
+ var ErrMissingPCKCrlRootCert = new Error(
2130
+ "missing root certificate in the issuer chain of PCK CRL"
2131
+ );
2132
+ var ErrTcbInfoExpired = new Error("tcbInfo has expired");
2133
+ var ErrQeIdentityExpired = new Error("QeIdentity has expired");
2134
+ var ErrTcbInfoSigningCertExpired = new Error(
2135
+ "tcbInfo signing certificate has expired"
2136
+ );
2137
+ var ErrTcbInfoRootCertExpired = new Error(
2138
+ "tcbInfo root certificate has expired"
2139
+ );
2140
+ var ErrQeIdentityRootCertExpired = new Error(
2141
+ "QeIdentity root certificate has expired"
2142
+ );
2143
+ var ErrQeIdentitySigningCertExpired = new Error(
2144
+ "QeIdentity signing certificate has expired"
2145
+ );
2146
+ var ErrRootCaCrlExpired = new Error("root CA CRL has expired");
2147
+ var ErrPCKCrlExpired = new Error("PCK CRL has expired");
2148
+ var ErrPCKCrlSigningCertExpired = new Error(
2149
+ "PCK CRL signing certificate has expired"
2150
+ );
2151
+ var ErrPCKCrlRootCertExpired = new Error(
2152
+ "PCK CRL root certificate has expired"
2153
+ );
2154
+ var ErrEmptyRootCRLUrl = new Error(
2155
+ "empty url found in QeIdentity issuer's chain which is required to receive ROOT CA CRL"
2156
+ );
2157
+ var sgxPckCrlIssuerChainPhrase = "Sgx-Pck-Crl-Issuer-Chain";
2158
+ var sgxQeIdentityIssuerChainPhrase = "Sgx-Enclave-Identity-Issuer-Chain";
2159
+ var enclaveIdentityPhrase = "enclaveIdentity";
2160
+ var tcbInfoIssuerChainPhrase = "Tcb-Info-Issuer-Chain";
2161
+ var tcbInfoPhrase = "tcbInfo";
2162
+ var OID_CRL_DISTRIBUTION_POINTS = "2.5.29.31";
2163
+ var pcsTdxBaseURL = "https://api.trustedservices.intel.com/tdx/certification/v4";
2164
+ var pcsSgxBaseURL = "https://api.trustedservices.intel.com/sgx/certification/v4";
2165
+ var RetryHTTPSGetter = class {
2166
+ constructor(underlying = new SimpleHTTPSGetter(), timeoutMs = 12e4, maxRetryDelayMs = 3e4) {
2167
+ this.underlying = underlying;
2168
+ this.timeoutMs = timeoutMs;
2169
+ this.maxRetryDelayMs = maxRetryDelayMs;
2170
+ }
2171
+ async get(url) {
2172
+ const deadline = Date.now() + this.timeoutMs;
2173
+ let delay = 2e3;
2174
+ for (; ; ) {
2175
+ try {
2176
+ return await this.underlying.get(url);
2177
+ } catch (err) {
2178
+ if (Date.now() + delay > deadline) {
2179
+ throw err;
2180
+ }
2181
+ await new Promise((r) => setTimeout(r, delay));
2182
+ delay = Math.min(delay * 2, this.maxRetryDelayMs);
2183
+ }
2184
+ }
2185
+ }
2186
+ };
2187
+ var SimpleHTTPSGetter = class {
2188
+ async get(url) {
2189
+ const resp = await fetch(url);
2190
+ if (!resp.ok) {
2191
+ throw new Error(`failed to retrieve ${url}, status code ${resp.status}`);
2192
+ }
2193
+ const headers = {};
2194
+ resp.headers.forEach((value, key) => {
2195
+ if (!headers[key]) headers[key] = [];
2196
+ headers[key].push(value);
2197
+ });
2198
+ const body = new Uint8Array(await resp.arrayBuffer());
2199
+ return [headers, body];
2200
+ }
2201
+ };
2202
+ var CRLUnavailableError = class extends Error {
2203
+ constructor(causes) {
2204
+ super("CRL is unavailable");
2205
+ this.name = "CRLUnavailableError";
2206
+ this.causes = causes;
2207
+ }
2208
+ };
2209
+ var AttestationRecreationError = class extends Error {
2210
+ constructor(message) {
2211
+ super(message);
2212
+ this.name = "AttestationRecreationError";
2213
+ }
2214
+ };
2215
+ function defaultHTTPSGetter() {
2216
+ return new RetryHTTPSGetter();
2217
+ }
2218
+ async function verifyCollateral(options) {
2219
+ const collateral = options.collateral;
2220
+ if (!collateral) throw ErrCollateralNil;
2221
+ if (!collateral.tcbInfoBody) throw ErrMissingTcbInfoBody;
2222
+ if (!collateral.enclaveIdentityBody) throw ErrMissingEnclaveIdentityBody;
2223
+ if (Object.keys(collateral.tdxTcbInfo || {}).length === 0) throw ErrTcbInfoNil;
2224
+ if (Object.keys(collateral.qeIdentity || {}).length === 0)
2225
+ throw ErrQeIdentityNil;
2226
+ if (!collateral.tcbInfoIssuerIntermediateCertificate)
2227
+ throw ErrMissingTcbInfoSigningCert;
2228
+ if (!collateral.tcbInfoIssuerRootCertificate) throw ErrMissingTcbInfoRootCert;
2229
+ if (!collateral.qeIdentityIssuerIntermediateCertificate)
2230
+ throw ErrMissingQeIdentitySigningCert;
2231
+ if (!collateral.qeIdentityIssuerRootCertificate)
2232
+ throw ErrMissingQeIdentityRootCert;
2233
+ if (options.checkRevocations) {
2234
+ if (!collateral.pckCrl) throw ErrMissingPckCrl;
2235
+ if (!collateral.rootCaCrl) throw ErrMissingRootCaCrl;
2236
+ if (!collateral.pckCrlIssuerIntermediateCertificate)
2237
+ throw ErrMissingPCKCrlSigningCert;
2238
+ if (!collateral.pckCrlIssuerRootCertificate) throw ErrMissingPCKCrlRootCert;
2239
+ }
2240
+ await checkCollateralExpiration(collateral, options);
2241
+ }
2242
+ async function checkCollateralExpiration(collateral, options) {
2243
+ const currentTime = options.now;
2244
+ const tcbInfo = collateral.tdxTcbInfo.tcbInfo;
2245
+ const qeIdentity = collateral.qeIdentity.enclaveIdentity;
2246
+ if (currentTime > tcbInfo.nextUpdate) {
2247
+ throw ErrTcbInfoExpired;
2248
+ }
2249
+ if (currentTime > qeIdentity.nextUpdate) {
2250
+ throw ErrQeIdentityExpired;
2251
+ }
2252
+ if (currentTime > collateral.tcbInfoIssuerIntermediateCertificate.notAfter) {
2253
+ throw ErrTcbInfoSigningCertExpired;
2254
+ }
2255
+ if (currentTime > collateral.tcbInfoIssuerRootCertificate.notAfter) {
2256
+ throw ErrTcbInfoRootCertExpired;
2257
+ }
2258
+ if (currentTime > collateral.qeIdentityIssuerRootCertificate.notAfter) {
2259
+ throw ErrQeIdentityRootCertExpired;
2260
+ }
2261
+ if (currentTime > collateral.qeIdentityIssuerIntermediateCertificate.notAfter) {
2262
+ throw ErrQeIdentitySigningCertExpired;
2263
+ }
2264
+ if (options.checkRevocations) {
2265
+ if (currentTime > collateral.rootCaCrl.nextUpdate) {
2266
+ throw ErrRootCaCrlExpired;
2267
+ }
2268
+ if (currentTime > collateral.pckCrl.nextUpdate) {
2269
+ throw ErrPCKCrlExpired;
2270
+ }
2271
+ if (currentTime > collateral.pckCrlIssuerIntermediateCertificate.notAfter) {
2272
+ throw ErrPCKCrlSigningCertExpired;
2273
+ }
2274
+ if (currentTime > collateral.pckCrlIssuerRootCertificate.notAfter) {
2275
+ throw ErrPCKCrlRootCertExpired;
2276
+ }
2277
+ }
2278
+ }
2279
+ async function obtainCollateral(fmspc, ca, options) {
2280
+ const getter = options.getter ?? defaultHTTPSGetter();
2281
+ const collateral = {};
2282
+ await getTcbInfo(fmspc, getter, collateral);
2283
+ await getQeIdentity(getter, collateral);
2284
+ if (options.checkRevocations) {
2285
+ await getPckCrl(ca, getter, collateral);
2286
+ await getRootCrl(getter, collateral);
2287
+ }
2288
+ return collateral;
2289
+ }
2290
+ function pckCrlURL(ca) {
2291
+ return `${pcsSgxBaseURL}/pckcrl?ca=${encodeURIComponent(ca)}&encoding=der`;
2292
+ }
2293
+ async function getPckCrl(ca, getter, collateral) {
2294
+ const pckCrlURLStr = pckCrlURL(ca);
2295
+ let headers;
2296
+ let body;
2297
+ try {
2298
+ ;
2299
+ [headers, body] = await getter.get(pckCrlURLStr);
2300
+ } catch (err) {
2301
+ const error = err instanceof Error ? err : new Error(String(err));
2302
+ throw new CRLUnavailableError([error, new Error("could not fetch PCK CRL")]);
2303
+ }
2304
+ let intermediateCert;
2305
+ let rootCert;
2306
+ try {
2307
+ ;
2308
+ [intermediateCert, rootCert] = headerToIssuerChain(
2309
+ headers,
2310
+ sgxPckCrlIssuerChainPhrase
2311
+ );
2312
+ } catch (err) {
2313
+ const error = err instanceof Error ? err : new Error(String(err));
2314
+ throw error;
2315
+ }
2316
+ collateral.pckCrlIssuerIntermediateCertificate = intermediateCert;
2317
+ collateral.pckCrlIssuerRootCertificate = rootCert;
2318
+ try {
2319
+ collateral.pckCrl = bodyToCrl(body);
2320
+ } catch (err) {
2321
+ const error = err instanceof Error ? err : new Error(String(err));
2322
+ throw new CRLUnavailableError([error, new Error("could not fetch PCK CRL")]);
2323
+ }
2324
+ }
2325
+ function headerToIssuerChain(headers, phrase) {
2326
+ const values = headers[phrase];
2327
+ if (!values || values.length !== 1) {
2328
+ throw new Error(
2329
+ `issuer chain is expected to be of size 1, found ${values?.length ?? 0}`
2330
+ );
2331
+ }
2332
+ const encodedChain = values[0];
2333
+ if (!encodedChain) {
2334
+ throw new Error(`issuer chain certificates missing in "${phrase}"`);
2335
+ }
2336
+ let certChain;
2337
+ try {
2338
+ certChain = decodeURIComponent(encodedChain);
2339
+ } catch (err) {
2340
+ throw new Error(`unable to decode issuer chain in "${phrase}": ${err}`);
2341
+ }
2342
+ const pemBlocks = x5093.PemConverter.decode(certChain);
2343
+ if (pemBlocks.length !== 2) {
2344
+ throw new Error(
2345
+ `expected 2 PEM blocks (intermediate + root), found ${pemBlocks.length}`
2346
+ );
2347
+ }
2348
+ const intermediate = new x5093.X509Certificate(pemBlocks[0]);
2349
+ const root = new x5093.X509Certificate(pemBlocks[1]);
2350
+ return [intermediate, root];
2351
+ }
2352
+ async function getQeIdentity(getter, collateral) {
2353
+ const QeIdentityURL = qeIdentityURL();
2354
+ let headers;
2355
+ let body;
2356
+ try {
2357
+ ;
2358
+ [headers, body] = await getter.get(QeIdentityURL);
2359
+ } catch (err) {
2360
+ throw new AttestationRecreationError(
2361
+ `could not receive QeIdentity response: ${err}`
2362
+ );
2363
+ }
2364
+ let intermediateCert;
2365
+ let rootCert;
2366
+ try {
2367
+ ;
2368
+ [intermediateCert, rootCert] = headerToIssuerChain(
2369
+ headers,
2370
+ sgxQeIdentityIssuerChainPhrase
2371
+ );
2372
+ } catch (err) {
2373
+ throw new AttestationRecreationError(err.message);
2374
+ }
2375
+ collateral.qeIdentityIssuerIntermediateCertificate = intermediateCert;
2376
+ collateral.qeIdentityIssuerRootCertificate = rootCert;
2377
+ try {
2378
+ collateral.qeIdentity = JSON.parse(
2379
+ new TextDecoder().decode(body)
2380
+ );
2381
+ } catch (err) {
2382
+ throw new AttestationRecreationError(
2383
+ `unable to unmarshal QeIdentity response: ${err}`
2384
+ );
2385
+ }
2386
+ try {
2387
+ collateral.enclaveIdentityBody = bodyToRawMessage(
2388
+ enclaveIdentityPhrase,
2389
+ body
2390
+ );
2391
+ } catch (err) {
2392
+ throw new AttestationRecreationError(err.message);
2393
+ }
2394
+ }
2395
+ function bodyToRawMessage(name, body) {
2396
+ if (body.length === 0) {
2397
+ throw new Error(`"${name}" is empty`);
2398
+ }
2399
+ const decoded = new TextDecoder().decode(body);
2400
+ let rawBody;
2401
+ try {
2402
+ rawBody = JSON.parse(decoded);
2403
+ } catch (err) {
2404
+ throw new Error(`could not convert "${name}" body to raw message: ${err}`);
2405
+ }
2406
+ const val = rawBody[name];
2407
+ if (val === void 0) {
2408
+ throw new Error(`"${name}" field is missing in the response received`);
2409
+ }
2410
+ const json = JSON.stringify(val);
2411
+ return new TextEncoder().encode(json);
2412
+ }
2413
+ function qeIdentityURL() {
2414
+ return `${pcsTdxBaseURL}/qe/identity`;
2415
+ }
2416
+ function TcbInfoURL(fmspc) {
2417
+ return `${pcsTdxBaseURL}/tcb?fmspc=${fmspc}`;
2418
+ }
2419
+ async function getTcbInfo(fmspc, getter, collateral) {
2420
+ const tcbInfoURL = TcbInfoURL(fmspc);
2421
+ let header;
2422
+ let body;
2423
+ try {
2424
+ ;
2425
+ [header, body] = await getter.get(tcbInfoURL);
2426
+ } catch (err) {
2427
+ throw new AttestationRecreationError(
2428
+ `could not receive tcbInfo response: ${err}`
2429
+ );
2430
+ }
2431
+ let intermediateCert, rootCert;
2432
+ try {
2433
+ ;
2434
+ [intermediateCert, rootCert] = headerToIssuerChain(
2435
+ header,
2436
+ tcbInfoIssuerChainPhrase
2437
+ );
2438
+ } catch (err) {
2439
+ throw new AttestationRecreationError(err.message);
2440
+ }
2441
+ collateral.tcbInfoIssuerIntermediateCertificate = intermediateCert;
2442
+ collateral.tcbInfoIssuerRootCertificate = rootCert;
2443
+ try {
2444
+ collateral.tdxTcbInfo = JSON.parse(
2445
+ new TextDecoder().decode(body)
2446
+ );
2447
+ } catch (err) {
2448
+ throw new AttestationRecreationError(
2449
+ `unable to unmarshal tcbInfo response: ${err}`
2450
+ );
2451
+ }
2452
+ try {
2453
+ collateral.tcbInfoBody = bodyToRawMessage(tcbInfoPhrase, body);
2454
+ } catch (err) {
2455
+ throw new AttestationRecreationError(err.message);
2456
+ }
2457
+ }
2458
+ function getCrlDistributionPoints(cert) {
2459
+ const ext = cert.extensions.find(
2460
+ (e) => e.type === OID_CRL_DISTRIBUTION_POINTS
2461
+ );
2462
+ if (!ext) return [];
2463
+ const outer = asn1js4.fromBER(ext.value);
2464
+ if (outer.offset === -1 || !(outer.result instanceof asn1js4.OctetString))
2465
+ return [];
2466
+ const inner = asn1js4.fromBER(outer.result.valueBlock.valueHex);
2467
+ if (inner.offset === -1 || !(inner.result instanceof asn1js4.Sequence))
2468
+ return [];
2469
+ const seq = inner.result;
2470
+ if (seq.valueBlock.value.length === 0) return [];
2471
+ const dp = seq.valueBlock.value[0];
2472
+ if (!(dp instanceof asn1js4.Constructed) || dp.valueBlock.value.length === 0)
2473
+ return [];
2474
+ const dpn = dp.valueBlock.value[0];
2475
+ if (!(dpn instanceof asn1js4.Constructed)) return [];
2476
+ const names = new GeneralNames({ schema: dpn });
2477
+ return names.names.filter((n) => n.type === 6).map((n) => n.value);
2478
+ }
2479
+ var GeneralName = class {
2480
+ constructor(type, value) {
2481
+ this.type = type;
2482
+ this.value = value;
2483
+ }
2484
+ };
2485
+ var GeneralNames = class {
2486
+ constructor(params) {
2487
+ this.names = [];
2488
+ const items = params.schema.valueBlock.value;
2489
+ for (const el of items) {
2490
+ if (el.idBlock.tagClass === 3) {
2491
+ const tag = el.idBlock.tagNumber;
2492
+ if (tag === 6 && el instanceof asn1js4.Primitive) {
2493
+ const uri = new asn1js4.IA5String({ valueHex: el.valueBlock.valueHex });
2494
+ this.names.push(new GeneralName(tag, uri.valueBlock.value));
2495
+ }
2496
+ }
2497
+ }
2498
+ }
2499
+ };
2500
+ async function getRootCrl(getter, collateral) {
2501
+ const rootCrlURLs = getCrlDistributionPoints(
2502
+ collateral.qeIdentityIssuerRootCertificate
2503
+ );
2504
+ if (rootCrlURLs.length === 0) {
2505
+ throw ErrEmptyRootCRLUrl;
2506
+ }
2507
+ const errors = [];
2508
+ for (const url of rootCrlURLs) {
2509
+ try {
2510
+ const [, body] = await getter.get(url);
2511
+ collateral.rootCaCrl = bodyToCrl(body);
2512
+ return;
2513
+ } catch (err) {
2514
+ errors.push(err instanceof Error ? err : new Error(String(err)));
2515
+ }
2516
+ }
2517
+ throw new CRLUnavailableError([
2518
+ ...errors,
2519
+ new Error("could not fetch root CRL")
2520
+ ]);
2521
+ }
2522
+ function bodyToCrl(body) {
2523
+ try {
2524
+ return new x5093.X509Crl(body);
2525
+ } catch (err) {
2526
+ throw new Error(`unable to parse DER bytes of CRL: ${err}`);
2527
+ }
2528
+ }
2529
+
2530
+ // src/audit/quote/verify.ts
2531
+ var ErrOptionsNil = new Error("options parameter is empty");
2532
+ var ErrRootCaCertExpired2 = new Error(
2533
+ "root CA certificate in PCK certificate chain has expired"
2534
+ );
2535
+ var ErrHashVerificationFail = new Error(
2536
+ "unable to verify message digest using quote's signature and ecdsa attestation key"
2537
+ );
2538
+ var ErrSHA256VerificationFail = new Error(
2539
+ "QE Report Data does not match with value of SHA 256 calculated over the concatenation of ECDSA Attestation Key and QE Authenticated Data"
2540
+ );
2541
+ async function extractChainFromQuoteV4(quote) {
2542
+ const certChainBytes = quote.signedData?.certificationData?.qeReportCertificationData?.pckCertificateChainData?.pckCertChain;
2543
+ if (!certChainBytes) throw ErrPckCertChainNil;
2544
+ const pemString = new TextDecoder().decode(certChainBytes);
2545
+ const pemBlocks = x5094.PemConverter.decode(pemString);
2546
+ if (pemBlocks.length < 3) {
2547
+ throw ErrPCKCertChainInvalid;
2548
+ }
2549
+ const pckCert = new x5094.X509Certificate(pemBlocks[0]);
2550
+ const intermediateCert = new x5094.X509Certificate(pemBlocks[1]);
2551
+ const rootCert = new x5094.X509Certificate(pemBlocks[2]);
2552
+ return {
2553
+ pckCertificate: pckCert,
2554
+ rootCert,
2555
+ intermediateCerts: intermediateCert
2556
+ };
2557
+ }
2558
+ async function localVerifyTdxQuote(aquote, opts) {
2559
+ if (!opts) throw ErrOptionsNil;
2560
+ checkQuoteV4(aquote);
2561
+ const chain = await extractChainFromQuoteV4(aquote);
2562
+ const exts = await pckCertificateExtensions(chain.pckCertificate);
2563
+ let acollateral;
2564
+ if (opts.getCollateral) {
2565
+ const ca = extractCaFromPckCert(chain.pckCertificate);
2566
+ acollateral = await obtainCollateral(exts.fmspc, ca, opts);
2567
+ }
2568
+ opts.collateral = acollateral;
2569
+ opts.pckCertExtensions = exts;
2570
+ opts.chain = chain;
2571
+ if (!opts.now) opts.now = /* @__PURE__ */ new Date();
2572
+ await verifyEvidenceV4(aquote, opts);
2573
+ return exts;
2574
+ }
2575
+ function equalBytes(a, b) {
2576
+ if (a.length !== b.length) return false;
2577
+ for (let i = 0; i < a.length; i++) {
2578
+ if (a[i] !== b[i]) return false;
2579
+ }
2580
+ return true;
2581
+ }
2582
+ function getHeaderAndTdQuoteBodyInAbiBytes(aquote) {
2583
+ const header = HeaderToAbiBytes(aquote.header);
2584
+ if (!header) {
2585
+ throw new Error("could not convert header to ABI bytes");
2586
+ }
2587
+ const tdQuoteBody = TdQuoteBodyToAbiBytes(aquote.tdQuoteBody);
2588
+ if (!tdQuoteBody) {
2589
+ throw new Error("could not convert TD Quote Body to ABI bytes");
2590
+ }
2591
+ const combined = new Uint8Array(header.length + tdQuoteBody.length);
2592
+ combined.set(header, 0);
2593
+ combined.set(tdQuoteBody, header.length);
2594
+ return combined;
2595
+ }
2596
+ async function verifyEvidenceV4(aquote, options) {
2597
+ if (aquote.header.teeType !== TeeTDX) {
2598
+ throw new Error("Invalid TEE type in quote (expected TDX)");
2599
+ }
2600
+ await verifyPCKCertificationChain(options);
2601
+ if (options.getCollateral) {
2602
+ await verifyCollateral(options);
2603
+ await verifyTCBinfo(options);
2604
+ await verifyQeIdentity(options);
2605
+ }
2606
+ await verifyQuote(aquote, options);
2607
+ }
2608
+ async function verifyQuote(aquote, options) {
2609
+ const chain = options.chain;
2610
+ const collateral = options.collateral;
2611
+ const pckCertExtensions = options.pckCertExtensions;
2612
+ const attestKey = aquote.signedData?.ecdsaAttestationKey;
2613
+ if (!attestKey) {
2614
+ throw new Error("Missing attestation key in quote");
2615
+ }
2616
+ const signature = aquote.signedData?.signature;
2617
+ if (!signature) {
2618
+ throw new Error("Missing signature in quote");
2619
+ }
2620
+ const message = getHeaderAndTdQuoteBodyInAbiBytes(aquote);
2621
+ const isValid = await verifyEcdsaSignature(attestKey, message, signature);
2622
+ if (!isValid) {
2623
+ throw ErrHashVerificationFail;
2624
+ }
2625
+ const qeReportCertificationData = aquote.signedData?.certificationData?.qeReportCertificationData;
2626
+ if (!qeReportCertificationData)
2627
+ throw new Error("Missing QE report certification data");
2628
+ await tdxProtoQeReportSignature(
2629
+ qeReportCertificationData,
2630
+ chain.pckCertificate
2631
+ );
2632
+ await verifyHash256(aquote);
2633
+ if (collateral) {
2634
+ await verifyTdQuoteBody(aquote.tdQuoteBody, {
2635
+ tcbInfo: collateral.tdxTcbInfo.tcbInfo,
2636
+ pckCertExtensions
2637
+ });
2638
+ await verifyQeReport(qeReportCertificationData.qeReport, {
2639
+ qeIdentity: collateral.qeIdentity.enclaveIdentity
2640
+ });
2641
+ }
2642
+ }
2643
+ async function verifyQeReport(qeReport, options) {
2644
+ const { qeIdentity } = options;
2645
+ const miscSelectMaskBytes = qeIdentity.miscselectMask.bytes;
2646
+ const miscSelectBytes = qeIdentity.miscselect.bytes;
2647
+ if (miscSelectMaskBytes.length !== 4) {
2648
+ throw new Error(
2649
+ `MISCSELECTMask field size (${miscSelectMaskBytes.length}) is not 4`
2650
+ );
2651
+ }
2652
+ if (miscSelectBytes.length !== 4) {
2653
+ throw new Error(
2654
+ `MISCSELECT field size (${miscSelectBytes.length}) is not 4`
2655
+ );
2656
+ }
2657
+ const viewMask = new DataView(
2658
+ miscSelectMaskBytes.buffer,
2659
+ miscSelectMaskBytes.byteOffset,
2660
+ 4
2661
+ );
2662
+ const viewSelect = new DataView(
2663
+ miscSelectBytes.buffer,
2664
+ miscSelectBytes.byteOffset,
2665
+ 4
2666
+ );
2667
+ const miscSelectMask = qeReport.miscSelect & viewMask.getUint32(0, true);
2668
+ const miscSelect = viewSelect.getUint32(0, true);
2669
+ if (miscSelectMask !== miscSelect) {
2670
+ throw new Error(
2671
+ `MISCSELECT mismatch: expected ${miscSelect}, got masked ${miscSelectMask}`
2672
+ );
2673
+ }
2674
+ const attrsMask = qeIdentity.attributesMask.bytes;
2675
+ const reportAttrs = qeReport.attributes;
2676
+ if (attrsMask.length !== reportAttrs.length) {
2677
+ throw new Error(
2678
+ `AttributesMask size (${attrsMask.length}) does not match report attributes size (${reportAttrs.length})`
2679
+ );
2680
+ }
2681
+ const maskedAttrs = applyMask(attrsMask, reportAttrs);
2682
+ const expectedAttrs = qeIdentity.attributes.bytes;
2683
+ if (!equalBytes(maskedAttrs, expectedAttrs)) {
2684
+ throw new Error(
2685
+ `Attributes mismatch: masked=${toHex(maskedAttrs)} expected=${toHex(expectedAttrs)}`
2686
+ );
2687
+ }
2688
+ const mrSignerReport = qeReport.mrSigner;
2689
+ const mrSignerExpected = qeIdentity.mrsigner.bytes;
2690
+ if (!equalBytes(mrSignerReport, mrSignerExpected)) {
2691
+ throw new Error(
2692
+ `MRSIGNER mismatch: report=${toHex(mrSignerReport)} expected=${toHex(mrSignerExpected)}`
2693
+ );
2694
+ }
2695
+ if (qeReport.isvProdId !== qeIdentity.isvProdID) {
2696
+ throw new Error(
2697
+ `ISV PRODID mismatch: report=${qeReport.isvProdId} expected=${qeIdentity.isvProdID}`
2698
+ );
2699
+ }
2700
+ checkQeTcbStatus(qeIdentity.tcbLevels, qeReport.isvSvn);
2701
+ }
2702
+ async function verifyHash256(aquote) {
2703
+ const qeReportCertData = aquote.signedData?.certificationData?.qeReportCertificationData;
2704
+ if (!qeReportCertData) throw new Error("missing QE Report certification data");
2705
+ const qeReportData = qeReportCertData.qeReport?.reportData;
2706
+ const qeAuthData = qeReportCertData.qeAuthData?.data;
2707
+ const attestKey = aquote.signedData?.ecdsaAttestationKey;
2708
+ if (!qeReportData || !qeAuthData || !attestKey) {
2709
+ throw new Error("missing fields for QE report SHA-256 verification");
2710
+ }
2711
+ const concat = new Uint8Array(attestKey.length + qeAuthData.length);
2712
+ concat.set(attestKey, 0);
2713
+ concat.set(qeAuthData, attestKey.length);
2714
+ const hashBuffer = await x5094.cryptoProvider.get().subtle.digest("SHA-256", concat);
2715
+ let hashedMessage = new Uint8Array(hashBuffer);
2716
+ if (hashedMessage.length < qeReportData.length) {
2717
+ const padded = new Uint8Array(qeReportData.length);
2718
+ padded.set(hashedMessage, 0);
2719
+ hashedMessage = padded;
2720
+ }
2721
+ if (!equalBytes(hashedMessage, qeReportData)) {
2722
+ throw ErrSHA256VerificationFail;
2723
+ }
2724
+ }
2725
+ async function tdxProtoQeReportSignature(qeReportCertificationData, pckCertX509) {
2726
+ const rawReport = enclaveReportToAbiBytes(
2727
+ qeReportCertificationData.qeReport
2728
+ );
2729
+ if (!rawReport) {
2730
+ throw new Error("could not parse QE report");
2731
+ }
2732
+ await tdxQeReportSignature(
2733
+ rawReport,
2734
+ qeReportCertificationData.qeReportSignature,
2735
+ pckCertX509
2736
+ );
2737
+ }
2738
+ async function tdxQeReportSignature(qeReport, signature, pckCert) {
2739
+ const cryptoKey = await x5094.cryptoProvider.get().subtle.importKey(
2740
+ "spki",
2741
+ pckCert.publicKey.rawData,
2742
+ { name: "ECDSA", namedCurve: "P-256" },
2743
+ false,
2744
+ ["verify"]
2745
+ );
2746
+ const isValid = await x5094.cryptoProvider.get().subtle.verify(
2747
+ { name: "ECDSA", hash: "SHA-256" },
2748
+ cryptoKey,
2749
+ signature,
2750
+ qeReport
2751
+ );
2752
+ if (!isValid) {
2753
+ throw new Error(
2754
+ "QE report's signature verification using PCK Leaf Certificate failed"
2755
+ );
2756
+ }
2757
+ }
2758
+
2759
+ // src/audit/quote/validateTdxQuote.ts
2760
+ var CA_ROOT_PEM = `
2761
+ -----BEGIN CERTIFICATE-----
2762
+ MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw
2763
+ aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
2764
+ cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
2765
+ BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG
2766
+ A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0
2767
+ aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT
2768
+ AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7
2769
+ 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB
2770
+ uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ
2771
+ MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50
2772
+ ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV
2773
+ Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI
2774
+ KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg
2775
+ AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=
2776
+ -----END CERTIFICATE-----
2777
+ `;
2778
+ var ErrCertificationDataNil = new Error("certification data is nil");
2779
+ var ErrQeReportCertificationDataNil = new Error(
2780
+ "QE Report certification data is nil"
2781
+ );
2782
+ var ErrQeAuthDataNil = new Error("QE AuthData is nil");
2783
+ var ErrCertNil = new Error("certificate is nil");
2784
+ var ErrParentCertNil = new Error("parent certificate is nil");
2785
+ var ErrCertPubKeyType = new Error(
2786
+ "certificate public key is not of type ecdsa public key"
2787
+ );
2788
+ var ErrRootCaCertExpired3 = new Error(
2789
+ "root CA certificate in PCK certificate chain has expired"
2790
+ );
2791
+ var ErrPublicKeySize = new Error("public key is of unexpected size");
2792
+ var ErrPckExtInvalid = new Error(
2793
+ "unexpected leftover bytes for PCK certificate's extension"
2794
+ );
2795
+ var ErrSgxExtInvalid = new Error(
2796
+ "unexpected leftover bytes when parsing SGX extensions"
2797
+ );
2798
+ var ErrTcbExtInvalid = new Error(
2799
+ "unexpected leftover bytes for TCB extension inside SGX extension field"
2800
+ );
2801
+ var ErrTcbCompInvalid = new Error(
2802
+ "unexpected leftover bytes for TCB components in TCB Extension inside SGX extension field"
2803
+ );
2804
+ var signedDataSignatureStart = 0;
2805
+ var signedDataSignatureEnd = 64;
2806
+ var signedDataAttestationKeyStart = signedDataSignatureEnd;
2807
+ var signedDataAttestationKeyEnd = 128;
2808
+ var signedDataCertificationDataStart = signedDataAttestationKeyEnd;
2809
+ var certificateDataTypeStart = 0;
2810
+ var certificateDataTypeEnd = 2;
2811
+ var certificateSizeStart = certificateDataTypeEnd;
2812
+ var certificateSizeEnd = 6;
2813
+ var certificateDataStart = certificateSizeEnd;
2814
+ var qeReportCertificationDataType = 6;
2815
+ var signatureSize = 64;
2816
+ var enclaveReportStart = 0;
2817
+ var enclaveReportEnd = 384;
2818
+ var qeReportCertificationDataSignatureStart = enclaveReportEnd;
2819
+ var qeReportCertificationDataSignatureEnd = 448;
2820
+ var qeReportCertificationDataAuthDataStart = qeReportCertificationDataSignatureEnd;
2821
+ var authDataParsedDataSizeStart = 0;
2822
+ var authDataParsedDataSizeEnd = 2;
2823
+ var attestationKeySize = 64;
2824
+ async function getTrustedRoots(rot) {
2825
+ const inlines = rot.cabundles ?? [];
2826
+ if (inlines.length === 0) return null;
2827
+ const pool = [];
2828
+ for (const pemString of inlines) {
2829
+ const cert = new x5095.X509Certificate(pemString);
2830
+ pool.push(cert);
2831
+ }
2832
+ return pool;
2833
+ }
2834
+ async function rootOfTrustToVerifyOptions(rot) {
2835
+ return {
2836
+ checkRevocations: !!rot.checkCrl,
2837
+ getCollateral: !!rot.getCollateral,
2838
+ trustedRoots: await getTrustedRoots(rot) ?? void 0
2839
+ };
2840
+ }
2841
+ function headerToProto(buf) {
2842
+ if (buf.length !== 48) throw new Error("header must be 48 bytes");
2843
+ return {
2844
+ version: leU16(buf, 0),
2845
+ attestationKeyType: leU16(buf, 2),
2846
+ teeType: leU32(buf, 4),
2847
+ pceSvn: buf.slice(8, 10),
2848
+ qeSvn: buf.slice(10, 12),
2849
+ qeVendorId: buf.slice(12, 28),
2850
+ userData: buf.slice(28, 48)
2851
+ };
2852
+ }
2853
+ function tdQuoteBodyToProto(buf) {
2854
+ if (buf.length !== 584) throw new Error("TDQuoteBody must be 0x248 bytes");
2855
+ const body = {
2856
+ teeTcbSvn: buf.slice(0, 16),
2857
+ mrSeam: buf.slice(16, 64),
2858
+ mrSignerSeam: buf.slice(64, 112),
2859
+ seamAttributes: buf.slice(112, 120),
2860
+ tdAttributes: buf.slice(120, 128),
2861
+ xfam: buf.slice(128, 136),
2862
+ mrTd: buf.slice(136, 184),
2863
+ mrConfigId: buf.slice(184, 232),
2864
+ mrOwner: buf.slice(232, 280),
2865
+ mrOwnerConfig: buf.slice(280, 328),
2866
+ rtmrs: [
2867
+ buf.slice(328, 376),
2868
+ buf.slice(376, 424),
2869
+ buf.slice(424, 472),
2870
+ buf.slice(472, 520)
2871
+ ],
2872
+ reportData: buf.slice(520, 584)
2873
+ };
2874
+ return body;
2875
+ }
2876
+ function signedDataToProto(b) {
2877
+ const data = b.slice();
2878
+ const signedData = {
2879
+ signature: data.slice(signedDataSignatureStart, signedDataSignatureEnd),
2880
+ ecdsaAttestationKey: data.slice(
2881
+ signedDataAttestationKeyStart,
2882
+ signedDataAttestationKeyEnd
2883
+ ),
2884
+ certificationData: certificationDataToProto(
2885
+ data.slice(signedDataCertificationDataStart)
2886
+ )
2887
+ };
2888
+ checkEcdsa256BitQuoteV4AuthData(signedData);
2889
+ return signedData;
2890
+ }
2891
+ function checkEcdsa256BitQuoteV4AuthData(signedData) {
2892
+ if (!signedData) {
2893
+ throw ErrQuoteV4AuthDataNil;
2894
+ }
2895
+ if (signedData.signature.length !== signatureSize) {
2896
+ throw new Error(
2897
+ `signature size is ${signedData.signature.length} bytes. Expected ${signatureSize} bytes`
2898
+ );
2899
+ }
2900
+ if (signedData.ecdsaAttestationKey.length !== attestationKeySize) {
2901
+ throw new Error(
2902
+ `ecdsa attestation key size is ${signedData.ecdsaAttestationKey.length} bytes. Expected ${attestationKeySize} bytes`
2903
+ );
2904
+ }
2905
+ checkCertificationData(signedData.certificationData);
2906
+ }
2907
+ function checkCertificationData(certification) {
2908
+ if (!certification) {
2909
+ throw ErrCertificationDataNil;
2910
+ }
2911
+ const { certificateDataType } = certification;
2912
+ if (certificateDataType >= 1 << 16) {
2913
+ throw new Error(
2914
+ `certification data type field size must fit in 2 bytes, got ${certificateDataType}`
2915
+ );
2916
+ }
2917
+ if (certificateDataType !== qeReportCertificationDataType) {
2918
+ throw new Error(
2919
+ `certification data type invalid, got ${certificateDataType}, expected ${qeReportCertificationDataType}`
2920
+ );
2921
+ }
2922
+ checkQeReportCertificationData(certification.qeReportCertificationData);
2923
+ }
2924
+ function checkQeReportCertificationData(qeReport) {
2925
+ if (!qeReport) {
2926
+ throw ErrQeReportCertificationDataNil;
2927
+ }
2928
+ checkQeReport(qeReport.qeReport);
2929
+ if (qeReport.qeReportSignature.length !== signatureSize) {
2930
+ throw new Error(
2931
+ `signature size is ${qeReport.qeReportSignature.length} bytes. Expected ${signatureSize} bytes`
2932
+ );
2933
+ }
2934
+ checkQeAuthData(qeReport.qeAuthData);
2935
+ checkPCKCertificateChain(qeReport.pckCertificateChainData);
2936
+ }
2937
+ function checkQeAuthData(authData) {
2938
+ if (!authData) {
2939
+ throw ErrQeAuthDataNil;
2940
+ }
2941
+ const { parsedDataSize, data } = authData;
2942
+ if (parsedDataSize >= 1 << 16) {
2943
+ throw new Error(
2944
+ `parsed data size field must fit in 2 bytes, got ${parsedDataSize}`
2945
+ );
2946
+ }
2947
+ if (parsedDataSize !== data.length) {
2948
+ throw new Error(
2949
+ `parsed data size is ${data.length} bytes. Expected ${parsedDataSize} bytes`
2950
+ );
2951
+ }
2952
+ }
2953
+ function certificationDataToProto(b) {
2954
+ const data = b.slice();
2955
+ const certificateDataType = new DataView(
2956
+ data.buffer,
2957
+ data.byteOffset,
2958
+ data.byteLength
2959
+ ).getUint16(certificateDataTypeStart, true);
2960
+ const size = new DataView(
2961
+ data.buffer,
2962
+ data.byteOffset,
2963
+ data.byteLength
2964
+ ).getUint32(certificateSizeStart, true);
2965
+ const rawCertificateData = data.slice(certificateDataStart);
2966
+ if (rawCertificateData.length !== size) {
2967
+ throw new Error(
2968
+ `size of certificate data is 0x${rawCertificateData.length.toString(16)}. Expected size 0x${size.toString(16)}`
2969
+ );
2970
+ }
2971
+ const qeReportCertificationData = qeReportCertificationDataToProto(rawCertificateData);
2972
+ const certification = {
2973
+ certificateDataType,
2974
+ size,
2975
+ qeReportCertificationData
2976
+ };
2977
+ checkCertificationData(certification);
2978
+ return certification;
2979
+ }
2980
+ function qeReportCertificationDataToProto(b) {
2981
+ const data = b.slice();
2982
+ const qeReportCertificationData = {};
2983
+ const enclaveReport = enclaveReportToProto(
2984
+ data.slice(enclaveReportStart, enclaveReportEnd)
2985
+ );
2986
+ qeReportCertificationData.qeReport = enclaveReport;
2987
+ qeReportCertificationData.qeReportSignature = data.slice(
2988
+ qeReportCertificationDataSignatureStart,
2989
+ qeReportCertificationDataSignatureEnd
2990
+ );
2991
+ const { authData, authDataSize } = qeAuthDataToProto(
2992
+ data.slice(qeReportCertificationDataAuthDataStart)
2993
+ );
2994
+ qeReportCertificationData.qeAuthData = authData;
2995
+ const pckCertStart = qeReportCertificationDataAuthDataStart + authDataSize;
2996
+ const pckCertChain = pckCertificateChainToProto(data.slice(pckCertStart));
2997
+ qeReportCertificationData.pckCertificateChainData = pckCertChain;
2998
+ checkQeReportCertificationData(qeReportCertificationData);
2999
+ return qeReportCertificationData;
3000
+ }
3001
+ function qeAuthDataToProto(b) {
3002
+ const data = b.slice();
3003
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
3004
+ const parsedDataSize = view.getUint16(authDataParsedDataSizeStart, true);
3005
+ const authDataStart = authDataParsedDataSizeEnd;
3006
+ const authDataEnd = authDataStart + parsedDataSize;
3007
+ const authData = {
3008
+ parsedDataSize,
3009
+ data: data.slice(authDataStart, authDataEnd)
3010
+ };
3011
+ checkQeAuthData(authData);
3012
+ return {
3013
+ authData,
3014
+ authDataSize: authDataEnd
3015
+ };
3016
+ }
3017
+ function quoteToProtoV4(buf) {
3018
+ if (buf.length < 1020) {
3019
+ throw new Error("quote too small - min 0x3fc");
3020
+ }
3021
+ const header = headerToProto(buf.slice(0, 48));
3022
+ const body = tdQuoteBodyToProto(buf.slice(48, 632));
3023
+ const signedDataSize = leU32(buf, 632);
3024
+ const signedDataStart = 636;
3025
+ if (buf.length < signedDataStart + signedDataSize)
3026
+ throw new Error("signedDataSize exceeds buffer");
3027
+ const signedData = signedDataToProto(
3028
+ buf.slice(signedDataStart, signedDataStart + signedDataSize)
3029
+ );
3030
+ const extra = buf.slice(signedDataStart + signedDataSize);
3031
+ return {
3032
+ header,
3033
+ tdQuoteBody: body,
3034
+ signedDataSize,
3035
+ signedData,
3036
+ extraBytes: extra.length ? extra : void 0
3037
+ };
3038
+ }
3039
+ function determineQuoteFormat(buf) {
3040
+ if (buf.length < 2) throw new Error("buffer too small to detect format");
3041
+ return leU16(buf);
3042
+ }
3043
+ function localValidateTdxQuote(quote, opts) {
3044
+ if (!opts) {
3045
+ throw ErrOptionsNil;
3046
+ }
3047
+ if (!quote || quote.tdQuoteBody === void 0) {
3048
+ throw new Error("unsupported quote type");
3049
+ }
3050
+ validateTdxQuoteV4(quote, opts);
3051
+ }
3052
+
3053
+ // src/audit/registry.ts
3054
+ var import_ethers6 = require("ethers");
3055
+ var REGISTRY_ABI = [
3056
+ "function isMeasurementRegistered(bytes32 measurement) view returns (bool)"
3057
+ ];
3058
+ async function isMeasurementRegistered(provider, contractAddress, measurement) {
3059
+ const contract = new import_ethers6.ethers.Contract(contractAddress, REGISTRY_ABI, provider);
3060
+ const measurementHex = import_ethers6.ethers.hexlify(measurement);
3061
+ return contract.isMeasurementRegistered(measurementHex);
3062
+ }
3063
+ async function verifyQuoteRegistered(hashAggregates, ethereumEndpoint, contractAddress) {
3064
+ const provider = new import_ethers6.ethers.JsonRpcProvider(ethereumEndpoint);
3065
+ for (const variant of hashAggregates) {
3066
+ try {
3067
+ if (await isMeasurementRegistered(provider, contractAddress, variant)) {
3068
+ return true;
3069
+ }
3070
+ } catch (err) {
3071
+ throw new Error(
3072
+ `Registry lookup failed for measurement ${import_ethers6.ethers.hexlify(variant)}: ${err.message}`
3073
+ );
3074
+ }
3075
+ }
3076
+ return false;
3077
+ }
3078
+ function toU8Arr(str) {
3079
+ return Uint8Array.from(Buffer.from(str, "hex"));
3080
+ }
3081
+ function aggregateAllMeasurementsPCS(m) {
3082
+ const body = m.body;
3083
+ const exts = m.pckExt;
3084
+ return concatUint8Arrays([
3085
+ body.mrConfigId,
3086
+ body.mrOwner,
3087
+ body.mrOwnerConfig,
3088
+ body.mrSeam,
3089
+ body.mrSignerSeam,
3090
+ body.mrTd,
3091
+ //measurements.ReportData, # differs between quotes
3092
+ body.seamAttributes,
3093
+ body.tdAttributes,
3094
+ //measurements.TeeTcbSvn, # differs between machines and needs to be greater than some value
3095
+ body.xfam,
3096
+ body.rtmrs[0],
3097
+ body.rtmrs[1],
3098
+ body.rtmrs[2],
3099
+ body.rtmrs[3],
3100
+ toU8Arr(exts.fmspc),
3101
+ // Family-Model-Stepping-Platform-Custom SKU
3102
+ toU8Arr(exts.ppid)
3103
+ // specific machine
3104
+ ]);
3105
+ }
3106
+ function aggregateCodeMeasurementsAllPCS(m) {
3107
+ const body = m.body;
3108
+ const exts = m.pckExt;
3109
+ return concatUint8Arrays([
3110
+ body.mrConfigId,
3111
+ body.mrOwner,
3112
+ body.mrOwnerConfig,
3113
+ body.mrSeam,
3114
+ body.mrSignerSeam,
3115
+ body.mrTd,
3116
+ body.rtmrs[0],
3117
+ body.rtmrs[1],
3118
+ body.rtmrs[2],
3119
+ body.rtmrs[3],
3120
+ toU8Arr(exts.fmspc)
3121
+ // Family-Model-Stepping-Platform-Custom SKU
3122
+ ]);
3123
+ }
3124
+ function aggregateCodeMeasurementsTD(m) {
3125
+ const body = m.body;
3126
+ return concatUint8Arrays([
3127
+ body.mrTd,
3128
+ body.rtmrs[0],
3129
+ body.rtmrs[1],
3130
+ body.rtmrs[2],
3131
+ body.rtmrs[3]
3132
+ ]);
3133
+ }
3134
+ function aggregateCodeMeasurementsAll(m) {
3135
+ const body = m.body;
3136
+ return concatUint8Arrays([
3137
+ body.mrConfigId,
3138
+ body.mrOwner,
3139
+ body.mrOwnerConfig,
3140
+ body.mrSeam,
3141
+ body.mrSignerSeam,
3142
+ body.mrTd,
3143
+ ...body.rtmrs
3144
+ ]);
3145
+ }
3146
+ function aggregateAllMeasurements(m) {
3147
+ const body = m.body;
3148
+ return concatUint8Arrays([
3149
+ body.mrConfigId,
3150
+ body.mrOwner,
3151
+ body.mrOwnerConfig,
3152
+ body.mrSeam,
3153
+ body.mrSignerSeam,
3154
+ body.mrTd,
3155
+ body.seamAttributes,
3156
+ body.tdAttributes,
3157
+ body.teeTcbSvn,
3158
+ body.xfam,
3159
+ ...body.rtmrs
3160
+ ]);
3161
+ }
3162
+ function concatUint8Arrays(arrays) {
3163
+ const totalLength = arrays.reduce((sum, a) => sum + a.length, 0);
3164
+ const result = new Uint8Array(totalLength);
3165
+ let offset = 0;
3166
+ for (const arr of arrays) {
3167
+ result.set(arr, offset);
3168
+ offset += arr.length;
3169
+ }
3170
+ return result;
3171
+ }
3172
+ async function hashMeasurements(input) {
3173
+ const first = await sha256(input);
3174
+ const second = await sha256(first);
3175
+ return second;
3176
+ }
3177
+
3178
+ // src/audit/quoteToHash.ts
3179
+ var CHALLENGE_DOMAIN_SEPARATOR = "CUSTOM-RPC ATTEST:";
3180
+ async function parseVerifyRawQuote(raw, rootPem) {
3181
+ const quote = await quoteToProto(raw);
3182
+ if (!quote) {
3183
+ throw new Error("Failed to parse quote");
3184
+ }
3185
+ const rot = {
3186
+ cabundles: [rootPem]
3187
+ };
3188
+ const verifyOptions = await rootOfTrustToVerifyOptions(rot);
3189
+ const exts = await localVerifyTdxQuote(quote, verifyOptions);
3190
+ const val_opts = {
3191
+ // defaults are reasonable
3192
+ headerOptions: {},
3193
+ tdQuoteBodyOptions: {}
3194
+ };
3195
+ localValidateTdxQuote(quote, val_opts);
3196
+ return { body: quote.tdQuoteBody, pckExt: exts };
3197
+ }
3198
+ async function quoteToProto(b) {
3199
+ const quoteFormat = determineQuoteFormat(b);
3200
+ if (!quoteFormat) {
3201
+ throw new Error("Failed to determine quote format");
3202
+ }
3203
+ switch (quoteFormat) {
3204
+ case intelQuoteV4Version:
3205
+ return quoteToProtoV4(b);
3206
+ default:
3207
+ throw new Error("Quote format not supported");
3208
+ }
3209
+ }
3210
+ async function quoteToHash(rawQuote, rootPem) {
3211
+ const tdQuoteBody = await parseVerifyRawQuote(rawQuote, rootPem);
3212
+ if (!tdQuoteBody) {
3213
+ throw new Error("error parsing and verifying TDX quote");
3214
+ }
3215
+ const reportData = tdQuoteBody.body.reportData;
3216
+ const domainSeparator = reportData.slice(0, CHALLENGE_DOMAIN_SEPARATOR.length);
3217
+ const expectedDomain = new TextEncoder().encode(CHALLENGE_DOMAIN_SEPARATOR);
3218
+ if (!equalBytes(domainSeparator, expectedDomain)) {
3219
+ throw new Error("quote constructed for alternate purpose");
3220
+ }
3221
+ return {
3222
+ challenge: reportData.slice(CHALLENGE_DOMAIN_SEPARATOR.length),
3223
+ hashVariants: [
3224
+ await hashMeasurements(
3225
+ aggregateCodeMeasurementsTD(tdQuoteBody)
3226
+ ),
3227
+ await hashMeasurements(
3228
+ aggregateCodeMeasurementsAll(tdQuoteBody)
3229
+ ),
3230
+ await hashMeasurements(
3231
+ aggregateAllMeasurements(tdQuoteBody)
3232
+ ),
3233
+ await hashMeasurements(
3234
+ aggregateAllMeasurementsPCS(tdQuoteBody)
3235
+ ),
3236
+ await hashMeasurements(
3237
+ aggregateCodeMeasurementsAllPCS(tdQuoteBody)
3238
+ )
3239
+ ]
3240
+ };
3241
+ }
3242
+ async function sha256(input) {
3243
+ const hashBuffer = await crypto.subtle.digest("SHA-256", input);
3244
+ return new Uint8Array(hashBuffer);
3245
+ }
3246
+
3247
+ // src/audit/challenge.ts
3248
+ function compressUncompressedSecp256k1(pubUncompressed) {
3249
+ if (pubUncompressed.length !== 65 || pubUncompressed[0] !== 4) {
3250
+ throw new Error(
3251
+ `Unexpected pubkey format/length: ${pubUncompressed.length}`
3252
+ );
3253
+ }
3254
+ const x = pubUncompressed.slice(1, 33);
3255
+ const y = pubUncompressed.slice(33, 65);
3256
+ const prefix = (y[31] & 1) === 0 ? 2 : 3;
3257
+ return Buffer.concat([Buffer.from([prefix]), Buffer.from(x)]);
3258
+ }
3259
+ var DomainSeparatorSign = "CUSTOM-RPC SIGN:";
3260
+ function stringToBytes(str) {
3261
+ let rawQuote;
3262
+ try {
3263
+ rawQuote = import_ethers7.ethers.getBytes("0x" + str);
3264
+ } catch (e) {
3265
+ throw new Error(`Invalid hex: ${e?.message ?? e}`);
3266
+ }
3267
+ return rawQuote;
3268
+ }
3269
+ async function validateTdxAttestation(attestBody, exporterKey, l1RpcUrl, registryContractAddress) {
3270
+ const text = attestBody.toString("utf8").trim();
3271
+ let json;
3272
+ try {
3273
+ json = JSON.parse(text);
3274
+ } catch (e) {
3275
+ throw new Error(`Invalid TDX attestation body JSON: ${e?.message ?? e}`);
3276
+ }
3277
+ const rawQuote = stringToBytes(json.quote);
3278
+ const { challenge, hashVariants } = await quoteToHash(rawQuote, CA_ROOT_PEM);
3279
+ const registered = await verifyQuoteRegistered(
3280
+ hashVariants,
3281
+ l1RpcUrl,
3282
+ registryContractAddress
3283
+ );
3284
+ if (!registered) {
3285
+ throw new Error("TDX measurement not registered in verification registry");
3286
+ }
3287
+ const challengeBuffer = Buffer.from(challenge);
3288
+ if (challengeBuffer.length < 32) {
3289
+ throw new Error(`Report data too short: ${challengeBuffer.length}`);
3290
+ }
3291
+ const pubHashFromQuote = challengeBuffer.subarray(challengeBuffer.length - 32);
3292
+ const pubCompressed = await recoverSignaturePubkey(
3293
+ exporterKey,
3294
+ json.signature
3295
+ );
3296
+ const pubHash = await sha256(pubCompressed);
3297
+ return bytesEqual(pubHashFromQuote, pubHash);
3298
+ }
3299
+ async function recoverSignaturePubkey(message, signatureHex) {
3300
+ const digest = await sha256(
3301
+ Buffer.concat([
3302
+ Buffer.from(DomainSeparatorSign, "utf8"),
3303
+ Buffer.from(message)
3304
+ ])
3305
+ );
3306
+ const sigBytes = stringToBytes(signatureHex);
3307
+ if (sigBytes.length !== 65) {
3308
+ throw new Error(
3309
+ `Invalid signature length: ${sigBytes.length} (expected 65)`
3310
+ );
3311
+ }
3312
+ const sig = Buffer.from(sigBytes);
3313
+ if (sig[64] === 0 || sig[64] === 1) {
3314
+ sig[64] += 27;
3315
+ }
3316
+ let pubUncompressed;
3317
+ try {
3318
+ const pubHex = import_ethers7.ethers.SigningKey.recoverPublicKey(
3319
+ import_ethers7.ethers.hexlify(digest),
3320
+ import_ethers7.ethers.hexlify(sig)
3321
+ );
3322
+ pubUncompressed = import_ethers7.ethers.getBytes(pubHex);
3323
+ } catch (e) {
3324
+ throw new Error(
3325
+ `Failed to recover public key from signature: ${e?.message ?? e}`
3326
+ );
3327
+ }
3328
+ const pubCompressed = compressUncompressedSecp256k1(pubUncompressed);
3329
+ return pubCompressed;
3330
+ }
3331
+ function bytesEqual(a, b) {
3332
+ if (a.length !== b.length) return false;
3333
+ for (let i = 0; i < a.length; i++) {
3334
+ if (a[i] !== b[i]) {
3335
+ return false;
3336
+ }
3337
+ }
3338
+ return true;
3339
+ }
799
3340
  // Annotate the CommonJS export names for ESM import in node:
800
3341
  0 && (module.exports = {
801
3342
  ChainId,
@@ -831,5 +3372,6 @@ var SilentDataRollupContract = class extends import_ethers5.Contract {
831
3372
  getAuthEIP721Types,
832
3373
  getAuthHeaders,
833
3374
  isSignableContractCall,
834
- prepareTypedDataPayload
3375
+ prepareTypedDataPayload,
3376
+ validateTdxAttestation
835
3377
  });