不经意传输是考虑这样一个场景,A拥有两个数据 m 0 m_0 m0和 m 1 m_1 m1,B想要从A中获取一个数据,但是,不想A知道B到底获取了什么。比如,A是一个云服务器,提供存储服务,B是一个用户。用户想要从服务器上获取一些资源,但是,出于隐私考虑,用户不愿意让服务器知道,他从服务器上到底获取了什么资源,更强一点,云服务器除了用户选择的资源外,他不想让用户知道其他的资源的内容。因为对于云服务器来说,这些资源可能是有价值的,用户购买了一个资源,但是不能把所有资源都暴露给用户,否则,下一次就没办法向用户收钱了。这时候,OT就派上用场了。我们通常把A叫做发送方,B叫做接收方。
本文将介绍文献[1]中提出的一个根据DH(Diffie-Hellman)密钥协商的构造一个简单快速的二选一不经意传输(1-out-of-2 OT)。被后面的多篇论文[2]指出,其原论文的安全性证明是有问题的,并不是UC(universal composability )安全的,但是并不影响,我们在半诚实的假设的安全性。也就是参与协议的两方都会遵守协议的规则,但是可能会做一些额外的计算去获取对方的隐私信息。
先简单回顾一下DH密钥协商协议,假设 p p p是一个大素数, Z p \mathbb{Z}_p Zp是模 p p p的剩余类群。现在A和B想要协商一个共同的密钥,用于加密。但是没有一个安全的信道,所以,不能由其中一方,比如A生成密钥,然后直接发送给B。
在协商开始之前,A和B确定公共参数, p , q , g p,q,g p,q,g,其中 g g g是 Z p \mathbb{Z}_p Zp的一个 q q q阶生成元,也就是 g q ≡ 1 m o d p g^q \equiv 1\mod p gq≡1modp,且任何 0 < k < q 0
首先,A从 Z p \mathbb{Z}_p Zp中选取一个随机数 a a a,计算 x = g a m o d p x=g^a \mod p x=gamodp,然后将 x x x发送给B。
同时,B也从 Z p \mathbb{Z}_p Zp选取一个随机数 b b b,计算 y = g b m o d p y=g^b \mod p y=gbmodp, 然后发送给A。
A收到 y y y后计算 k A = y a m o d p k_A=y^a \mod p kA=yamodp。
B收到 x x x后计算 k B = x b m o d p k_B=x^b \mod p kB=xbmodp。
显然, k A = y a = ( g b ) a = ( g a ) b = x b = k B k_A=y^a=(g^b)^a=(g^a)^b=x^b =k_B kA=ya=(gb)a=(ga)b=xb=kB在模 p p p的意义下是相等的。接下来,我们使用雷士的流程来构造OT。
首先,公共参数和DH协商的参数是一样的, p , q , g p,q,g p,q,g,此外还需要确定一个加密算法,比如AES,一个哈希函数或者是密钥派生函数 H ( x ) H(x) H(x),将 Z p \mathbb{Z}_p Zp中的元素 x x x转化为加密算法的加密密钥。
A有两个消息 m 0 , m 1 m_0,m_1 m0,m1,B从中选则一个消息, m c m_c mc其中, c = 0 c=0 c=0或者 c = 1 c=1 c=1。
A从 Z p \mathbb{Z}_p Zp中选择一个随机数 a a a,计算 x = g a m o d p x=g^a \mod p x=gamodp,然后发送给B。
B从 Z p \mathbb{Z}_p Zp中选择一个随机数 b b b,如果 c = 0 c=0 c=0,那么计算 y = g b m o d p y=g^b \mod p y=gbmodp,如果 c = 1 c=1 c=1,那么计算 y = x g b m o d p y=xg^b \mod p y=xgbmodp。然后将 y y y发送给A。
A计算 k 0 = H ( y a ) , k_0=H(y^a), k0=H(ya),和 k 1 = H ( ( y x ) a ) k_1=H((\frac{y}{x})^a) k1=H((xy)a),注意,这里的 y x \frac{y}{x} xy使用的除法是 Z p \mathbb{Z}_p Zp中的除法,也就是 y y y乘以 x x x在 Z p \mathbb{Z}_p Zp中的逆元。
然后A使用 k 0 k_0 k0加密 m 0 m_0 m0得到密文 e 0 e_0 e0,使用 k 1 k_1 k1加密 m 1 m_1 m1得到密文 e 1 e_1 e1。A将两个密文都发送给B。
B计算自己的密钥 k c = H ( x b ) k_c=H(x^b) kc=H(xb),然后根据自己的选择,解密对应的密文 e c e_c ec,得到想要的 m c m_c mc。
如果 c = 0 c=0 c=0,那么 y = g b y=g^b y=gb,此时,A计算的 k 0 = H ( y a ) = H ( g a b ) k_0=H(y^a)=H(g^{ab}) k0=H(ya)=H(gab),而 k 1 = H ( ( g b g a ) a ) = H ( g a b − a 2 ) k_1=H((\frac{g^b}{g^a})^a)=H(g^{ab-a^2}) k1=H((gagb)a)=H(gab−a2),而B拥有的密钥 k c = H ( x b ) = H ( g a b ) k_c=H(x^b)=H(g^{ab}) kc=H(xb)=H(gab)。这样,B就只能解密出 e 0 e_0 e0。
同理,如果 c = 1 c=1 c=1,那么 y = x g b = g a b y=xg^b=g^{ab} y=xgb=gab,此时,A计算的 k 0 = H ( y a ) = H ( g a 2 b ) k_0=H(y^a)=H(g^{a^2b}) k0=H(ya)=H(ga2b),而 k 1 = H ( x g b x a ) = H ( g a b ) k_1=H(\frac{xg^b}{x}^a)=H(g^{ab}) k1=H(xxgba)=H(gab)。B的密钥 k c = H ( x b ) = g a b k_c=H(x^b)=g^{ab} kc=H(xb)=gab,则B只能解密出 e 1 e_1 e1。
我们注意到,对于A来说, k 0 k_0 k0和 k 1 k_1 k1都是一个随机数,当B选择不同的 c c c时,A是没有办法区分。而对于B,假设加密方法是安全的,那么,B在没有正确密钥的时候,也是没有办法解密得到另外一个没有选择的消息。
我们需要注意的是,使用离散对数构造的方案,是可以平推到椭圆曲线的。只需要将刚刚的 g g g变成一个椭圆曲线上的点,KaTeX parse error: Undefined control sequence: \mathBB at position 1: \̲m̲a̲t̲h̲B̲B̲{Z}_p变成一个椭圆曲线上的域,就可以直接平推过去。
目前有一个实现了这个椭圆曲线版本的简单OT的库,叫做otc库。使用命令
python -m pip install otc
可以直接安装。
下面是一个简单的例子:
import otc
s=otc.send()
r=otc.receive()
def round(select: int, m0:int , m1: int):
selecttion=r.query(s.public,select)
replies=s.reply(selecttion,m0.to_bytes(16,"little",signed=True),m1.to_bytes(16,"little",signed=True))
res=r.elect(s.public,select,*replies)
return int.from_bytes(res,"little",signed=True)
print(round(0,2,3))
print(round(1,2,3))
以下是几个需要注意的点:
选择比特只能是0或者1,reply的输入,两个消息m0和m1都是16比特长度的字节流,最后返回的结果也是一个字节流。
[1] Chou, Tung, and Claudio Orlandi. “The simplest protocol for oblivious transfer.” Progress in Cryptology–LATINCRYPT 2015: 4th International Conference on Cryptology and Information Security in Latin America, Guadalajara, Mexico, August 23-26, 2015, Proceedings 4. Springer International Publishing, 2015.
[2] Genç, Ziya Alper, Vincenzo Iovino, and Alfredo Rial. ““The simplest protocol for oblivious transfer” revisited.” Information Processing Letters 161 (2020): 105975.