UNPKG

@phala/dstack-sdk

Version:
89 lines (85 loc) 3.08 kB
'use strict'; 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;