密码协议学习笔记(1.5):几种常用的非对称密码体系

RSA密码体系:

RSA密码体系是一种依赖于依赖于大数质因数分解的难解性的密码体系.

RSA加密算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取两个大质数p,q(需要使用Miller-Rabin算法判定其是否为质数),计算n=pq,其欧拉函数φ(n)=(p1)(q1)
  2. Alice选取大质数e(max(p,q),φ(n)),这保证了gcd(e,φ(n))=1
  3. Alice(使用扩展欧几里得算法或欧拉定理)计算出d,使得de=kφ(n)+1,其中k是任意的整数,这意味着,d=e1modφ(n)
  4. Alice将pk=(n,e)作为公钥发送给Bob,将sk=(p,q,d)作为私钥自己保留
  5. 若Bob要给Alice发送信息(明文)m,用以下加密(encryption)算法将其加密为密文c:c=Enc(pk,m)=memodn
  6. Alice收到密文c后,用以下解密(decryption)算法还原出明文m:Dec(sk,c)=cdmodn.

正确性:

至于为什么cd=mmodn,证明要根据密文m是否与模数n互质,考虑如下两种情况:

m,n互质时,

cd=med=mkφ(n)+1(k为整数,由于第3步的构造)=m(mφ(n))k=m(1)kmodn(欧拉定理)=m

m,n不互质时,欧拉定理失效,由于n=pq,因此m要么是p的倍数,要么是q的倍数

不妨记m=kp,k为整数,且m,q互质(否则m会是q的倍数,同时是p,q的倍数,就是n的倍数,此时证明是显然的),那么,此时可应用以q为模数的欧拉定理

xq1=1modqxk(p1)(q1)=1modq(等式两端同时做k(p1)次方)xk(p1)(q1)=iq+1(将同余式改写成恒等式)

重新观察解密过程

cd=med=mkφ(n)+1(k为整数,由于第3步的构造)=mmk(p1)(q1)=(iq+1)m(xk(p1)(q1)=iq+1)=mq+m=kpq+m(m=kp)=mmodn

综上,

cd=mmodn

安全性:

Alice的私钥sk=(p,q,d)不会上传到网络上,因此理论上无法被攻击者截获,攻击者若截获了加密消息c,同时已知公开信息pk=(n,e)时,由于大整数分解质因数问题的难解性,攻击者很难知道n=pq中的p,q到底是多少,也就不知道e=d1mod(p1)(q1)是多少.

代码:

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod

p = 97
q = 103 #实际上这两个大整数是随机生成并通过Miller-Rabin算法判定的
print("Gen()->pk,sk")
print('p =',p,end=' = ')
print(*divi(p),sep='*')
print('q =',q,end=' = ')
print(*divi(q),sep='*')
g,x,y = exgcd(p,q)
n = p*q
print('n = pq =',n)
φ = (p-1)*(q-1)
print('φ = (p-1)(q-1) =',φ)
e = 197
print('e =',e,'\ngcd(z,e) =',gcd(φ,e))
d = rev(e,φ,type ='not_prime')
print('''d = e^{-1}(mod φ) =''',d)
print('pk:(n =',n,',e =',e,')')
print('sk:(n =',n,',d =',d,')')

print('\n')
print("Enc(pk,m)->c")
m = 114
print('m =',m)
c = qpow(m,e,n)
print('''c = m^e mod n =''',c)

print('\n')
print('Dec(c,sk)->m')
m1 = qpow(c,d,n)
print('''m = c^d mod n =''',m1)

运行结果:

Gen()->pk,sk
p = 97 = 97
q = 103 = 103
e = 197
gcd(z,e) = 1
d = e^{-1}(mod φ) = 845
pk:(n = 9991 ,e = 197 )
sk:(n = 9991 ,d = 845 )


Enc(pk,m)->c
m = 114
c = m^e mod n = 7731


Dec(c,sk)->m
m = c^d mod n = 114

