@phala/dstack-sdk
Version:
89 lines (85 loc) • 3.08 kB
JavaScript
;
var sha3 = require('@noble/hashes/sha3');
var secp256k1 = require('@noble/curves/secp256k1');
// src/verify-env-encrypt-public-key.ts
var DEFAULT_MAX_AGE_SECONDS = 300;
function bigintToBeBytes(value, length) {
const bytes = new Uint8Array(length);
for (let i = length - 1; i >= 0; i--) {
bytes[i] = Number(value & 0xffn);
value >>= 8n;
}
return bytes;
}
function hexToBytes(hex) {
if (hex.startsWith("0x") || hex.startsWith("0X")) hex = hex.slice(2);
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(hex)) return null;
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
}
return bytes;
}
function bytesToHex(bytes) {
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}
function concat(...parts) {
const total = parts.reduce((n, p) => n + p.length, 0);
const out = new Uint8Array(total);
let offset = 0;
for (const part of parts) {
out.set(part, offset);
offset += part.length;
}
return out;
}
function recoverSigner(messageHash, signature) {
try {
const sigBytes = signature.slice(0, 64);
const recovery = signature[64];
const recoveredPubKey = secp256k1.secp256k1.Signature.fromCompact(sigBytes).addRecoveryBit(recovery).recoverPublicKey(messageHash);
return "0x" + bytesToHex(recoveredPubKey.toRawBytes(true));
} catch (error) {
console.error("signature verification failed:", error);
return null;
}
}
function verifyEnvEncryptPublicKey(publicKey, signature, appId, timestamp, options) {
if (signature.length !== 65) return null;
const ts = typeof timestamp === "bigint" ? timestamp : BigInt(timestamp);
const maxAge = options?.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
const now = BigInt(Math.floor(Date.now() / 1e3));
const age = now - ts;
if (age < -60n) {
console.error("timestamp is too far in the future");
return null;
}
if (age > BigInt(maxAge)) {
console.error(`timestamp is too old: ${age}s > ${maxAge}s`);
return null;
}
const appIdBytes = hexToBytes(appId);
if (!appIdBytes) return null;
const prefix = new TextEncoder().encode("dstack-env-encrypt-pubkey");
const separator = new TextEncoder().encode(":");
const timestampBytes = bigintToBeBytes(ts, 8);
const message = concat(
prefix,
separator,
appIdBytes,
timestampBytes,
publicKey
);
return recoverSigner(sha3.keccak_256(message), signature);
}
function verifyEnvEncryptPublicKeyLegacy(publicKey, signature, appId) {
if (signature.length !== 65) return null;
const appIdBytes = hexToBytes(appId);
if (!appIdBytes) return null;
const prefix = new TextEncoder().encode("dstack-env-encrypt-pubkey");
const separator = new TextEncoder().encode(":");
const message = concat(prefix, separator, appIdBytes, publicKey);
return recoverSigner(sha3.keccak_256(message), signature);
}
exports.verifyEnvEncryptPublicKey = verifyEnvEncryptPublicKey;
exports.verifyEnvEncryptPublicKeyLegacy = verifyEnvEncryptPublicKeyLegacy;