@shard-auth/client
Version:
Next-generation API authentication without secret keys - MPC-based authentication with FROST threshold signatures
139 lines • 6.81 kB
JavaScript
"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