@shard-auth/client
Version:
Next-generation API authentication without secret keys - MPC-based authentication with FROST threshold signatures
83 lines • 3.7 kB
JavaScript
;
/**
* API用のFROST署名実装
* 協調署名のための部分署名生成と結合
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAPIPartialSignature = createAPIPartialSignature;
exports.combineAPIPartialSignatures = combineAPIPartialSignatures;
exports.verifyAPISignature = verifyAPISignature;
const secp256k1_1 = require("@noble/curves/secp256k1");
const sha256_1 = require("@noble/hashes/sha256");
const order = secp256k1_1.secp256k1.CURVE.n;
// API用の部分署名生成
async function createAPIPartialSignature(share, message, nonce, otherNonceCommitment, groupPublicKey) {
// 自分のノンスコミットメントを計算
const ownNonceCommitment = computeNonceCommitmentFromNonce(nonce);
// グループコミットメントを計算(R = R1 + R2)
const R1 = secp256k1_1.secp256k1.ProjectivePoint.fromHex(share.index === 1 ? ownNonceCommitment.hiding : otherNonceCommitment.hiding);
const R2 = secp256k1_1.secp256k1.ProjectivePoint.fromHex(share.index === 2 ? ownNonceCommitment.hiding : otherNonceCommitment.hiding);
const groupCommitment = R1.add(R2);
const groupCommitmentBytes = groupCommitment.toRawBytes(true);
// チャレンジを計算 e = H(R || P || m)
const challenge = (0, sha256_1.sha256)(Buffer.concat([
groupCommitmentBytes,
groupPublicKey,
message
]));
const e = BigInt('0x' + Buffer.from(challenge).toString('hex')) % order;
// 部分署名を計算 si = ri + e * xi
const ri = BigInt('0x' + nonce.hiding.toString('hex'));
const xi = BigInt('0x' + share.share.toString('hex'));
const si = (ri + e * xi) % order;
return {
partialSignature: {
index: share.index,
signature: Buffer.from(si.toString(16).padStart(64, '0'), 'hex'),
proof: Buffer.alloc(64), // 簡略化
nonceCommitment: share.index === 1 ? ownNonceCommitment : otherNonceCommitment
},
groupCommitment: Buffer.from(groupCommitmentBytes)
};
}
// 部分署名の結合
async function combineAPIPartialSignatures(partialSig1, partialSig2, groupCommitment) {
// s = s1 + s2
const s1 = BigInt('0x' + partialSig1.signature.toString('hex'));
const s2 = BigInt('0x' + partialSig2.signature.toString('hex'));
const s = (s1 + s2) % order;
return {
r: groupCommitment,
s: Buffer.from(s.toString(16).padStart(64, '0'), 'hex'),
async verify(message, publicKey) {
return verifyAPISignature(groupCommitment, s, message, publicKey);
}
};
}
// 署名検証
function verifyAPISignature(r, s, message, publicKey) {
try {
const R = secp256k1_1.secp256k1.ProjectivePoint.fromHex(r);
const P = secp256k1_1.secp256k1.ProjectivePoint.fromHex(publicKey);
// e = H(R || P || m)
const challenge = (0, sha256_1.sha256)(Buffer.concat([r, publicKey, message]));
const e = BigInt('0x' + Buffer.from(challenge).toString('hex')) % order;
// 検証: s*G = R + e*P
const sG = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(s);
const RplusEP = R.add(P.multiply(e));
return sG.equals(RplusEP);
}
catch {
return false;
}
}
// ノンスからコミットメントを計算
function computeNonceCommitmentFromNonce(nonce) {
const hidingPoint = secp256k1_1.secp256k1.ProjectivePoint.BASE.multiply(BigInt('0x' + nonce.hiding.toString('hex')));
// bindingは簡略化のため省略
return {
hiding: Buffer.from(hidingPoint.toRawBytes(true)),
binding: Buffer.alloc(33) // ダミー
};
}
//# sourceMappingURL=frost-api-signature.js.map