RSA数字签名算法:

RSA密码体系还可以用于数字签名.

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取两个大质数p,q(需要使用Miller-Rabin算法判定其是否为质数),计算n=pq,其欧拉函数φ(n)=(p1)(q1)
  2. Alice选取大质数e(max(p,q),φ(n)),这保证了gcd(e,φ(n))=1
  3. Alice(使用扩展欧几里得算法或欧拉定理)计算出d,使得de=kφ(n)+1,其中k是任意的整数,这意味着,d=e1modφ(n)
  4. Alice将pk=(n,e)作为公钥发送给Bob,将sk=(p,q,d)作为私钥自己保留
  5. 若Alice要对一段信息m进行签名,用以下签名(signature)算法为其签名s:s=Sign(sk,m)=mdmodn
  6. Bob收到(m,s)后,用以下验证(verify)算法验算签名是否有效Ver(pk,m,s):se==m?1:0.

正确性:

同上,略.

安全性:

同上,攻击者要伪造签名,就需要在仅知道公钥pk=(n,e)的情况下,计算出d=e1mod(p1)(q1),需要进行分解质因数n=pq,这在计算复杂度上是不可行的.

代码:

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod

p = 97
q = 103 #实际上这两个大整数是随机生成并通过Miller-Rabin算法判定的
print("Gen()->pk,sk")
print('p =',p,end=' = ')
print(*divi(p),sep='*')
print('q =',q,end=' = ')
print(*divi(q),sep='*')
g,x,y = exgcd(p,q)
n = p*q
print('n = pq =',n)
φ = (p-1)*(q-1)
print('φ = (p-1)(q-1) =',φ)
e = 197
print('e =',e,'\ngcd(z,e) =',gcd(φ,e))
d = rev(e,φ,type ='not_prime')
print('''d = e^{-1}(mod φ) =''',d)
print('pk:(n =',n,',e =',e,')')
print('sk:(n =',n,',d =',d,')')

print('\n')
print("Sign(sk,m)->s")
m = 114
print('m =',m)
s = qpow(m,e,n)
print('''s = m^e mod n =''',s)

print('\n')
print('''Ver(pk,s,m)->{Valid,Invalid}''')
m1 = qpow(s,d,n)
print('''s^d mod n =''',m1)
if (m1==m):
    print("Valid")
else:
    print("Invalid")
    

运行结果:

Gen()->pk,sk
p = 97 = 97
q = 103 = 103
n = pq = 9991
φ = (p-1)(q-1) = 9792
e = 197
gcd(z,e) = 1
d = e^{-1}(mod φ) = 845
pk:(n = 9991 ,e = 197 )
sk:(n = 9991 ,d = 845 )


Sign(sk,m)->s
m = 114
s = m^e mod n = 7731


Ver(pk,s,m)->{1,0}
s^d mod n = 114
Valid

离散对数密码体系(ElGamal):

ElGamal密码体系是一种利用离散对数难解性问题的密码体系

ElGamal加密算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取大质数p,整数g[0,p1],整数α[0,p2],计算β=gαmodp
  2. Alice将pk=(p,g,β)作为公钥发送给Bob,将sk=(p,α)作为私钥自己保留
  3. 若Bob要给Alice发送信息(明文)m,需要先随机生成一个k[0,p1],然后用以下加密(encryption)算法将其加密为密文c:c=Enc(pk,m,k)=(r,s)其中r=gkmodp,   s=mβkmodp
  4. Alice收到密文c=(r,s)后,用以下解密(decryption)算法还原出明文m:Dec(sk,c)=s(rα)1modn.

正确性:

srα=mβk(gk)α(加密过程,r=gkmodp,s=mβkmodp)=m(gα)k(gk)α(密钥生成过程,β=gαmodp)=mgαkgαk=m

安全性:

