你的 AI 聊天机器人说了什么?解决两方证明问题的双密钥验证
I’m happy to translate the article for you, but I’ll need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll keep the source link unchanged and translate the rest into Simplified Chinese while preserving all formatting, markdown, and technical terms.
双方证明问题
当你的 AI 聊天机器人告诉用户他们的保险理赔已 批准,而用户随后声称机器人说了不同的话——谁对?
双方都无法提供证据。请求发送给 AI 提供商(例如 OpenAI),响应返回后,双方只能依赖截图和信任。
这就是 双方面证明问题,随着 AI 交互变得更加重要,它只会变得更糟。
介绍 IOProof
IOProof 是一个开源代理,位于您的服务与任何 AI API 之间,捕获并对每一次交互进行加密证明。
其关键创新是 双密钥验证模型——当双方意见不一致时,使其发挥作用的机制。
IOProof 的功能
- 捕获 请求和响应的原始字节。
- 使用 SHA‑256 对所有内容进行哈希。
- 生成两个独立的加密密钥(所有者密钥和用户密钥)。
- 盲化 证明并将其批量化为 Merkle 树。
- 将 Merkle 根提交至 Solana。
哈希核心(刻意简化)
const crypto = require('crypto');
function sha256(buffer) {
return crypto.createHash('sha256').update(buffer).digest('hex');
}
function buildCombinedHash(requestHash, responseHash, timestamp) {
const payload = `${requestHash}|${responseHash}|${timestamp}`;
return sha256(Buffer.from(payload, 'utf-8'));
}
组合哈希将请求、响应和时间戳绑定在一起。任意位置的单字节更改都会产生完全不同的哈希——教材式的内容可寻址完整性。
双密钥生成
IOProof 的第一个版本在每个证明中只生成 一个 密钥,这导致了信任瓶颈。
解决办法是在创建证明时生成 两个 独立的密钥:
function generateSecret() {
return crypto.randomBytes(32).toString('hex');
}
// In the proxy route:
const secret = generateSecret(); // 所有者密钥(供 API 调用者使用)
const userSecret = generateSecret(); // 用户密钥(供最终用户使用)
const blindedHash = blindHash(combinedHash, secret);
两个密钥都能解锁 相同的证明细节(完整的请求/响应负载、所有哈希、Merkle 证明、Solana 交易)。它们通过 不同的密码学机制 进行验证,这一点很重要。
所有者密钥 – 通过盲化验证
function blindHash(combinedHash, secret) {
return sha256(Buffer.from(`${combinedHash}|${secret}`, 'utf-8'));
}
在验证时,服务器会使用提供的密钥重新计算盲化哈希。
如果 SHA‑256(combined_hash | secret) === stored_blinded_hash,则所有者通过身份验证。无需存储密钥——只需数学运算。
用户密钥 – 通过恒定时间比较验证
function safeEqual(a, b) {
if (!a || !b || a.length !== b.length) return false;
return crypto.timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));
}
为什么使用 timingSafeEqual?
普通的 === 比较在第一个不同的字节处就会短路,从而泄露前缀匹配的字符数量(经典的时序侧信道)。crypto.timingSafeEqual 始终耗时相同,防止攻击者逐字节暴力破解密钥。
验证端点逻辑
let secretValid = false;
let accessType = null;
// Owner: re‑derive the blinded hash
const expectedBlinded = blindHash(proof.combinedHash, secret);
if (expectedBlinded === proof.blindedHash) {
secretValid = true;
accessType = 'owner';
}
// User: constant‑time comparison against stored secret
if (!secretValid && proof.userSecret && safeEqual(secret, proof.userSecret)) {
secretValid = true;
accessType = 'user';
}
// Response includes which party succeeded
res.json({ secretValid, access_type: accessType, proof });
双方都收到 相同的证明数据,但彼此之间无需相互信任或协同访问。
Merkle‑Tree 批处理(节省链上费用)
为每个 API 调用写一条 Solana 交易既昂贵又慢。
相反,IOProof 将待处理的证明批量 放入 Merkle 树,只提交根哈希:
function buildMerkleTree(leaves) {
if (leaves.length === 0) return { root: null, layers: [] };
if (leaves.length === 1) return { root: leaves[0], layers: [leaves] };
const layers = [leaves.slice()];
let currentLayer = leaves.slice();
while (currentLayer.length > 1) {
const nextLayer = [];
for (let i = 0; i < currentLayer.length; i += 2) {
const left = currentLayer[i];
const right = currentLayer[i + 1] || left;
nextLayer.push(sha256(Buffer.from(left + right, 'utf-8')));
}
layers.push(nextLayer);
currentLayer = nextLayer;
}
return { root: currentLayer[0], layers };
}
免费托管层
- 配额: 每月 100 条证明
- 注册:
安装
npm install ioproof # v0.2.0
- 整个代理以单个 Node.js 进程运行。
- 将现有的 API 调用指向它,所有交互即可由双方独立验证。
- 代码采用 MIT 许可证——阅读、分叉、挑漏洞,随意。
为什么使用 IOProof?
如果你正在构建任何 AI 输出会产生现实后果的系统——客服、医疗分诊、金融建议、法律研究——问题不在于是否需要审计轨迹,而在于是否双方都能在不相互信任的前提下证明发生了什么。