Fiat–Shamir heuristic 启发式的应用 理解 代码实现

更多技术内容可关注本人知乎专栏: 区块链技术最前沿https://zhuanlan.zhihu.com/blockchain-top-paper

先讲问题,再引入Fiat–Shamir heuristic。

问题

平时我们使用密码注册和登录一个网站的过程可能是这样的。我们使用用户名和密码注册一个网站,网站后台收到用户名和密码之后,使用一个hash算法计算密码的哈希值,然后将用户名和哈希值存入数据库。下次用户登录的时候,后台以同样的方式计算出哈希值,对比数据库中的是否一样,如果两者的用户名和哈希值都一样,登录成功。好一点的后台可能在计算哈希值的时候,选择一个数字用作“盐”,也就是加盐哈希。这样攻击者拿到加盐的哈希值之后,在没有知道盐值的情况下是无法通过暴力的方式计算出用户的密码的。但是,如果攻击者知道了盐值呢?攻击者就能通过字典的单词和盐值计算出哈希值,对比保存在数据库中的哈希值,来试探出用户的密码。
总之,用户将自己的密码发送给后台是不安全的,因为用户的密码通过网络传播到远方的服务器上,会发生什么泄露密码的事情的不确定性太大了。所以,最安全的办法就是用户不用把登录密钥发给后台。
所以,**有没有一种办法,让用户不用发密码给后台,用户也能注册和登录呢?**这篇文章就是讲这个问题的一个方案。

应用场景描述

1.用户通过自己的密码生成一个数据,将这个数据而不是密码发给网站后台完成注册。网站后台把这个数据保存到数据库中。
2. 注册成功之后,用户想登录这个网站,这时候,用户自己生成另一个数据,这个数据跟第一步的数据是不一样的。然后把这个数据发给网站后台。
3. 网站后台通过第一步和第二步的两个数据,进行一定的计算,就能够确认现在想登录的用户确实是第一步所注册的用户。后台认证通过。

上面的场景描述中,用户并没有把密钥发给后台,并且每一次登录的时候,用户发给后台的数据可以是不一样子的。具体怎么做到的呢?

上面的三个步骤是非交互式的证明方法,所谓非交互式,即是用户在登录的时候,想向服务器证明自己,只需要向服务器发送一个证明数据就okay了,服务器拿到这个数据自个就能够确认。一次发送,一次接受。而非交互式的指的是,用户想向服务器证明自己的时候,用户和服务器需要多次交互。为了简单起见,下面先讲交互式的,再说在这基础之上如何改成非交互式的证明。

非交互式的证明的过程是下图这个样子的。下图中,victor作为后台服务器,peggy作为用户。他两人都事先知道g。首先x是peggy的密钥,她使用密钥生成数据y,向victor发送y,完成完整注册。当peggy登录网站的时候,peggy生成另一个数据t,向victor发送t。接下来victor发送一个随机数c给peggy,peggy结合这个c生成数据r。victor拿到数据r之后,通过公式计算 g r y c g^ry^c ,如果其结果等于t,那么认证通过。
在这里插入图片描述
证明过程是:
g r × y c = g v c x × g x c = g v c x + c x = g v g^r×y^c=g^{v−cx}×g^{xc}=g^{v−cx+cx}=g^v

上面的证明过程是交互式的,因为其中认证者victor还要向证明者peggy发送一个随机数c。下面我们把victor向peggy发送随机数c这一步改为下面这一步,让整个证明过程变成非交互式的:peggy计算c=H(g, y, t),其中H()是加密哈算法,g,y和t都是前面的数。其他步骤不变。

代码实现:

import sys
import random
import hashlib
n=997
text="Hello"
g= 3
def extended_euclidean_algorithm(a, b):
    """
    Returns a three-tuple (gcd, x, y) such that
    a * x + b * y == gcd, where gcd is the greatest
    common divisor of a and b.
This function implements the extended Euclidean
    algorithm and runs in O(log b) in the worst case.
    """
    s, old_s = 0, 1
    t, old_t = 1, 0
    r, old_r = b, a
while r != 0:
        quotient = old_r // r
        old_r, r = r, old_r - quotient * r
        old_s, s = s, old_s - quotient * s
        old_t, t = t, old_t - quotient * t
return old_r, old_s, old_t
def inverse_of(n, p):
    """
    Returns the multiplicative inverse of
    n modulo p.
This function returns an integer m such that
    (n * m) % p == 1.
    """
    gcd, x, y = extended_euclidean_algorithm(n, p)
    assert (n * x + p * y) % p == gcd
if gcd != 1:
        # Either n is 0, or p is not a prime number.
        raise ValueError(
            '{} has no multiplicative inverse '
            'modulo {}'.format(n, p))
    else:
        return x % p
def pickg(p):
 for x in range (1,p):
  rand = x
  exp=1
  next = rand % p
while (next <> 1 ):
   next = (next*rand) % p
   exp = exp+1
  
  if (exp==p-1):
   return rand
v = random.randint(1,n)
c = random.randint(1,n)
print "Password:\t",text
x = int(hashlib.md5(text).hexdigest()[:8], 16) % n
g=pickg(n)
y= pow(g,x,n)
t = pow(g,v,n)
r = (v - c * x)
if (r<0):
 Result = ( inverse_of(pow(g,-r,n),n) * pow(y,c,n))  % n
else:
 Result = ( pow(g,r,n) * pow(y,c,n))  % n
print '======Agreed parameters============'
print 'P=',n,'\t(Prime number)'
print 'G=',g,'\t(Generator)'
print '======The secret=================='
print 'x=',x,'\t(Alice\'s secret)'
print '======Random values==============='
print 'c=',c,'\t(Bob\'s random value)'
print 'v=',v,'\t(Alice\'s random value)'
print '======Shared value==============='
print 'g^x mod P=\t',y
print 'r=\t\t',r
print '=========Resluts==================='
print 't=g**v % n =\t\t',t
print '( (g**r) * (y**c) )=\t',Result
if (t==Result):
 print 'Alice has proven she knows password'
else:
 print 'Alice has not proven she knows x'

参考内容:
https://asecuritysite.com//encryption/fiat2
https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic

发布了188 篇原创文章 · 获赞 390 · 访问量 74万+

猜你喜欢

转载自blog.csdn.net/liangyihuai/article/details/103438974