@t2000/sdk 5.17.0 → 5.19.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 CHANGED
@@ -13,6 +13,9 @@ var cryptography = require('@mysten/sui/cryptography');
13
13
  var promises = require('fs/promises');
14
14
  var path = require('path');
15
15
  var os = require('os');
16
+ var secp256k1 = require('@noble/curves/secp256k1');
17
+ var sha256 = require('@noble/hashes/sha256');
18
+ var utils$1 = require('@noble/hashes/utils');
16
19
  var fs = require('fs');
17
20
  var suins = require('@mysten/suins');
18
21
 
@@ -2031,9 +2034,85 @@ async function listModels(opts) {
2031
2034
  }));
2032
2035
  }
2033
2036
  var RECEIPT_ANCHORED_SUFFIX = "::anchor::ReceiptAnchored";
2037
+ function normalizeClaims(claims) {
2038
+ if (!claims) {
2039
+ return [];
2040
+ }
2041
+ if (Array.isArray(claims)) {
2042
+ return claims.filter((c) => c.name).map((c) => ({
2043
+ name: c.name,
2044
+ status: c.status ?? "unknown",
2045
+ source: c.source
2046
+ }));
2047
+ }
2048
+ return Object.entries(claims).map(([name, v]) => ({
2049
+ name,
2050
+ status: v?.status ?? "unknown",
2051
+ source: v?.source
2052
+ }));
2053
+ }
2034
2054
  function fullnodeUrl(network) {
2035
2055
  return network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
2036
2056
  }
2057
+ function jcs(value) {
2058
+ if (value === null) {
2059
+ return "null";
2060
+ }
2061
+ if (typeof value === "boolean") {
2062
+ return value ? "true" : "false";
2063
+ }
2064
+ if (typeof value === "number") {
2065
+ if (!Number.isInteger(value)) {
2066
+ throw new Error("JCS: non-integer number");
2067
+ }
2068
+ return String(value);
2069
+ }
2070
+ if (typeof value === "string") {
2071
+ return JSON.stringify(value);
2072
+ }
2073
+ if (Array.isArray(value)) {
2074
+ return `[${value.map(jcs).join(",")}]`;
2075
+ }
2076
+ const keys = Object.keys(value).sort();
2077
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${jcs(value[k])}`).join(",")}}`;
2078
+ }
2079
+ function verifyReceiptSignature(receipt, signingKeyHex) {
2080
+ try {
2081
+ const sig = receipt.signature;
2082
+ if (sig?.algo !== "ecdsa-secp256k1" || !sig.value) {
2083
+ return false;
2084
+ }
2085
+ const canonical = {
2086
+ api_version: receipt.api_version ?? "",
2087
+ receipt_id: receipt.receipt_id ?? "",
2088
+ chat_id: receipt.chat_id ?? null,
2089
+ workload_id: receipt.workload_id ?? "",
2090
+ workload_keyset_digest: receipt.workload_keyset_digest ?? "",
2091
+ endpoint: receipt.endpoint ?? "",
2092
+ method: receipt.method ?? "",
2093
+ served_at: receipt.served_at ?? 0,
2094
+ event_log: receipt.event_log ?? [],
2095
+ signature: { algo: sig.algo, key_id: sig.key_id ?? "" }
2096
+ };
2097
+ const prehash = sha256.sha256(new TextEncoder().encode(jcs(canonical)));
2098
+ const sigBytes = utils$1.hexToBytes(sig.value);
2099
+ if (sigBytes.length !== 65) {
2100
+ return false;
2101
+ }
2102
+ let v = sigBytes[64];
2103
+ if (v >= 27 && v <= 30) {
2104
+ v -= 27;
2105
+ }
2106
+ if (v > 3) {
2107
+ return false;
2108
+ }
2109
+ const recovered = secp256k1.secp256k1.Signature.fromCompact(sigBytes.slice(0, 64)).addRecoveryBit(v).recoverPublicKey(prehash).toHex(false);
2110
+ const endorsed = utils$1.bytesToHex(utils$1.hexToBytes(signingKeyHex.replace(/^0x/, "")));
2111
+ return recovered.toLowerCase() === endorsed.toLowerCase();
2112
+ } catch {
2113
+ return false;
2114
+ }
2115
+ }
2037
2116
  async function verifyReceipt(receiptId, opts = {}) {
2038
2117
  const base = opts.apiBase ?? DEFAULT_API_BASE;
2039
2118
  const network = opts.network ?? "mainnet";
@@ -2065,6 +2144,7 @@ async function verifyReceipt(receiptId, opts = {}) {
2065
2144
  });
