@reclaimprotocol/attestor-core 5.1.1 → 5.1.4
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/LICENSE +660 -660
- package/README.md +48 -22
- package/browser/resources/attestor-browser.min.mjs +32 -32
- package/lib/external-rpc/index.js +68 -29
- package/lib/index.js +68 -29
- package/lib/proto/tee-bundle.d.ts +45 -0
- package/lib/server/tee/acme-http-server.d.ts +13 -0
- package/lib/server/tee/attestation-generate.d.ts +29 -0
- package/lib/server/tee/bootstrap.d.ts +11 -0
- package/lib/server/tee/cert-manager.d.ts +24 -0
- package/lib/server/tee/cloud-logging.d.ts +23 -0
- package/lib/server/tee/secret-loader.d.ts +10 -0
- package/lib/server/tee/secret-manager.d.ts +3 -0
- package/lib/server/utils/sev-snp/gcp.d.ts +2 -0
- package/lib/server/utils/sev-snp/nitrotpm.d.ts +1 -0
- package/lib/server/utils/sev-snp/secure-boot.d.ts +9 -0
- package/lib/server/utils/sev-snp/secure-boot.test.d.ts +1 -0
- package/lib/server/utils/sev-snp/verify.d.ts +19 -0
- package/lib/server/utils/tee-cbc-contract.d.ts +3 -0
- package/lib/server/utils/tee-oprf-mpc-verification.d.ts +1 -0
- package/lib/server/utils/tee-verification.d.ts +3 -0
- package/lib/utils/gcp-attestation.d.ts +23 -0
- package/package.json +3 -3
|
@@ -8807,13 +8807,14 @@ function parseHttpResponse(buff) {
|
|
|
8807
8807
|
var RE2;
|
|
8808
8808
|
import("re2").then((m) => {
|
|
8809
8809
|
RE2 = m.default;
|
|
8810
|
-
}).catch(() => {
|
|
8810
|
+
}).catch((err) => {
|
|
8811
|
+
logger.warn({ err }, "Failed to load RE2; falling back to native RegExp");
|
|
8811
8812
|
});
|
|
8812
8813
|
function makeRegex(str) {
|
|
8813
8814
|
if (RE2) {
|
|
8814
8815
|
return RE2(str, "sgiu");
|
|
8815
8816
|
}
|
|
8816
|
-
return new RegExp(str, "
|
|
8817
|
+
return new RegExp(str, "sgi");
|
|
8817
8818
|
}
|
|
8818
8819
|
var TEMPLATE_START_CHARCODE = "{".charCodeAt(0);
|
|
8819
8820
|
var TEMPLATE_END_CHARCODE = "}".charCodeAt(0);
|
|
@@ -9038,6 +9039,9 @@ var HTTP_PROVIDER = {
|
|
|
9038
9039
|
if (res.headerIndices["date"]) {
|
|
9039
9040
|
reveals.push(res.headerIndices["date"]);
|
|
9040
9041
|
}
|
|
9042
|
+
if (res.headerIndices["content-type"]) {
|
|
9043
|
+
reveals.push(res.headerIndices["content-type"]);
|
|
9044
|
+
}
|
|
9041
9045
|
if (revealFraming && res.headerIndices["transfer-encoding"]) {
|
|
9042
9046
|
reveals.push(res.headerIndices["transfer-encoding"]);
|
|
9043
9047
|
}
|
|
@@ -9129,36 +9133,53 @@ var HTTP_PROVIDER = {
|
|
|
9129
9133
|
const firstRealIdx = allServerBlocks.findIndex((b) => !b.every((x) => x === REDACTION_CHAR_CODE2));
|
|
9130
9134
|
const serverBlocks = firstRealIdx === -1 ? [] : allServerBlocks.slice(firstRealIdx);
|
|
9131
9135
|
const response = concatArrays(...serverBlocks);
|
|
9132
|
-
let
|
|
9133
|
-
|
|
9136
|
+
let bodyStart;
|
|
9137
|
+
if (shouldRevealCrlf(ctx)) {
|
|
9138
|
+
const responseAfterStatus = response.slice(OK_HTTP_HEADER.length);
|
|
9139
|
+
const separatorIdx = findIndexInUint8Array(
|
|
9140
|
+
responseAfterStatus,
|
|
9141
|
+
strToUint8Array("\r\n\r\n")
|
|
9142
|
+
);
|
|
9143
|
+
if (separatorIdx === -1) {
|
|
9144
|
+
throw new Error("Response body start not found");
|
|
9145
|
+
}
|
|
9146
|
+
bodyStart = OK_HTTP_HEADER.length + separatorIdx + 4;
|
|
9147
|
+
} else {
|
|
9148
|
+
bodyStart = OK_HTTP_HEADER.length;
|
|
9149
|
+
}
|
|
9150
|
+
const headersText = uint8ArrayToStr(response.slice(0, bodyStart));
|
|
9134
9151
|
const okRegex = makeRegex("^HTTP\\/1.1 2\\d{2}");
|
|
9135
|
-
const matchRes = okRegex.exec(
|
|
9152
|
+
const matchRes = okRegex.exec(headersText);
|
|
9136
9153
|
if (!matchRes) {
|
|
9137
9154
|
const statusRegex = makeRegex("^HTTP\\/1.1 (\\d{3})");
|
|
9138
|
-
const matchRes2 = statusRegex.exec(
|
|
9155
|
+
const matchRes2 = statusRegex.exec(headersText);
|
|
9139
9156
|
if (matchRes2 && matchRes2.length > 1) {
|
|
9140
9157
|
throw new Error(`Provider returned error ${matchRes2[1]}`);
|
|
9141
9158
|
}
|
|
9142
|
-
let lineEnd =
|
|
9159
|
+
let lineEnd = headersText.indexOf("*");
|
|
9143
9160
|
if (lineEnd === -1) {
|
|
9144
|
-
lineEnd =
|
|
9161
|
+
lineEnd = headersText.indexOf("\n");
|
|
9145
9162
|
}
|
|
9146
9163
|
if (lineEnd === -1) {
|
|
9147
9164
|
lineEnd = OK_HTTP_HEADER.length;
|
|
9148
9165
|
}
|
|
9149
9166
|
throw new Error(
|
|
9150
|
-
`Response did not start with "HTTP/1.1 2XX" got "${
|
|
9167
|
+
`Response did not start with "HTTP/1.1 2XX" got "${headersText.slice(0, lineEnd)}"`
|
|
9151
9168
|
);
|
|
9152
9169
|
}
|
|
9153
|
-
|
|
9154
|
-
if (
|
|
9155
|
-
|
|
9156
|
-
|
|
9157
|
-
|
|
9170
|
+
const paramBody = params.body instanceof Uint8Array ? params.body : strToUint8Array(params.body || "");
|
|
9171
|
+
if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
|
|
9172
|
+
throw new Error("request body mismatch");
|
|
9173
|
+
}
|
|
9174
|
+
let body = response.slice(bodyStart);
|
|
9175
|
+
if (shouldRevealChunkFraming(clientVersion)) {
|
|
9176
|
+
if (/transfer-encoding:\s*chunked/i.test(headersText)) {
|
|
9177
|
+
body = dechunkResponseBody(body);
|
|
9158
9178
|
}
|
|
9159
|
-
} else {
|
|
9160
|
-
bodyStart = OK_HTTP_HEADER.length;
|
|
9161
9179
|
}
|
|
9180
|
+
const charset = shouldRevealCrlf(ctx) ? getResponseBodyCharset(headersText) : void 0;
|
|
9181
|
+
const bodyText = decodeResponseBody(body, charset);
|
|
9182
|
+
let res = headersText + bodyText;
|
|
9162
9183
|
const dateHeader = makeRegex(dateHeaderRegex).exec(res);
|
|
9163
9184
|
if (dateHeader && dateHeader.length > 1) {
|
|
9164
9185
|
const serverDate = new Date(dateHeader[1]);
|
|
@@ -9166,14 +9187,7 @@ var HTTP_PROVIDER = {
|
|
|
9166
9187
|
logger2.info({ dateHeader: dateHeader[0], current: Date.now() }, "date header is off");
|
|
9167
9188
|
}
|
|
9168
9189
|
}
|
|
9169
|
-
|
|
9170
|
-
if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
|
|
9171
|
-
throw new Error("request body mismatch");
|
|
9172
|
-
}
|
|
9173
|
-
if (shouldRevealChunkFraming(clientVersion)) {
|
|
9174
|
-
const headersStr = res.slice(0, bodyStart);
|
|
9175
|
-
res = /transfer-encoding:\s*chunked/i.test(headersStr) ? headersStr + dechunkResponseBody(res.slice(bodyStart)) : res;
|
|
9176
|
-
} else if (!secretParams) {
|
|
9190
|
+
if (!shouldRevealChunkFraming(clientVersion) && !secretParams) {
|
|
9177
9191
|
res = res.slice(bodyStart).replace(/\*{3,}/g, "");
|
|
9178
9192
|
}
|
|
9179
9193
|
for (const { type, value, invert } of params.responseMatches || []) {
|
|
@@ -9264,12 +9278,35 @@ function shouldRevealCrlf({ version }) {
|
|
|
9264
9278
|
function shouldRevealChunkFraming(version) {
|
|
9265
9279
|
return version >= AttestorVersion.ATTESTOR_VERSION_3_2_0;
|
|
9266
9280
|
}
|
|
9281
|
+
function getResponseBodyCharset(headers) {
|
|
9282
|
+
const contentType = /(?:^|[\r\n*])content-type\s*:\s*([^*\r\n]*)/i.exec(headers)?.[1];
|
|
9283
|
+
if (typeof contentType === "undefined") {
|
|
9284
|
+
return void 0;
|
|
9285
|
+
}
|
|
9286
|
+
const charsetMatch = /(?:^|;)\s*charset\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^;\s]*))/i.exec(contentType);
|
|
9287
|
+
if (!charsetMatch) {
|
|
9288
|
+
return void 0;
|
|
9289
|
+
}
|
|
9290
|
+
const charset = charsetMatch[1]?.replace(/\\(.)/g, "$1") ?? charsetMatch[2];
|
|
9291
|
+
return charset.trim() || void 0;
|
|
9292
|
+
}
|
|
9293
|
+
function decodeResponseBody(body, charset) {
|
|
9294
|
+
try {
|
|
9295
|
+
return new TextDecoder(charset ?? "utf-8").decode(body);
|
|
9296
|
+
} catch (error) {
|
|
9297
|
+
if (charset) {
|
|
9298
|
+
throw new Error(`Unsupported response charset "${charset}"`, { cause: error });
|
|
9299
|
+
}
|
|
9300
|
+
throw error;
|
|
9301
|
+
}
|
|
9302
|
+
}
|
|
9267
9303
|
function dechunkResponseBody(body) {
|
|
9268
9304
|
const parser = makeHttpResponseParser();
|
|
9269
|
-
parser.onChunk(
|
|
9270
|
-
strToUint8Array("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
|
|
9271
|
-
|
|
9272
|
-
|
|
9305
|
+
parser.onChunk(concatenateUint8Arrays7([
|
|
9306
|
+
strToUint8Array("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"),
|
|
9307
|
+
body
|
|
9308
|
+
]));
|
|
9309
|
+
return parser.res.body ?? new Uint8Array();
|
|
9273
9310
|
}
|
|
9274
9311
|
function getHostPort(params, secretParams) {
|
|
9275
9312
|
const { host } = new URL(getURL(params, secretParams));
|
|
@@ -9955,6 +9992,7 @@ async function _createClaimOnAttestor({
|
|
|
9955
9992
|
block,
|
|
9956
9993
|
redactedPlaintext,
|
|
9957
9994
|
overshotToprfFromPrevBlock,
|
|
9995
|
+
overshotOprfRawFromPrevBlock,
|
|
9958
9996
|
toprfs,
|
|
9959
9997
|
oprfRawMarkers
|
|
9960
9998
|
} = reveal;
|
|
@@ -9963,7 +10001,8 @@ async function _createClaimOnAttestor({
|
|
|
9963
10001
|
redactedPlaintext,
|
|
9964
10002
|
toprfs,
|
|
9965
10003
|
oprfRawMarkers,
|
|
9966
|
-
overshotToprfFromPrevBlock
|
|
10004
|
+
overshotToprfFromPrevBlock,
|
|
10005
|
+
overshotOprfRawFromPrevBlock
|
|
9967
10006
|
});
|
|
9968
10007
|
revealedPackets.push(
|
|
9969
10008
|
{ sender: "server", message: redactedPlaintext }
|
package/lib/index.js
CHANGED
|
@@ -7124,13 +7124,14 @@ function parseHttpResponse(buff) {
|
|
|
7124
7124
|
var RE2;
|
|
7125
7125
|
import("re2").then((m) => {
|
|
7126
7126
|
RE2 = m.default;
|
|
7127
|
-
}).catch(() => {
|
|
7127
|
+
}).catch((err) => {
|
|
7128
|
+
logger.warn({ err }, "Failed to load RE2; falling back to native RegExp");
|
|
7128
7129
|
});
|
|
7129
7130
|
function makeRegex(str) {
|
|
7130
7131
|
if (RE2) {
|
|
7131
7132
|
return RE2(str, "sgiu");
|
|
7132
7133
|
}
|
|
7133
|
-
return new RegExp(str, "
|
|
7134
|
+
return new RegExp(str, "sgi");
|
|
7134
7135
|
}
|
|
7135
7136
|
var TEMPLATE_START_CHARCODE = "{".charCodeAt(0);
|
|
7136
7137
|
var TEMPLATE_END_CHARCODE = "}".charCodeAt(0);
|
|
@@ -7336,6 +7337,9 @@ var HTTP_PROVIDER = {
|
|
|
7336
7337
|
if (res.headerIndices["date"]) {
|
|
7337
7338
|
reveals.push(res.headerIndices["date"]);
|
|
7338
7339
|
}
|
|
7340
|
+
if (res.headerIndices["content-type"]) {
|
|
7341
|
+
reveals.push(res.headerIndices["content-type"]);
|
|
7342
|
+
}
|
|
7339
7343
|
if (revealFraming && res.headerIndices["transfer-encoding"]) {
|
|
7340
7344
|
reveals.push(res.headerIndices["transfer-encoding"]);
|
|
7341
7345
|
}
|
|
@@ -7427,36 +7431,53 @@ var HTTP_PROVIDER = {
|
|
|
7427
7431
|
const firstRealIdx = allServerBlocks.findIndex((b) => !b.every((x) => x === REDACTION_CHAR_CODE2));
|
|
7428
7432
|
const serverBlocks = firstRealIdx === -1 ? [] : allServerBlocks.slice(firstRealIdx);
|
|
7429
7433
|
const response = concatArrays(...serverBlocks);
|
|
7430
|
-
let
|
|
7431
|
-
|
|
7434
|
+
let bodyStart;
|
|
7435
|
+
if (shouldRevealCrlf(ctx)) {
|
|
7436
|
+
const responseAfterStatus = response.slice(OK_HTTP_HEADER.length);
|
|
7437
|
+
const separatorIdx = findIndexInUint8Array(
|
|
7438
|
+
responseAfterStatus,
|
|
7439
|
+
strToUint8Array("\r\n\r\n")
|
|
7440
|
+
);
|
|
7441
|
+
if (separatorIdx === -1) {
|
|
7442
|
+
throw new Error("Response body start not found");
|
|
7443
|
+
}
|
|
7444
|
+
bodyStart = OK_HTTP_HEADER.length + separatorIdx + 4;
|
|
7445
|
+
} else {
|
|
7446
|
+
bodyStart = OK_HTTP_HEADER.length;
|
|
7447
|
+
}
|
|
7448
|
+
const headersText = uint8ArrayToStr(response.slice(0, bodyStart));
|
|
7432
7449
|
const okRegex = makeRegex("^HTTP\\/1.1 2\\d{2}");
|
|
7433
|
-
const matchRes = okRegex.exec(
|
|
7450
|
+
const matchRes = okRegex.exec(headersText);
|
|
7434
7451
|
if (!matchRes) {
|
|
7435
7452
|
const statusRegex = makeRegex("^HTTP\\/1.1 (\\d{3})");
|
|
7436
|
-
const matchRes2 = statusRegex.exec(
|
|
7453
|
+
const matchRes2 = statusRegex.exec(headersText);
|
|
7437
7454
|
if (matchRes2 && matchRes2.length > 1) {
|
|
7438
7455
|
throw new Error(`Provider returned error ${matchRes2[1]}`);
|
|
7439
7456
|
}
|
|
7440
|
-
let lineEnd =
|
|
7457
|
+
let lineEnd = headersText.indexOf("*");
|
|
7441
7458
|
if (lineEnd === -1) {
|
|
7442
|
-
lineEnd =
|
|
7459
|
+
lineEnd = headersText.indexOf("\n");
|
|
7443
7460
|
}
|
|
7444
7461
|
if (lineEnd === -1) {
|
|
7445
7462
|
lineEnd = OK_HTTP_HEADER.length;
|
|
7446
7463
|
}
|
|
7447
7464
|
throw new Error(
|
|
7448
|
-
`Response did not start with "HTTP/1.1 2XX" got "${
|
|
7465
|
+
`Response did not start with "HTTP/1.1 2XX" got "${headersText.slice(0, lineEnd)}"`
|
|
7449
7466
|
);
|
|
7450
7467
|
}
|
|
7451
|
-
|
|
7452
|
-
if (
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7468
|
+
const paramBody = params.body instanceof Uint8Array ? params.body : strToUint8Array(params.body || "");
|
|
7469
|
+
if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
|
|
7470
|
+
throw new Error("request body mismatch");
|
|
7471
|
+
}
|
|
7472
|
+
let body = response.slice(bodyStart);
|
|
7473
|
+
if (shouldRevealChunkFraming(clientVersion)) {
|
|
7474
|
+
if (/transfer-encoding:\s*chunked/i.test(headersText)) {
|
|
7475
|
+
body = dechunkResponseBody(body);
|
|
7456
7476
|
}
|
|
7457
|
-
} else {
|
|
7458
|
-
bodyStart = OK_HTTP_HEADER.length;
|
|
7459
7477
|
}
|
|
7478
|
+
const charset = shouldRevealCrlf(ctx) ? getResponseBodyCharset(headersText) : void 0;
|
|
7479
|
+
const bodyText = decodeResponseBody(body, charset);
|
|
7480
|
+
let res = headersText + bodyText;
|
|
7460
7481
|
const dateHeader = makeRegex(dateHeaderRegex).exec(res);
|
|
7461
7482
|
if (dateHeader && dateHeader.length > 1) {
|
|
7462
7483
|
const serverDate = new Date(dateHeader[1]);
|
|
@@ -7464,14 +7485,7 @@ var HTTP_PROVIDER = {
|
|
|
7464
7485
|
logger2.info({ dateHeader: dateHeader[0], current: Date.now() }, "date header is off");
|
|
7465
7486
|
}
|
|
7466
7487
|
}
|
|
7467
|
-
|
|
7468
|
-
if (paramBody.length > 0 && !matchRedactedStrings(paramBody, req.body)) {
|
|
7469
|
-
throw new Error("request body mismatch");
|
|
7470
|
-
}
|
|
7471
|
-
if (shouldRevealChunkFraming(clientVersion)) {
|
|
7472
|
-
const headersStr = res.slice(0, bodyStart);
|
|
7473
|
-
res = /transfer-encoding:\s*chunked/i.test(headersStr) ? headersStr + dechunkResponseBody(res.slice(bodyStart)) : res;
|
|
7474
|
-
} else if (!secretParams) {
|
|
7488
|
+
if (!shouldRevealChunkFraming(clientVersion) && !secretParams) {
|
|
7475
7489
|
res = res.slice(bodyStart).replace(/\*{3,}/g, "");
|
|
7476
7490
|
}
|
|
7477
7491
|
for (const { type, value, invert } of params.responseMatches || []) {
|
|
@@ -7562,12 +7576,35 @@ function shouldRevealCrlf({ version }) {
|
|
|
7562
7576
|
function shouldRevealChunkFraming(version) {
|
|
7563
7577
|
return version >= AttestorVersion.ATTESTOR_VERSION_3_2_0;
|
|
7564
7578
|
}
|
|
7579
|
+
function getResponseBodyCharset(headers) {
|
|
7580
|
+
const contentType = /(?:^|[\r\n*])content-type\s*:\s*([^*\r\n]*)/i.exec(headers)?.[1];
|
|
7581
|
+
if (typeof contentType === "undefined") {
|
|
7582
|
+
return void 0;
|
|
7583
|
+
}
|
|
7584
|
+
const charsetMatch = /(?:^|;)\s*charset\s*=\s*(?:"((?:[^"\\]|\\.)*)"|([^;\s]*))/i.exec(contentType);
|
|
7585
|
+
if (!charsetMatch) {
|
|
7586
|
+
return void 0;
|
|
7587
|
+
}
|
|
7588
|
+
const charset = charsetMatch[1]?.replace(/\\(.)/g, "$1") ?? charsetMatch[2];
|
|
7589
|
+
return charset.trim() || void 0;
|
|
7590
|
+
}
|
|
7591
|
+
function decodeResponseBody(body, charset) {
|
|
7592
|
+
try {
|
|
7593
|
+
return new TextDecoder(charset ?? "utf-8").decode(body);
|
|
7594
|
+
} catch (error) {
|
|
7595
|
+
if (charset) {
|
|
7596
|
+
throw new Error(`Unsupported response charset "${charset}"`, { cause: error });
|
|
7597
|
+
}
|
|
7598
|
+
throw error;
|
|
7599
|
+
}
|
|
7600
|
+
}
|
|
7565
7601
|
function dechunkResponseBody(body) {
|
|
7566
7602
|
const parser = makeHttpResponseParser();
|
|
7567
|
-
parser.onChunk(
|
|
7568
|
-
strToUint8Array("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
|
|
7569
|
-
|
|
7570
|
-
|
|
7603
|
+
parser.onChunk(concatenateUint8Arrays6([
|
|
7604
|
+
strToUint8Array("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"),
|
|
7605
|
+
body
|
|
7606
|
+
]));
|
|
7607
|
+
return parser.res.body ?? new Uint8Array();
|
|
7571
7608
|
}
|
|
7572
7609
|
function getHostPort(params, secretParams) {
|
|
7573
7610
|
const { host } = new URL(getURL(params, secretParams));
|
|
@@ -8454,6 +8491,7 @@ async function _createClaimOnAttestor({
|
|
|
8454
8491
|
block,
|
|
8455
8492
|
redactedPlaintext,
|
|
8456
8493
|
overshotToprfFromPrevBlock,
|
|
8494
|
+
overshotOprfRawFromPrevBlock,
|
|
8457
8495
|
toprfs,
|
|
8458
8496
|
oprfRawMarkers
|
|
8459
8497
|
} = reveal;
|
|
@@ -8462,7 +8500,8 @@ async function _createClaimOnAttestor({
|
|
|
8462
8500
|
redactedPlaintext,
|
|
8463
8501
|
toprfs,
|
|
8464
8502
|
oprfRawMarkers,
|
|
8465
|
-
overshotToprfFromPrevBlock
|
|
8503
|
+
overshotToprfFromPrevBlock,
|
|
8504
|
+
overshotOprfRawFromPrevBlock
|
|
8466
8505
|
});
|
|
8467
8506
|
revealedPackets.push(
|
|
8468
8507
|
{ sender: "server", message: redactedPlaintext }
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
|
|
2
2
|
export declare const protobufPackage = "teeproto";
|
|
3
|
+
export declare const TLS12CBCRecordMode: {
|
|
4
|
+
readonly TLS12_CBC_RECORD_MODE_UNSPECIFIED: 0;
|
|
5
|
+
readonly TLS12_CBC_RECORD_MODE_MAC_THEN_ENCRYPT: 1;
|
|
6
|
+
readonly TLS12_CBC_RECORD_MODE_ENCRYPT_THEN_MAC: 2;
|
|
7
|
+
readonly UNRECOGNIZED: -1;
|
|
8
|
+
};
|
|
9
|
+
export type TLS12CBCRecordMode = typeof TLS12CBCRecordMode[keyof typeof TLS12CBCRecordMode];
|
|
10
|
+
export declare namespace TLS12CBCRecordMode {
|
|
11
|
+
type TLS12_CBC_RECORD_MODE_UNSPECIFIED = typeof TLS12CBCRecordMode.TLS12_CBC_RECORD_MODE_UNSPECIFIED;
|
|
12
|
+
type TLS12_CBC_RECORD_MODE_MAC_THEN_ENCRYPT = typeof TLS12CBCRecordMode.TLS12_CBC_RECORD_MODE_MAC_THEN_ENCRYPT;
|
|
13
|
+
type TLS12_CBC_RECORD_MODE_ENCRYPT_THEN_MAC = typeof TLS12CBCRecordMode.TLS12_CBC_RECORD_MODE_ENCRYPT_THEN_MAC;
|
|
14
|
+
type UNRECOGNIZED = typeof TLS12CBCRecordMode.UNRECOGNIZED;
|
|
15
|
+
}
|
|
16
|
+
export declare function tLS12CBCRecordModeFromJSON(object: any): TLS12CBCRecordMode;
|
|
17
|
+
export declare function tLS12CBCRecordModeToJSON(object: TLS12CBCRecordMode): string;
|
|
3
18
|
/** Signature wrapper used everywhere */
|
|
4
19
|
export declare const BodyType: {
|
|
5
20
|
readonly BODY_TYPE_UNSPECIFIED: 0;
|
|
@@ -27,6 +42,13 @@ export interface ResponseRedactionRange {
|
|
|
27
42
|
start: number;
|
|
28
43
|
length: number;
|
|
29
44
|
}
|
|
45
|
+
export interface TLS12CBCSessionBinding {
|
|
46
|
+
contractVersion: number;
|
|
47
|
+
cipherSuite: number;
|
|
48
|
+
recordMode: TLS12CBCRecordMode;
|
|
49
|
+
extendedMasterSecret: boolean;
|
|
50
|
+
sessionBinding: Uint8Array;
|
|
51
|
+
}
|
|
30
52
|
export interface SignedRedactedDecryptionStream {
|
|
31
53
|
redactedStream: Uint8Array;
|
|
32
54
|
seqNum: number;
|
|
@@ -79,6 +101,9 @@ export interface KOutputPayload {
|
|
|
79
101
|
sessionId: string;
|
|
80
102
|
/** MPC OPRF outputs (TEE-to-TEE computation) - field 10 to match reclaim-tee */
|
|
81
103
|
oprfOutputs: OPRFOutput[];
|
|
104
|
+
/** Signed TEE attestation generation */
|
|
105
|
+
attestationType: string;
|
|
106
|
+
tls12Cbc: TLS12CBCKOutput | undefined;
|
|
82
107
|
}
|
|
83
108
|
export interface TOutputPayload {
|
|
84
109
|
/** NEW: Single consolidated ciphertext instead of individual packets */
|
|
@@ -91,6 +116,23 @@ export interface TOutputPayload {
|
|
|
91
116
|
sessionId: string;
|
|
92
117
|
/** MPC OPRF outputs (TEE-to-TEE computation) - field 10 to match reclaim-tee */
|
|
93
118
|
oprfOutputs: OPRFOutput[];
|
|
119
|
+
/** Signed TEE attestation generation */
|
|
120
|
+
attestationType: string;
|
|
121
|
+
tls12Cbc: TLS12CBCTOutput | undefined;
|
|
122
|
+
}
|
|
123
|
+
export interface TLS12CBCKOutput {
|
|
124
|
+
binding: TLS12CBCSessionBinding | undefined;
|
|
125
|
+
authenticatedRedactedRequest: Uint8Array;
|
|
126
|
+
requestRecordsSha256: Uint8Array;
|
|
127
|
+
requestRedactionRanges: RequestRedactionRange[];
|
|
128
|
+
}
|
|
129
|
+
export interface TLS12CBCTOutput {
|
|
130
|
+
binding: TLS12CBCSessionBinding | undefined;
|
|
131
|
+
authenticatedRedactedResponse: Uint8Array;
|
|
132
|
+
responseRecordsSha256: Uint8Array;
|
|
133
|
+
responseRedactionRanges: ResponseRedactionRange[];
|
|
134
|
+
plaintextRecordLengths: number[];
|
|
135
|
+
closeNotify: boolean;
|
|
94
136
|
}
|
|
95
137
|
/** Attestation report with structured data */
|
|
96
138
|
export interface AttestationReport {
|
|
@@ -130,6 +172,7 @@ export interface VerificationBundle {
|
|
|
130
172
|
}
|
|
131
173
|
export declare const RequestRedactionRange: MessageFns<RequestRedactionRange>;
|
|
132
174
|
export declare const ResponseRedactionRange: MessageFns<ResponseRedactionRange>;
|
|
175
|
+
export declare const TLS12CBCSessionBinding: MessageFns<TLS12CBCSessionBinding>;
|
|
133
176
|
export declare const SignedRedactedDecryptionStream: MessageFns<SignedRedactedDecryptionStream>;
|
|
134
177
|
export declare const CertificateInfo: MessageFns<CertificateInfo>;
|
|
135
178
|
export declare const HandshakeSecrets: MessageFns<HandshakeSecrets>;
|
|
@@ -137,6 +180,8 @@ export declare const Opening: MessageFns<Opening>;
|
|
|
137
180
|
export declare const OPRFOutput: MessageFns<OPRFOutput>;
|
|
138
181
|
export declare const KOutputPayload: MessageFns<KOutputPayload>;
|
|
139
182
|
export declare const TOutputPayload: MessageFns<TOutputPayload>;
|
|
183
|
+
export declare const TLS12CBCKOutput: MessageFns<TLS12CBCKOutput>;
|
|
184
|
+
export declare const TLS12CBCTOutput: MessageFns<TLS12CBCTOutput>;
|
|
140
185
|
export declare const AttestationReport: MessageFns<AttestationReport>;
|
|
141
186
|
export declare const SignedMessage: MessageFns<SignedMessage>;
|
|
142
187
|
export declare const OPRFVerificationData: MessageFns<OPRFVerificationData>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ephemeral HTTP server that answers ACME HTTP-01 challenges. Started right
|
|
3
|
+
* before an order is placed and stopped as soon as the order is finalized,
|
|
4
|
+
* so the attestor does not keep port 80 bound during normal operation.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AcmeChallengeServer {
|
|
7
|
+
private readonly tokens;
|
|
8
|
+
private server?;
|
|
9
|
+
add(token: string, keyAuthorization: string): void;
|
|
10
|
+
remove(token: string): void;
|
|
11
|
+
start(port: number): Promise<void>;
|
|
12
|
+
stop(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AttestationReport } from '../../proto/tee-bundle.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Requests a custom attestation token from the Confidential Space launcher
|
|
4
|
+
* over the unix domain socket. Mirrors reclaim-tee's shared/gcp_attestation.go.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateAttestationJwt(nonces: string[]): Promise<Uint8Array>;
|
|
7
|
+
export interface AttestationContext {
|
|
8
|
+
attestorAddress: string;
|
|
9
|
+
tlsCertSha256Hex: () => string | undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Starts the background attestation refresh loop. The first attestation is
|
|
13
|
+
* generated synchronously so getCachedAttestationJwt() is ready by the time
|
|
14
|
+
* the server begins handling claims.
|
|
15
|
+
*/
|
|
16
|
+
export declare function startAttestationRefresh(ctx: AttestationContext): Promise<void>;
|
|
17
|
+
export declare function stopAttestationRefresh(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the currently-cached attestation JWT bytes, or undefined if no
|
|
20
|
+
* attestation has been generated yet or the cache has expired without a
|
|
21
|
+
* successful refresh.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getCachedAttestationJwt(): Uint8Array | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Returns an AttestationReport ready to embed in a claim response, or
|
|
26
|
+
* undefined when no attestation is available (i.e. attestor is not running
|
|
27
|
+
* inside a TEE, or the refresh loop has not produced one yet).
|
|
28
|
+
*/
|
|
29
|
+
export declare function makeClaimAttestation(): AttestationReport | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brings the attestor up in TEE mode:
|
|
3
|
+
* 1. Pull signing/OPRF secrets from GCP Secret Manager into process.env.
|
|
4
|
+
* 2. Load (or obtain via ACME) the TLS cert and start the renewal loop.
|
|
5
|
+
* 3. Start the attestation refresh loop, with the public key + cert hash
|
|
6
|
+
* as nonces.
|
|
7
|
+
*
|
|
8
|
+
* Must run before #src/server/index.ts is imported, since modules in that
|
|
9
|
+
* tree read PRIVATE_KEY at module load.
|
|
10
|
+
*/
|
|
11
|
+
export declare function bootstrapTee(): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import tls from 'tls';
|
|
2
|
+
export interface CertManagerConfig {
|
|
3
|
+
projectId: string;
|
|
4
|
+
domain: string;
|
|
5
|
+
email: string;
|
|
6
|
+
directoryUrl: string;
|
|
7
|
+
httpChallengePort: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ActiveCertificate {
|
|
10
|
+
certPem: string;
|
|
11
|
+
keyPem: string;
|
|
12
|
+
notAfter: Date;
|
|
13
|
+
sha256Hex: string;
|
|
14
|
+
secureContext: tls.SecureContext;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Bootstraps the TLS certificate. Tries Secret Manager first; if absent or
|
|
18
|
+
* expiring within the renewal window, runs ACME against the configured
|
|
19
|
+
* directory URL and persists the result.
|
|
20
|
+
*/
|
|
21
|
+
export declare function bootstrapCertificate(cfg: CertManagerConfig): Promise<ActiveCertificate>;
|
|
22
|
+
export declare function startRenewalLoop(cfg: CertManagerConfig): void;
|
|
23
|
+
export declare function stopRenewalLoop(): void;
|
|
24
|
+
export declare function getActiveCertificate(): ActiveCertificate | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LogLevel } from '../../types/index.ts';
|
|
2
|
+
interface CloudLoggingOptions {
|
|
3
|
+
projectId: string;
|
|
4
|
+
logName: string;
|
|
5
|
+
level?: LogLevel;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Replaces the default pino logger with one that forwards every log line
|
|
9
|
+
* to GCP Cloud Logging under the given log name. Idempotent.
|
|
10
|
+
*
|
|
11
|
+
* Probes the Cloud Logging client first by writing a no-op entry; if
|
|
12
|
+
* authentication or transport fails, leaves the default stdout logger in
|
|
13
|
+
* place rather than crashing the process. On Confidential Space VMs the
|
|
14
|
+
* launcher's `tee-container-log-redirect` ships stdout to Cloud Logging
|
|
15
|
+
* anyway, so the worst case is logs appear under
|
|
16
|
+
* `confidential-space-launcher` rather than the configured `logName`.
|
|
17
|
+
*
|
|
18
|
+
* We also install a process-wide `unhandledRejection` filter that
|
|
19
|
+
* swallows errors originating in `@google-cloud/logging`, since the SDK
|
|
20
|
+
* has internal lazy gRPC init that escapes our local `.catch()`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function installCloudLogging(opts: CloudLoggingOptions): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches the attestor's signing key and OPRF key material from GCP
|
|
3
|
+
* Secret Manager and writes them into process.env, so that the rest of
|
|
4
|
+
* the server (which reads these via getEnvVariable at module load) sees
|
|
5
|
+
* them as if they had been set in the environment.
|
|
6
|
+
*
|
|
7
|
+
* Must be called before any module that reads PRIVATE_KEY / TOPRF_* is
|
|
8
|
+
* imported, otherwise the reads happen before the values are populated.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadSecretsIntoEnv(projectId: string): Promise<void>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function accessLatestSecret(projectId: string, secretId: string): Promise<Uint8Array>;
|
|
2
|
+
export declare function createSecretIfNotExists(projectId: string, secretId: string): Promise<void>;
|
|
3
|
+
export declare function addSecretVersion(projectId: string, secretId: string, payload: Uint8Array): Promise<void>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Bank = 'sha256' | 'sha384';
|
|
2
|
+
export interface SecureBootEventLogResult {
|
|
3
|
+
eventLogBytes: number;
|
|
4
|
+
verifiedEvents: number;
|
|
5
|
+
postSeparatorUses: number;
|
|
6
|
+
releasePublicKeySha256: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function verifySecureBootEventLog(raw: Uint8Array, quotedPcrs: Map<number, Buffer>, bank: Bank): SecureBootEventLogResult;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const SEV_TAG_GCP = 1;
|
|
10
10
|
export declare const SEV_TAG_AWS = 2;
|
|
11
|
+
export declare const SECURE_BOOT_TAG_GCP = 3;
|
|
12
|
+
export declare const SECURE_BOOT_TAG_AWS = 4;
|
|
11
13
|
export declare const SNP_APP_PREFIX = "snp-app:";
|
|
12
14
|
export declare const SNP_BASE_PREFIX = "snp-base:";
|
|
13
15
|
export declare const APP_PCR = 8;
|
|
@@ -17,6 +19,8 @@ export interface SevSnpEnvelope {
|
|
|
17
19
|
tpm?: Uint8Array;
|
|
18
20
|
nitrotpm?: Uint8Array;
|
|
19
21
|
sev?: Uint8Array;
|
|
22
|
+
sev2?: Uint8Array;
|
|
23
|
+
eventlog?: Uint8Array;
|
|
20
24
|
nonces?: string[];
|
|
21
25
|
}
|
|
22
26
|
export interface SevSnpResult {
|
|
@@ -37,6 +41,10 @@ export declare function parseSevSnpEnvelope(att: Uint8Array): Promise<{
|
|
|
37
41
|
* what makes the carried nonces unforgeable.
|
|
38
42
|
*/
|
|
39
43
|
export declare function snpNonceCommitment(nonces: string[]): Buffer;
|
|
44
|
+
/** Commits the AMD report to the exact NitroTPM evidence from the same broker. */
|
|
45
|
+
export declare function awsCombinedV2ReportData(bound: Uint8Array, appHash: Uint8Array, nitroTpm: Uint8Array): Buffer;
|
|
46
|
+
/** Only an absent value or an explicit 0 keeps the AWS expansion path active. */
|
|
47
|
+
export declare function requireAwsAttestationV2(value?: string | undefined): boolean;
|
|
40
48
|
/**
|
|
41
49
|
* expectedPCR8: the value the loader produces by extending a pristine (all-zero)
|
|
42
50
|
* PCR 8 once with alg(appHash), i.e. alg(0^algSize || alg(appHash)). GCP uses the
|
|
@@ -57,3 +65,14 @@ export declare function extractTeeKeyFromNonces(nonces: string[]): {
|
|
|
57
65
|
* `now` defaults to the real clock; tests may pass a time in the leaf window.
|
|
58
66
|
*/
|
|
59
67
|
export declare function verifyCombinedSevSnp(att: Uint8Array, now?: Date): Promise<SevSnpResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Verifies the additive Secure Boot claim format. The cloud-specific leg is
|
|
70
|
+
* unchanged from SEV2; only after it succeeds do we replay PCR 4/7/11 and pin R.
|
|
71
|
+
*/
|
|
72
|
+
export declare function verifyCombinedSecureBoot(att: Uint8Array, now?: Date): Promise<SevSnpResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Restores the distinct Secure Boot tag on legacy-compatible client evidence.
|
|
75
|
+
* Call this only when an authenticated protocol value requires Secure Boot;
|
|
76
|
+
* the legacy tag alone is intentionally not a generation signal.
|
|
77
|
+
*/
|
|
78
|
+
export declare function secureBootAttestationFromCompatibleWire(att: Uint8Array): Buffer;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { KOutputPayload, TOutputPayload, VerificationBundle } from '../../proto/tee-bundle.ts';
|
|
2
|
+
export type TeeProtocolMode = 'split-aead' | 'tls12-cbc';
|
|
3
|
+
export declare function validateTeeTranscriptContract(bundle: VerificationBundle, kPayload: KOutputPayload, tPayload: TOutputPayload): TeeProtocolMode;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import type { KOutputPayload, TOutputPayload } from '../../proto/tee-bundle.ts';
|
|
10
10
|
import type { OprfVerificationResult } from '../../server/utils/tee-oprf-verification.ts';
|
|
11
11
|
import type { Logger } from '../../types/general.ts';
|
|
12
|
+
export declare const MAX_TEE_OPRF_MPC_OUTPUTS = 20;
|
|
12
13
|
/**
|
|
13
14
|
* Verifies OPRF MPC outputs from TEE_K and TEE_T match
|
|
14
15
|
* Returns verified outputs for transcript replacement (same format as ZK OPRF)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { SignedMessage } from '../../proto/tee-bundle.ts';
|
|
6
6
|
import { KOutputPayload, TOutputPayload } from '../../proto/tee-bundle.ts';
|
|
7
|
+
import type { TeeProtocolMode } from '../../server/utils/tee-cbc-contract.ts';
|
|
7
8
|
import type { Logger } from '../../types/general.ts';
|
|
8
9
|
export interface TeeBundleData {
|
|
9
10
|
teekSigned: SignedMessage;
|
|
@@ -13,6 +14,7 @@ export interface TeeBundleData {
|
|
|
13
14
|
teekPcr0: string;
|
|
14
15
|
teetPcr0: string;
|
|
15
16
|
teeSessionId: string;
|
|
17
|
+
protocolMode: TeeProtocolMode;
|
|
16
18
|
}
|
|
17
19
|
export interface TeeSignatureVerificationResult {
|
|
18
20
|
isValid: boolean;
|
|
@@ -26,3 +28,4 @@ export interface TeeSignatureVerificationResult {
|
|
|
26
28
|
* @returns Validated TEE bundle data
|
|
27
29
|
*/
|
|
28
30
|
export declare function verifyTeeBundle(bundleBytes: Uint8Array, logger: Logger): Promise<TeeBundleData>;
|
|
31
|
+
export declare function resolveSignedAttestationType(signedType: string, reportType: string): string;
|