UNPKG

@shard-auth/client

Version:

Next-generation API authentication without secret keys - MPC-based authentication with FROST threshold signatures

139 lines 6.81 kB
"use strict"; /** * FROST署名生成と検証 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.generateNonce = generateNonce; exports.computeNonceCommitment = computeNonceCommitment; exports.computeBindingFactor = computeBindingFactor; exports.generateFROSTPartialSignature = generateFROSTPartialSignature; exports.combineFROSTSignatures = combineFROSTSignatures; const secp256k1_1 = require("@noble/curves/secp256k1"); const math_1 = require("./math"); // ノンス生成 function generateNonce() { const order = secp256k1_1.secp256k1.CURVE.n; const hiding = BigInt('0x' + (0, math_1.randomBytes)(32).toString('hex')) % order; const binding = BigInt('0x' + (0, math_1.randomBytes)(32).toString('hex')) % order; return { hiding: Buffer.from(hiding.toString(16).padStart(64, '0'), 'hex'), binding: Buffer.from(binding.toString(16).padStart(64, '0'), 'hex') }; } // ノンスコミットメント計算 function computeNonceCommitment(nonce) { const hidingPoint = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(BigInt('0x' + nonce.hiding.toString('hex'))); const bindingPoint = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(BigInt('0x' + nonce.binding.toString('hex'))); return { hiding: Buffer.from(hidingPoint.toRawBytes(true)), binding: Buffer.from(bindingPoint.toRawBytes(true)) }; } // バインディング係数の計算 function computeBindingFactor(participantIndex, message, commitments) { const inputs = [ Buffer.from([participantIndex]), message ]; // コミットメントを順序付けて追加 const sortedCommitments = Array.from(commitments.entries()).sort((a, b) => a[0] - b[0]); for (const [_, commitment] of sortedCommitments) { inputs.push(commitment.hiding); inputs.push(commitment.binding); } return (0, math_1.hash)('FROST-binding', ...inputs); } // 部分署名の生成 async function generateFROSTPartialSignature(share, message, nonce, commitments, groupPublicKey) { const order = secp256k1_1.secp256k1.CURVE.n; const shareValue = BigInt('0x' + share.share.toString('hex')); const hidingNonce = BigInt('0x' + nonce.hiding.toString('hex')); const bindingNonce = BigInt('0x' + nonce.binding.toString('hex')); // バインディング係数を計算 const bindingFactor = computeBindingFactor(share.index, message, commitments); const bindingFactorValue = BigInt('0x' + bindingFactor.toString('hex')) % order; // グループコミットメントを計算 if (commitments.size === 0) { throw new Error('No commitments provided for signature generation'); } let groupCommitment = secp256k1_1.secp256k1.ProjectivePoint.ZERO; for (const [idx, commitment] of commitments) { const hidingPoint = secp256k1_1.secp256k1.ProjectivePoint.fromHex(commitment.hiding.toString('hex')); const bindingPoint = secp256k1_1.secp256k1.ProjectivePoint.fromHex(commitment.binding.toString('hex')); const combined = hidingPoint.add(bindingPoint.multiply(bindingFactorValue)); groupCommitment = groupCommitment.add(combined); } if (groupCommitment.equals(secp256k1_1.secp256k1.ProjectivePoint.ZERO)) { throw new Error('Group commitment is zero - invalid signature'); } // チャレンジを計算 const challenge = computeChallenge(groupCommitment.toRawBytes(true), groupPublicKey, message); const challengeValue = BigInt('0x' + challenge.toString('hex')) % order; // Lagrange係数を計算 const participantIndices = Array.from(commitments.keys()); const lagrangeCoeff = (0, math_1.computeLagrangeCoefficient)(share.index, participantIndices); // 部分署名を計算 const effectiveNonce = (hidingNonce + bindingNonce * bindingFactorValue) % order; const signature = (effectiveNonce + challengeValue * lagrangeCoeff * shareValue) % order; return { index: share.index, signature: Buffer.from(signature.toString(16).padStart(64, '0'), 'hex'), proof: Buffer.alloc(64), // 簡略化のため、実際のプルーフは省略 nonceCommitment: computeNonceCommitment(nonce) }; } // チャレンジ計算 function computeChallenge(groupCommitment, groupPublicKey, message) { return (0, math_1.hash)('FROST-challenge', Buffer.from(groupCommitment), groupPublicKey, message); } // 署名の結合 async function combineFROSTSignatures(partialSignatures, message, groupPublicKey) { const order = secp256k1_1.secp256k1.CURVE.n; // コミットメントマップを構築 const commitments = new Map(); for (const sig of partialSignatures) { commitments.set(sig.index, sig.nonceCommitment); } // バインディング係数を計算 const bindingFactor = computeBindingFactor(1, message, commitments); const bindingFactorValue = BigInt('0x' + bindingFactor.toString('hex')) % order; // グループコミットメントを計算 let groupCommitment = secp256k1_1.secp256k1.ProjectivePoint.ZERO; for (const [_, commitment] of commitments) { const hidingPoint = secp256k1_1.secp256k1.ProjectivePoint.fromHex(commitment.hiding.toString('hex')); const bindingPoint = secp256k1_1.secp256k1.ProjectivePoint.fromHex(commitment.binding.toString('hex')); const combined = hidingPoint.add(bindingPoint.multiply(bindingFactorValue)); groupCommitment = groupCommitment.add(combined); } // 部分署名を結合 let s = 0n; for (const sig of partialSignatures) { const sigHex = sig.signature.toString('hex'); if (sigHex && sigHex !== '') { s = (s + BigInt('0x' + sigHex)) % order; } } const r = groupCommitment.toRawBytes(true); return { r: Buffer.from(r), s: Buffer.from(s.toString(16).padStart(64, '0'), 'hex'), async verify(message, publicKey) { try { // Schnorr署名検証 const pubKey = secp256k1_1.secp256k1.ProjectivePoint.fromHex(publicKey.toString('hex')); const rPoint = secp256k1_1.secp256k1.ProjectivePoint.fromHex(r); const sValue = BigInt('0x' + s.toString(16)); const challenge = computeChallenge(r, publicKey, message); const challengeValue = BigInt('0x' + challenge.toString('hex')) % order; // s*G = R + c*P を検証 const sG = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(sValue); const rPlusCP = rPoint.add(pubKey.multiply(challengeValue)); return sG.equals(rPlusCP); } catch { return false; } } }; } //# sourceMappingURL=signature.js.map