攻击者在截获密文c=(r,s),并已知公钥pk=(p,g,β)时,要还原密文,就需要知道α是多少,这就需要在已知p,g,β的情况下求解β=gαmodp中的α是多少,该等式可记为如下等价形式α=loggβmodp称为离散对数问题,这在计算复杂度上是难以求解的.

代码:

import time #用于生成随机数
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod


print("Gen()->pk,sk")
p=97
print('p =',p,end=' = ')
print(*divi(p),sep='*')
g=5
print('g =',g,', g in [0,p-1]')
α=8
print('α =',α,', α in [0,p-2]')
β=qpow(g,α,p)
print('''β = g^α mod p =''',β)
print('pk:(p =',p,',g =',g,',β =',β,')')
print('sk:(p =',p,',α =',α,')')

print('\n')
print("Enc(pk,m)->c")
m = 66
print('m =',m)
k=int(time.time())%(p-1)
print('随机生成 k in [0,p-2], k =',k)
r=qpow(g,k,p)
s=m*qpow(β,k,p) % p
print('r=g^k mod p =',r)
print('s= m*β^k mod p =',s)
print('c=(r,s)')

print('\n')
print("Dec(sk,c)->m")
m1=s*rev(qpow(r,α,p),p,type='prime')%p
print('''m=s*(r^α)^{-1} mod p =''',m1)

运行结果:

Gen()->pk,sk
p = 97 = 97
g = 5 , g in [0,p-1]
α = 8 , α in [0,p-2]
β = g^α mod p = 6
pk:(p = 97 ,g = 5 ,β = 6 )
sk:(p = 97 ,α = 8 )


Enc(pk,m)->c
m = 66
随机生成 k in [0,p-2], k = 3
r=g^k mod p = 28
s= m*β^k mod p = 94
c=(r,s)


Dec(sk,c)->m
m=s*(r^α)^{-1} mod p = 66

ElGamal数字签名算法:

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. Alice选取大质数p,整数g[0,p1],整数α[0,p2],计算β=gαmodp
  2. Alice将pk=(p,g,β)作为公钥发送给Bob,将sk=(p,α)作为私钥自己保留
  3. 若Alice要对一段信息m进行签名,首先在[1,p2]中随机选取一个与p1互质的数k,然后用以下签名(signature)算法为其签名sig:sig=Sign(sk,m,k)=(r,s) 其中 r=gkmodp s=(mαr)k1mod(p1)
  4. Bob收到(m,sig)后,用以下验证(verify)算法验算签名是否有效Ver(pk,m,sig):βrrs==gm?1:0.

特别注意,这里Alice在计算k的逆元时,要计算的是模数为(p1)时的逆元,而非模数为p时的逆元,否则:

gk(k1(modp))=gwp+1=g(gp)w(wZ)

然而当p为质数时,根据欧拉定理,gp=g1modp

正确性:

βrrs=(gα)r(gk)s=gαrgk(mrα)k1(由签名过程,s=(mαr)k1)=gαr+(mαr)=gm

安全性:

同上,攻击者若要伪造签名,就需要就需要知道α是多少,就要求解离散对数问题α=loggβmodp.

代码:

import time #用于生成随机数
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod


print("Gen()->pk,sk")
p=97
print('p =',p,end=' = ')
print(*divi(p),sep='*')
g=5
print('g =',g,', g in [0,p-1]')
α=8
print('α =',α,', α in [0,p-2]')
β=qpow(g,α,p)
print('''β = g^α mod p =''',β)
print('pk:(p =',p,',g =',g,',β =',β,')')
print('sk:(p =',p,',α =',α,')')

print('\n')
print("Sign(pk,m)->c")
m = 66
print('m =',m)
k=0
while(gcd(k,p-1)!=1):
    k=int(time.time()*1000)%(p-1)
print('随机生成与(p-1)互质的 k in [0,p-2], k =',k,'gcd(p-1,k) =',gcd(p-1,k))
r=qpow(g,k,p)
print('r=g^k mod p =',r)
s=(m-r*α)*rev(k,p-1,type='not_prime')%(p-1)
print('''s= (m-ra)k^{-1} mod (p-1) =''',s)
print('sig=(r,s)')

