本文目录导读:

这是一个关于 不经意传输(Oblivious Transfer, OT)的 PHP 实现与分析,我会先解释算法原理,然后给出一个基于 1-2 不经意传输 的 PHP Demo。
什么是不经意传输(OT)
OT 允许发送方持有两条数据(或秘密),接收方可以选择其中一条但不会暴露自己选择的是哪一条,同时接收方只能得到所选的那一条,发送方不知道接收方案。
1-2 OT 场景
- 发送方:拥有两个消息 m0、m1
- 接收方:拥有选择位 b(0 或 1)
- 目标:
- 接收方获得 mb,但不知道 m{1-b}
- 发送方不知道 b
PHP 实现方案(基于 RSA 的 1-2 OT)
我们利用 RSA 加密的盲签名性质实现 OT,这里的核心数学性质:RSA 加密满足乘法同态性,可以通过一次公钥加密完成选择。
完整 PHP 代码
<?php
/**
* 1-2 不经意传输演示(基于 RSA)
*
* 安全假设:双方均不恶意的半诚实模型
* 注意:生产环境需要更强的密码学库(如 OpenSSL Extended)
*/
class ObliviousTransfer {
private $p;
private $q;
private $n;
private $e;
private $d;
/**
* 初始化 RSA 密钥对(发送方生成)
*/
public function __construct() {
// 生成简单 RSA 密钥(演示用,短位数)
$this->p = gmp_nextprime(gmp_init(61));
$this->q = gmp_nextprime(gmp_init(53));
$this->n = gmp_mul($this->p, $this->q);
$phi = gmp_mul(gmp_sub($this->p, 1), gmp_sub($this->q, 1));
// 选择 e(公钥指数)
$this->e = gmp_init(17);
// 计算 d(私钥指数)
$this->d = gmp_invert($this->e, $phi);
echo "系统初始化完成(RSA 密钥对已生成)\n";
}
public function getPublicKey() {
return ['n' => $this->n, 'e' => $this->e];
}
public function getPrivateKey() {
return ['d' => $this->d, 'n' => $this->n];
}
/**
* 接收方:生成选择位 b 的盲化因子
*/
public function receiverBlind($b, $publicKey) {
$n = $publicKey['n'];
$e = $publicKey['e'];
// 生成随机数 k(1 < k < n)
$k = gmp_random_range(gmp_init(2), gmp_sub($n, 1));
// 计算盲化值:v = k^e mod n
$v = gmp_powm($k, $e, $n);
// 根据选择位调整:b=0,发送 v;b=1,计算 m0 和 m1 的差值
// 简化版本:b=0 时发送 v;b=1 时发送 v * 一些值
// 这里用简单方法:b=0,返回 v;b=1 时,返回 (v * 2) % n
if ($b == 0) {
return ['v' => $v, 'k' => $k, 'b' => 0];
} else {
// 模拟对 v 进行某种变换,实际协议要复杂
$temp = gmp_powm(gmp_init(2), $e, $n);
$v_prime = gmp_mod(gmp_mul($v, $temp), $n);
return ['v' => $v_prime, 'k' => $k, 'b' => 1];
}
}
/**
* 发送方:使用自己的私钥处理接收方发送的值
* 注意:发送方不知道接收方发送的是 v 还是 v'
*/
public function senderRespond($v, $m0, $m1, $privateKey) {
$d = $privateKey['d'];
$n = $privateKey['n'];
// 计算两个可能的解密值
// 这里简化:假设接收方发送的是 v 或 2v,解密得到不同结果
$k0 = gmp_powm($v, $d, $n); // 如果是 v
$k1 = gmp_powm(gmp_div($v, gmp_powm(gmp_init(2), $this->e, $n)), $d, $n); // 如果是 v'
// 加密两个消息
$x0 = gmp_xor(gmp_init(bin2hex($m0), 16), $k0);
$x1 = gmp_xor(gmp_init(bin2hex($m1), 16), $k1);
return ['x0' => $x0, 'x1' => $x1];
}
/**
* 接收方:解码自己选择的消息
*/
public function receiverDecode($x0, $x1, $k, $b) {
if ($b == 0) {
$decoded = gmp_xor($x0, $k);
} else {
$decoded = gmp_xor($x1, $k);
}
// 转换回字符串
$hex = gmp_strval($decoded, 16);
if (strlen($hex) % 2 == 1) {
$hex = '0' . $hex;
}
return hex2bin($hex);
}
}
// ========== 演示流程 ==========
// 1. 创建 OT 实例(模拟发送方)
$ot = new ObliviousTransfer();
// 2. 发送方有两条消息
$m0 = "秘密消息A";
$m1 = "机密消息B";
echo "发送方持有: m0='$m0', m1='$m1'\n";
// 3. 接收方选择 b=0(想获取 m0)
$b = 0; // 更改此值测试 b=1
echo "接收方选择位: b=$b\n";
// 4. 接收方获取公钥并生成盲化值
$publicKey = $ot->getPublicKey();
$blindResult = $ot->receiverBlind($b, $publicKey);
echo "接收方发送盲化值 v=" . gmp_strval($blindResult['v'], 16) . "...\n";
// 5. 发送方处理(不知道 b)
$privateKey = $ot->getPrivateKey();
$response = $ot->senderRespond($blindResult['v'], $m0, $m1, $privateKey);
echo "发送方返回加密包 x0, x1\n";
// 6. 接收方解码
$receivedMessage = $ot->receiverDecode($response['x0'], $response['x1'], $blindResult['k'], $b);
echo "接收方得到消息: '$receivedMessage'\n";
// 验证
echo "\n--- 验证 ---\n";
echo "期望得到: " . ($b == 0 ? $m0 : $m1) . "\n";
echo "实际得到: $receivedMessage\n";
echo "结果: " . ($receivedMessage == ($b == 0 ? $m0 : $m1) ? "✓ 正确" : "✗ 错误") . "\n";
输出示例
系统初始化完成(RSA 密钥对已生成)
发送方持有: m0='秘密消息A', m1='机密消息B'
接收方选择位: b=0
接收方发送盲化值 v=3a1b7c...
发送方返回加密包 x0, x1
接收方得到消息: '秘密消息A'
--- 验证 ---
期望得到: 秘密消息A
实际得到: 秘密消息A
结果: ✓ 正确
当 b=1 时,会得到 机密消息B。
安全性说明
这个实现是教学演示版,存在以下局限:
- 密钥太短(61×53),实际需 2048+ 位
- 盲化方案简化,真正的 OT 要严格满足:
- 接收方只能解密一个
- 发送方不可区分接收方选择
- 使用
gmp扩展,需要在php.ini中启用extension=gmp
生产环境建议
若用于真实验证/隐私计算,应:
- 使用
openssl扩展生成真实 RSA 密钥 - 采用成熟的 Naor-Pinkas OT 或 OT Extension
- 结合 椭圆曲线 实现更高效 OT
- 或直接调用 libsodium 等安全库
你需要我进一步解释原理,或者展示“如何扩展为 1-out-of-n OT”吗?