@t2000/sdk 5.17.0 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +90 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +90 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.d.cts
CHANGED
|
@@ -97,6 +97,9 @@ interface VerifyOptions {
|
|
|
97
97
|
apiBase?: string;
|
|
98
98
|
/** Sui network for the trustless anchor read (default `mainnet`). */
|
|
99
99
|
network?: 'mainnet' | 'testnet';
|
|
100
|
+
/** Confidential model id for fetching the attested receipt-signing key
|
|
101
|
+
* (default `phala/glm-5.2`; the gateway key is workload-wide). */
|
|
102
|
+
model?: string;
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
105
|
* Verify a confidential response by its receipt id. Reads the signed receipt
|
package/dist/index.d.ts
CHANGED
|
@@ -97,6 +97,9 @@ interface VerifyOptions {
|
|
|
97
97
|
apiBase?: string;
|
|
98
98
|
/** Sui network for the trustless anchor read (default `mainnet`). */
|
|
99
99
|
network?: 'mainnet' | 'testnet';
|
|
100
|
+
/** Confidential model id for fetching the attested receipt-signing key
|
|
101
|
+
* (default `phala/glm-5.2`; the gateway key is workload-wide). */
|
|
102
|
+
model?: string;
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
105
|
* Verify a confidential response by its receipt id. Reads the signed receipt
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,9 @@ import { decodeSuiPrivateKey } from '@mysten/sui/cryptography';
|
|
|
11
11
|
import { access, mkdir, writeFile, readFile } from 'fs/promises';
|
|
12
12
|
import { join, dirname, resolve } from 'path';
|
|
13
13
|
import { homedir } from 'os';
|
|
14
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
15
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
16
|
+
import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
|
14
17
|
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
15
18
|
import { SuinsTransaction } from '@mysten/suins';
|
|
16
19
|
|
|
@@ -2028,6 +2031,65 @@ var RECEIPT_ANCHORED_SUFFIX = "::anchor::ReceiptAnchored";
|
|
|
2028
2031
|
function fullnodeUrl(network) {
|
|
2029
2032
|
return network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
2030
2033
|
}
|
|
2034
|
+
function jcs(value) {
|
|
2035
|
+
if (value === null) {
|
|
2036
|
+
return "null";
|
|
2037
|
+
}
|
|
2038
|
+
if (typeof value === "boolean") {
|
|
2039
|
+
return value ? "true" : "false";
|
|
2040
|
+
}
|
|
2041
|
+
if (typeof value === "number") {
|
|
2042
|
+
if (!Number.isInteger(value)) {
|
|
2043
|
+
throw new Error("JCS: non-integer number");
|
|
2044
|
+
}
|
|
2045
|
+
return String(value);
|
|
2046
|
+
}
|
|
2047
|
+
if (typeof value === "string") {
|
|
2048
|
+
return JSON.stringify(value);
|
|
2049
|
+
}
|
|
2050
|
+
if (Array.isArray(value)) {
|
|
2051
|
+
return `[${value.map(jcs).join(",")}]`;
|
|
2052
|
+
}
|
|
2053
|
+
const keys = Object.keys(value).sort();
|
|
2054
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(value[k])}`).join(",")}}`;
|
|
2055
|
+
}
|
|
2056
|
+
function verifyReceiptSignature(receipt, signingKeyHex) {
|
|
2057
|
+
try {
|
|
2058
|
+
const sig = receipt.signature;
|
|
2059
|
+
if (sig?.algo !== "ecdsa-secp256k1" || !sig.value) {
|
|
2060
|
+
return false;
|
|
2061
|
+
}
|
|
2062
|
+
const canonical = {
|
|
2063
|
+
api_version: receipt.api_version ?? "",
|
|
2064
|
+
receipt_id: receipt.receipt_id ?? "",
|
|
2065
|
+
chat_id: receipt.chat_id ?? null,
|
|
2066
|
+
workload_id: receipt.workload_id ?? "",
|
|
2067
|
+
workload_keyset_digest: receipt.workload_keyset_digest ?? "",
|
|
2068
|
+
endpoint: receipt.endpoint ?? "",
|
|
2069
|
+
method: receipt.method ?? "",
|
|
2070
|
+
served_at: receipt.served_at ?? 0,
|
|
2071
|
+
event_log: receipt.event_log ?? [],
|
|
2072
|
+
signature: { algo: sig.algo, key_id: sig.key_id ?? "" }
|
|
2073
|
+
};
|
|
2074
|
+
const prehash = sha256(new TextEncoder().encode(jcs(canonical)));
|
|
2075
|
+
const sigBytes = hexToBytes(sig.value);
|
|
2076
|
+
if (sigBytes.length !== 65) {
|
|
2077
|
+
return false;
|
|
2078
|
+
}
|
|
2079
|
+
let v = sigBytes[64];
|
|
2080
|
+
if (v >= 27 && v <= 30) {
|
|
2081
|
+
v -= 27;
|
|
2082
|
+
}
|
|
2083
|
+
if (v > 3) {
|
|
2084
|
+
return false;
|
|
2085
|
+
}
|
|
2086
|
+
const recovered = secp256k1.Signature.fromCompact(sigBytes.slice(0, 64)).addRecoveryBit(v).recoverPublicKey(prehash).toHex(false);
|
|
2087
|
+
const endorsed = bytesToHex(hexToBytes(signingKeyHex.replace(/^0x/, "")));
|
|
2088
|
+
return recovered.toLowerCase() === endorsed.toLowerCase();
|
|
2089
|
+
} catch {
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2031
2093
|
async function verifyReceipt(receiptId, opts = {}) {
|
|
2032
2094
|
const base = opts.apiBase ?? DEFAULT_API_BASE;
|
|
2033
2095
|
const network = opts.network ?? "mainnet";
|
|
@@ -2121,15 +2183,39 @@ async function verifyReceipt(receiptId, opts = {}) {
|
|
|
2121
2183
|
});
|
|
2122
2184
|
}
|
|
2123
2185
|
}
|
|
2186
|
+
let signatureForged = false;
|
|
2187
|
+
let sigStatus = "skip";
|
|
2188
|
+
let sigDetail = "no signature on receipt";
|
|
2189
|
+
if (receipt.signature?.value) {
|
|
2190
|
+
try {
|
|
2191
|
+
const model = opts.model ?? "phala/glm-5.2";
|
|
2192
|
+
const res = await fetch(
|
|
2193
|
+
`${base}/aci/attestation?model=${encodeURIComponent(model)}`
|
|
2194
|
+
);
|
|
2195
|
+
const att = res.ok ? await res.json() : null;
|
|
2196
|
+
if (!att?.signingKey) {
|
|
2197
|
+
sigDetail = "could not fetch the attested keyset to check the signature";
|
|
2198
|
+
} else if (att.workloadId && att.workloadId !== workloadId) {
|
|
2199
|
+
sigDetail = `attested keyset is for a different workload \u2014 pass --model for ${workloadId}`;
|
|
2200
|
+
} else {
|
|
2201
|
+
const ok = verifyReceiptSignature(receipt, att.signingKey);
|
|
2202
|
+
sigStatus = ok ? "pass" : "fail";
|
|
2203
|
+
signatureForged = !ok;
|
|
2204
|
+
sigDetail = ok ? `signed by the attested receipt key (${receipt.signature.key_id ?? "key"})` : "signature does NOT recover the attested receipt key \u2014 forged/altered";
|
|
2205
|
+
}
|
|
2206
|
+
} catch {
|
|
2207
|
+
sigDetail = "signature check errored";
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2124
2210
|
checks.push({
|
|
2125
2211
|
name: "Receipt signature",
|
|
2126
|
-
status:
|
|
2127
|
-
detail:
|
|
2128
|
-
trust: "roadmap"
|
|
2212
|
+
status: sigStatus,
|
|
2213
|
+
detail: sigDetail,
|
|
2214
|
+
trust: sigStatus === "skip" ? "roadmap" : "trustless"
|
|
2129
2215
|
});
|
|
2130
2216
|
return {
|
|
2131
2217
|
receiptId,
|
|
2132
|
-
verified: Boolean(wireHash && workloadId) && anchorVerified,
|
|
2218
|
+
verified: Boolean(wireHash && workloadId) && anchorVerified && !signatureForged,
|
|
2133
2219
|
anchorVerified,
|
|
2134
2220
|
checks,
|
|
2135
2221
|
wireHash,
|