print('\n')
print('''Ver(pk,sig)->{0,1}''')
m1=qpow(β,r,p)*qpow(r,s,p)%p
m2=qpow(g,m,p)

print('''β^r·r^s mod p =''',m1)
print('''g^m mod p =''',m2)
if (m1==m2):
    print("Valid")
else:
    print("Invalid")

运行结果:

Gen()->pk,sk
p = 97 = 97
g = 5 , g in [0,p-1]
α = 8 , α in [0,p-2]
β = g^α mod p = 6
pk:(p = 97 ,g = 5 ,β = 6 )
sk:(p = 97 ,α = 8 )


Sign(pk,m)->c
m = 66
随机生成与(p-1)互质的 k in [0,p-2], k = 41 gcd(p-1,k) = 1
r=g^k mod p = 80
s= (m-ra)k^{-1} mod (p-1) = 82
sig=(r,s)


Ver(pk,sig)->{0,1}
β^r·r^s mod p = 70
g^m mod p = 70
Valid

椭圆曲线上定义的交换群:

尝试以最简洁最直接的方式说明椭圆曲线的定义

由一个质数p作为模数,两个整数a,b作为参数,当4a3+27b2modp0时,可以定义一个椭圆曲线y2=x3+ax+bmodp上的交换群Ep(a,b)=(G{O},+)

G为椭圆曲线上的实点,O为无穷远处的虚点,同时充当着该群上的零元.

遍历x[0,p1],计算z=x3+ax+b,然后计算模平方根y2=zmod11(若存在)(计算方法参考素性检验问题和模平方根问题 - Isakovsky - 博客园 (cnblogs.com)),即可得到群上的所有实点.

