UNPKG

@shard-auth/client

Version:

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

195 lines 7.65 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SignatureAPIServer = void 0; const signature_1 = require("../frost/signature"); const frost_api_signature_1 = require("./frost-api-signature"); const crypto_1 = __importDefault(require("crypto")); class SignatureAPIServer { sessionManager; serverShare; constructor(sessionManager, serverShare) { this.sessionManager = sessionManager; this.serverShare = serverShare; } async handleSessionStart(request) { // リクエスト検証 this.validateSessionRequest(request); // タイムスタンプ検証 this.validateTimestamp(request.timestamp); // サーバー側のノンス生成 const serverNonce = (0, signature_1.generateNonce)(); const serverNonceCommitment = (0, signature_1.computeNonceCommitment)(serverNonce); // セッション作成 const session = this.sessionManager.createSession({ method: request.method, path: request.path, bodyHash: request.bodyHash, timestamp: request.timestamp }, request.clientNonceCommitment, serverNonce, serverNonceCommitment); return { sessionId: session.sessionId, serverNonceCommitment: serverNonceCommitment, expiresAt: session.expiresAt.toISOString() }; } async handleCompleteSignature(request, publicKey) { // セッション検証 const session = this.sessionManager.getSession(request.sessionId); if (!session) { throw new Error('Session not found'); } // 期限切れチェック if (new Date() > session.expiresAt) { throw new Error('Session expired'); } if (session.used) { throw new Error('Session already used'); } // メッセージ生成 const message = this.createSigningMessage(session.requestDetails); // サーバー側の部分署名を生成 const { partialSignature: serverPartialSig, groupCommitment } = await (0, frost_api_signature_1.createAPIPartialSignature)(this.serverShare, message, session.serverNonce, session.clientNonceCommitment, publicKey); // クライアントとサーバーの部分署名を結合 const signature = await (0, frost_api_signature_1.combineAPIPartialSignatures)(request.partialSignature, serverPartialSig, groupCommitment); // セッションを使用済みにマーク this.sessionManager.markSessionUsed(session.sessionId); // Authorizationヘッダー生成 const authHeader = this.createAuthorizationHeader(publicKey, signature, session.requestDetails.timestamp); return { signature: { r: signature.r.toString('base64'), s: signature.s.toString('base64') }, authorizationHeader: authHeader }; } async verifySignature(authorizationHeader, requestDetails) { try { // ヘッダーパース const parsed = this.parseAuthorizationHeader(authorizationHeader); if (!parsed) { return { isValid: false, error: 'Invalid authorization header format' }; } // タイムスタンプ検証 try { this.validateTimestamp(parsed.timestamp); } catch (error) { return { isValid: false, error: 'Timestamp expired' }; } // メッセージ生成 const message = this.createSigningMessage({ ...requestDetails, timestamp: parsed.timestamp }); // 署名検証 const publicKey = Buffer.from(parsed.publicKey, 'base64'); const r = Buffer.from(parsed.signature.r, 'base64'); const s = Buffer.from(parsed.signature.s, 'base64'); // FROSTSignatureオブジェクトとして検証 const signature = { r, s, async verify(msg, pubKey) { // frost-api-signatureの検証ロジックを使用 const { verifyAPISignature } = require('./frost-api-signature'); const sBigInt = BigInt('0x' + s.toString('hex')); return verifyAPISignature(r, sBigInt, msg, pubKey); } }; const isValid = await signature.verify(message, publicKey); return { isValid, publicKey: parsed.publicKey, error: isValid ? undefined : 'Invalid signature' }; } catch (error) { return { isValid: false, error: 'Verification error: ' + error.message }; } } validateSessionRequest(request) { if (!request.method || !request.path || !request.bodyHash || !request.timestamp) { throw new Error('Invalid request'); } if (!request.clientNonceCommitment) { throw new Error('Client nonce commitment required'); } } validateTimestamp(timestamp) { const requestTime = new Date(timestamp).getTime(); const now = Date.now(); const fiveMinutes = 5 * 60 * 1000; if (now - requestTime > fiveMinutes) { throw new Error('Timestamp too old'); } } createSigningMessage(details) { const parts = [ details.method, details.path, details.bodyHash.toString('base64'), details.timestamp ]; const combined = parts.join('|'); return crypto_1.default.createHash('sha256').update(combined).digest(); } createAuthorizationHeader(publicKey, signature, timestamp) { const pk = publicKey.toString('base64'); const sig = `${signature.r.toString('base64')}.${signature.s.toString('base64')}`; return `ShardAuth pk=${pk},sig=${sig},ts=${timestamp}`; } parseAuthorizationHeader(header) { if (!header || !header.startsWith('ShardAuth ')) { return null; } const parts = header.substring(10).split(','); if (parts.length !== 3) { return null; } const result = {}; for (const part of parts) { const [key, value] = part.split('='); if (!key || !value) return null; switch (key) { case 'pk': result.publicKey = value; break; case 'sig': const [r, s] = value.split('.'); if (!r || !s) return null; result.signature = { r, s }; break; case 'ts': result.timestamp = value; break; default: return null; } } if (!result.publicKey || !result.signature || !result.timestamp) { return null; } return result; } // テスト用メソッド getEndpoint(path) { return `/api/v1${path}`; } } exports.SignatureAPIServer = SignatureAPIServer; //# sourceMappingURL=signature-server.js.map