2066
2145
  const upstreamEv = receipt.event_log.find((e) => e.type === "upstream.verified");
2067
2146
  const upstreamOk = upstreamEv?.result === "verified";
2147
+ const claims = normalizeClaims(upstreamEv?.claims);
2068
2148
  checks.push({
2069
2149
  name: "Confidential upstream",
2070
2150
  status: upstreamEv ? upstreamOk ? "pass" : "fail" : "skip",
@@ -2127,23 +2207,50 @@ async function verifyReceipt(receiptId, opts = {}) {
2127
2207
  });
2128
2208
  }
2129
2209
  }
2210
+ let signatureForged = false;
2211
+ let sigStatus = "skip";
2212
+ let sigDetail = "no signature on receipt";
2213
+ if (receipt.signature?.value) {
2214
+ try {
2215
+ const model = opts.model ?? "phala/glm-5.2";
2216
+ const res = await fetch(
2217
+ `${base}/aci/attestation?model=${encodeURIComponent(model)}`
2218
+ );
2219
+ const att = res.ok ? await res.json() : null;
2220
+ if (!att?.signingKey) {
2221
+ sigDetail = "could not fetch the attested keyset to check the signature";
2222
+ } else if (att.workloadId && att.workloadId !== workloadId) {
2223
+ sigDetail = `attested keyset is for a different workload \u2014 pass --model for ${workloadId}`;
2224
+ } else {
2225
+ const ok = verifyReceiptSignature(receipt, att.signingKey);
2226
+ sigStatus = ok ? "pass" : "fail";
2227
+ signatureForged = !ok;
2228
+ sigDetail = ok ? `signed by the attested receipt key (${receipt.signature.key_id ?? "key"})` : "signature does NOT recover the attested receipt key \u2014 forged/altered";
2229
+ }
2230
+ } catch {
2231
+ sigDetail = "signature check errored";
2232
+ }
2233
+ }
2130
2234
  checks.push({
2131
2235
  name: "Receipt signature",
2132
- status: "skip",
2133
- detail: receipt.signature ? "present (local recompute via RedPill keyset canonicalization = roadmap)" : "no signature on receipt",
2134
- trust: "roadmap"
2236
+ status: sigStatus,
2237
+ detail: sigDetail,
2238
+ trust: sigStatus === "skip" ? "roadmap" : "trustless"
2135
2239
  });
2136
2240
  return {
2137
2241
  receiptId,
2138
- verified: Boolean(wireHash && workloadId) && anchorVerified,
2242
+ verified: Boolean(wireHash && workloadId) && anchorVerified && !signatureForged,
2139
2243
  anchorVerified,
2140
2244
  checks,
2141
2245
  wireHash,
2142
2246
  workloadId,
2143
2247
  upstream: upstreamEv ? {
2144
2248
  provider: upstreamEv.provider ?? upstreamEv.upstream_name,
2249
+ modelId: upstreamEv.model_id,
2145
2250
  result: upstreamEv.result,
2146
- tcbStatus: upstreamEv.tcb_status
2251
+ tcbStatus: upstreamEv.tcb_status,
2252
+ sessionId: upstreamEv.session_id,
2253
+ claims: claims.length > 0 ? claims : void 0
2147
2254
  } : void 0,
2148
2255
  anchor
2149
2256
  };