171 lines
5.4 KiB
Python
171 lines
5.4 KiB
Python
#
|
|
# qNEW.py : The q-NEW signature algorithm.
|
|
#
|
|
# Part of the Python Cryptography Toolkit
|
|
#
|
|
# Distribute and use freely; there are no restrictions on further
|
|
# dissemination and usage except those imposed by the laws of your
|
|
# country of residence. This software is provided "as is" without
|
|
# warranty of fitness for use or suitability for any purpose, express
|
|
# or implied. Use at your own risk or not at all.
|
|
#
|
|
|
|
__revision__ = "$Id: qNEW.py,v 1.8 2003/04/04 15:13:35 akuchling Exp $"
|
|
|
|
from Crypto.PublicKey import pubkey
|
|
from Crypto.Util.number import *
|
|
from Crypto.Hash import SHA
|
|
|
|
class error (Exception):
|
|
pass
|
|
|
|
HASHBITS = 160 # Size of SHA digests
|
|
|
|
def generate(bits, randfunc, progress_func=None):
|
|
"""generate(bits:int, randfunc:callable, progress_func:callable)
|
|
|
|
Generate a qNEW key of length 'bits', using 'randfunc' to get
|
|
random data and 'progress_func', if present, to display
|
|
the progress of the key generation.
|
|
"""
|
|
obj=qNEWobj()
|
|
|
|
# Generate prime numbers p and q. q is a 160-bit prime
|
|
# number. p is another prime number (the modulus) whose bit
|
|
# size is chosen by the caller, and is generated so that p-1
|
|
# is a multiple of q.
|
|
#
|
|
# Note that only a single seed is used to
|
|
# generate p and q; if someone generates a key for you, you can
|
|
# use the seed to duplicate the key generation. This can
|
|
# protect you from someone generating values of p,q that have
|
|
# some special form that's easy to break.
|
|
if progress_func:
|
|
progress_func('p,q\n')
|
|
while (1):
|
|
obj.q = getPrime(160, randfunc)
|
|
# assert pow(2, 159L)<obj.q<pow(2, 160L)
|
|
obj.seed = S = long_to_bytes(obj.q)
|
|
C, N, V = 0, 2, {}
|
|
# Compute b and n such that bits-1 = b + n*HASHBITS
|
|
n= (bits-1) / HASHBITS
|
|
b= (bits-1) % HASHBITS ; powb=2L << b
|
|
powL1=pow(long(2), bits-1)
|
|
while C<4096:
|
|
# The V array will contain (bits-1) bits of random
|
|
# data, that are assembled to produce a candidate
|
|
# value for p.
|
|
for k in range(0, n+1):
|
|
V[k]=bytes_to_long(SHA.new(S+str(N)+str(k)).digest())
|
|
p = V[n] % powb
|
|
for k in range(n-1, -1, -1):
|
|
p= (p << long(HASHBITS) )+V[k]
|
|
p = p+powL1 # Ensure the high bit is set
|
|
|
|
# Ensure that p-1 is a multiple of q
|
|
p = p - (p % (2*obj.q)-1)
|
|
|
|
# If p is still the right size, and it's prime, we're done!
|
|
if powL1<=p and isPrime(p):
|
|
break
|
|
|
|
# Otherwise, increment the counter and try again
|
|
C, N = C+1, N+n+1
|
|
if C<4096:
|
|
break # Ended early, so exit the while loop
|
|
if progress_func:
|
|
progress_func('4096 values of p tried\n')
|
|
|
|
obj.p = p
|
|
power=(p-1)/obj.q
|
|
|
|
# Next parameter: g = h**((p-1)/q) mod p, such that h is any
|
|
# number <p-1, and g>1. g is kept; h can be discarded.
|
|
if progress_func:
|
|
progress_func('h,g\n')
|
|
while (1):
|
|
h=bytes_to_long(randfunc(bits)) % (p-1)
|
|
g=pow(h, power, p)
|
|
if 1<h<p-1 and g>1:
|
|
break
|
|
obj.g=g
|
|
|
|
# x is the private key information, and is
|
|
# just a random number between 0 and q.
|
|
# y=g**x mod p, and is part of the public information.
|
|
if progress_func:
|
|
progress_func('x,y\n')
|
|
while (1):
|
|
x=bytes_to_long(randfunc(20))
|
|
if 0 < x < obj.q:
|
|
break
|
|
obj.x, obj.y=x, pow(g, x, p)
|
|
|
|
return obj
|
|
|
|
# Construct a qNEW object
|
|
def construct(tuple):
|
|
"""construct(tuple:(long,long,long,long)|(long,long,long,long,long)
|
|
Construct a qNEW object from a 4- or 5-tuple of numbers.
|
|
"""
|
|
obj=qNEWobj()
|
|
if len(tuple) not in [4,5]:
|
|
raise error, 'argument for construct() wrong length'
|
|
for i in range(len(tuple)):
|
|
field = obj.keydata[i]
|
|
setattr(obj, field, tuple[i])
|
|
return obj
|
|
|
|
class qNEWobj(pubkey.pubkey):
|
|
keydata=['p', 'q', 'g', 'y', 'x']
|
|
|
|
def _sign(self, M, K=''):
|
|
if (self.q<=K):
|
|
raise error, 'K is greater than q'
|
|
if M<0:
|
|
raise error, 'Illegal value of M (<0)'
|
|
if M>=pow(2,161L):
|
|
raise error, 'Illegal value of M (too large)'
|
|
r=pow(self.g, K, self.p) % self.q
|
|
s=(K- (r*M*self.x % self.q)) % self.q
|
|
return (r,s)
|
|
def _verify(self, M, sig):
|
|
r, s = sig
|
|
if r<=0 or r>=self.q or s<=0 or s>=self.q:
|
|
return 0
|
|
if M<0:
|
|
raise error, 'Illegal value of M (<0)'
|
|
if M<=0 or M>=pow(2,161L):
|
|
return 0
|
|
v1 = pow(self.g, s, self.p)
|
|
v2 = pow(self.y, M*r, self.p)
|
|
v = ((v1*v2) % self.p)
|
|
v = v % self.q
|
|
if v==r:
|
|
return 1
|
|
return 0
|
|
|
|
def size(self):
|
|
"Return the maximum number of bits that can be handled by this key."
|
|
return 160
|
|
|
|
def has_private(self):
|
|
"""Return a Boolean denoting whether the object contains
|
|
private components."""
|
|
return hasattr(self, 'x')
|
|
|
|
def can_sign(self):
|
|
"""Return a Boolean value recording whether this algorithm can generate signatures."""
|
|
return 1
|
|
|
|
def can_encrypt(self):
|
|
"""Return a Boolean value recording whether this algorithm can encrypt data."""
|
|
return 0
|
|
|
|
def publickey(self):
|
|
"""Return a new key object containing only the public information."""
|
|
return construct((self.p, self.q, self.g, self.y))
|
|
|
|
object = qNEWobj
|
|
|