群上的运算+的定义如下,设P=(x1,y1),Q=(x2,y2)G:

  1. O+O=O
  2. P+O=P
  3. x1=x2,y1+y2=p,则P+Q=O
  4. 否则,P+Q=S=(x3,y3),x3,y3由以下规则确定: x3=λ2x1x2modp y3=λ(x1x3)y1modp 其中 λ={y2y1x2x1modp,x1x23x12+a2y1modp,x1=x2

类似地,群上的数乘运算kP定义为kP相加

示例代码(E11(1,6)下定义的交换群):

def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod
        

def ECCAdd(p1,p2,p,a,b): #椭圆曲线上的加法
    if(p1=='inf'):
        return p2
    elif(p2=='inf'):
        return p1
    # 无穷远点定义为零元
    x1,y1=p1
    x2,y2=p2
    if(x1==x2 and y1+y2==p):
        return 'inf'
    # 定义关于y轴对称两点(在模意义下实际上是相加得p)互为加法逆元,相加得零元

    if(x1==x2 and y1==y2):
        λ=(3*x1*x1+a)*rev(2*y1,p,type='prime')%p # 两点不重合,过此两点做一条直线
    else:
        λ=(y2-y1)*rev((x2-x1+p)%p,p,type='prime')%p # 两点重合,则取其与曲线的切线

    x3=(λ*λ-x1-x2+p)%p
    y3=(λ*(x1-x3)-y1+p)%p
    # 定义这条直线与曲线的另一个交点为群上加法的结果

    return (x3,y3)


p=11 #定义模数
a=1
b=6 #当4a^3+27b^2 ≠0 mod p 时,可定义一个交换群
print('p =',p,'a =',a,'b =',b)
if(4*a*a*a+27*b*b%p==0):
    print("无法构成交换群")
    exit()
else:
    print("可以构成交换群,群上的元素有:")
    for x in range(p):
        z=(x*x*x+a*x+b)%p
        for y in range(p):
            if(y*y%p==z):
                print((x,y))
    print('inf')
    print('请输入第一个点:')
    p1=input()
    if(p1!='inf'):
        p1=tuple(p1.split(','))
        p1=(int(p1[0][1:]),int(p1[1][:-1]))
        if((p1[0]*p1[0]*p1[0]+a*p1[0]+b)%p!=(p1[1]*p1[1])%p):
            print('该点不在椭圆曲线上!')
            exit()
    print('请输入第二个点:')
    p2=input()
    if(p2!='inf'):
        p2=tuple(p2.split(','))
        p2=(int(p2[0][1:]),int(p2[1][:-1]))
        if((p2[0]*p2[0]*p2[0]+a*p2[0]+b)%p!=(p2[1]*p2[1])%p):
            print('该点不在椭圆曲线上!')
            exit()
    print(p1,'+',p2,'=',ECCAdd(p1,p2,p,a,b))

运行结果:

p = 11 a = 1 b = 6
可以构成交换群,群上的元素有:
(2, 4)
(2, 7)
(3, 5)
(3, 6)
(5, 9)
(7, 2)
(7, 9)
(8, 3)
(8, 8)
(10, 2)
(10, 9)
inf

输入: 
inf
inf
输出:
inf

输入:
(7,2)
inf
输出:
(7,2)

输入:
(5,2)
(5,9)
输出:
inf

输入:
(8,3)
(8,3)
输出:
(7,9)

输入:
(8,3)
(7,2)
输出:
(8,8)

椭圆曲线上的ElGamal密码体系:

椭圆曲线上的离散对数问题也是难解的,由此可构造出此种密码体系并确保其安全.

椭圆曲线密码体系上的明文和密文都是椭圆曲线上的点,因此在使用该密码体系之前需要构造一个整数与点之间的双射.

参与者:

  • 私钥持有者Alice
  • 公钥持有者Bob

运行步骤:

  1. 定义椭圆曲线上点的个数|G{O}|=n,Alice选取点PG,再选取x[2,n1],计算Q=xP,将Q作为公钥发送给Bob,x作为私钥保留
  2. 若Bob要给Alice发送信息PmG{O},需要先随机生成一个k[1,n1],然后用以下加密(encryption)算法将其加密为密文c=Enc(pk,k,Pm)=(C1,C2):C1=kP C2=Pm+kQ
  3. Alice收到密文c=(C1,C2)后,用以下解密(decryption)算法还原出明文Pm:Dec(sk,c)=C2xC1.

正确性:

C2xC1=(Pm+kQ)x(kP)=(Pm+k(xP))x(kP)=Pm

安全性:

攻击者要想从c=(C1,C2)计算出Pm,就必须知道k,而要从PkP中计算出k将面临求解椭圆曲线上的离散对数问题.

代码:

import time
def divi(a): #分解质因数
    i=2
    ans=[]
    while(i*i<a):
        while(a%i==0):
            ans.append(i)
            a=a//i
        i=i+1
    if(a>1):
        ans.append(a)
    return ans

def gcd(a,b): #辗转相除法
    if(b == 0):
        return a
    else:
        return gcd(b,a%b)
    
def exgcd(a,b): #扩展欧几里得算法
    if(b == 0):
        x = 1
        y = 0
        return a,x,y
    ans,t,x = exgcd(b,a%b)
    y = t-a//b*x
    return ans,x,y

def qpow(base,exp,mod): #快速幂
    ans = 1
    while(exp>0):
        if(exp%2 == 1):
            ans = ans*base%mod
        exp = exp//2
        base = base*base%mod
    return ans

def rev(a,mod,type ='not_prime'):
    if(type =='prime'): #费马小定理求逆元
        return qpow(a,mod-2,mod)
    else: #辗转相除法求逆元
        g,x,y = exgcd(a,mod)
        if(g!= 1):
            return -1 #逆元不存在
        else:
            return (x+mod)%mod
        

def ECCAdd(p1,p2,p,a,b): #椭圆曲线上的加法
    if(p1=='inf'):
        return p2
    elif(p2=='inf'):
        return p1
    # 无穷远点定义为零元
    x1,y1=p1
    x2,y2=p2
    if(x1==x2 and y1+y2==p):
        return 'inf'
    # 定义关于y轴对称两点(在模意义下实际上是相加得p)互为加法逆元,相加得零元

    if(x1==x2 and y1==y2):
        λ=(3*x1*x1+a)*rev(2*y1,p,type='prime')%p # 两点不重合,过此两点做一条直线
    else:
        λ=(y2-y1)*rev((x2-x1+p)%p,p,type='prime')%p # 两点重合,则取其与曲线的切线

    x3=(λ*λ-x1-x2+p)%p
    y3=(λ*(x1-x3)-y1+p)%p
    # 定义这条直线与曲线的另一个交点为群上加法的结果
    return (x3,y3)

def EccMinus(p1,p2,p,a,b): #做减法就是加上加法逆元
    if(p2=='inf'):
        return p1
    else:
        return ECCAdd(p1,(p2[0],p-p2[1]),p,a,b)


def ECCMult(k,base,p,a,b): #椭圆曲线上的数乘(龟速乘)
    ans = 'inf'
    while(k>0):
        if(k%2 == 1):
            ans = ECCAdd(ans,base,p,a,b)
        k = k//2
        base = ECCAdd(base,base,p,a,b)
    return ans



p=11 #定义模数
a=1
b=6 #当4a^3+27b^2 ≠0 mod p 时,可定义一个交换群
print('p =',p,'a =',a,'b =',b)
if(4*a*a*a+27*b*b%p==0):
    print("无法构成交换群")
    exit()
else:
    print("可以构成交换群,群上的元素有:")
n=0 #统计椭圆曲线上的元素个数
for x in range(p):
    z=(x*x*x+a*x+b)%p
    for y in range(p):
        if(y*y%p==z):
            print((x,y))
            n=n+1
print('inf')
n=n+1 #这个值同时也是(除零元O以外的)所有的点的阶数
# 即,使得 nP=O 最小的正整数n

print("Gen()->pk,sk")
P=(5,2)
x=6
Q=ECCMult(x,P,p,a,b)
print('P =',P)
print('x =',x)
print('Q = x * P =',Q)
print('pk:(Q =',Q,')')
print('sk:(x =',x,')')

print('\n')
print("Enc(pk,m)->c")
m = (8,8)
print('m =',m)
k=int(time.time()*1000)%(n-1)+1
print('k =',k)
c1=ECCMult(k,P,p,a,b)
c2=ECCAdd(m,ECCMult(k,Q,p,a,b),p,a,b)
print('c1 = kP =',c1)
print('c1 = m + kQ =',c1)
print('c = ( c1 =',c1,'c2 =',c2,')')

print('\n')
print('Dec(c,sk)->m')
m1 = EccMinus(c2,ECCMult(x,c1,p,a,b),p,a,b)
print('''m = c2 - x * c1 =''',m1)

运行结果:

p = 11 a = 1 b = 6
可以构成交换群,群上的元素有:
(2, 4)
(2, 7)
(3, 5)
(3, 6)
(5, 2)
(5, 9)
(7, 2)
(7, 9)
(8, 3)
(8, 8)
(10, 2)
(10, 9)
inf
Gen()->pk,sk
P = (5, 2)
x = 6
Q = x * P = (2, 4)
pk:(Q = (2, 4) )
sk:(x = 6 )


Enc(pk,m)->c
m = (8, 8)
k = 10
c1 = kP = (7, 2)
c1 = m + kQ = (7, 2)
c = ( c1 = (7, 2) c2 = inf )


Dec(c,sk)->m
m = c2 - x * c1 = (8, 8)

(注:若待加密的消息过长(二进制值超过了模数),可将其二进制形式进行分块,然后参与加密运算)

(若待签名的消息过长,可直接对其哈希值进行签名)

posted @   Isakovsky  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示