# # DSA.py : Stupid name. Should really be called qNEW.py or something. # Suggestions for a better name would be welcome. # # Maintained by A.M. Kuchling (amk@magnet.com) # Date: 1997/09/03 # # Distribute and use freely; there are no restrictions on further # dissemination and usage except those imposed by the laws of your # country of residence. # # TODO : # Change the name # Add more comments and docstrings # Write documentation # Add better RNG (?) import types, md5 error = 'DSA module error' def RandomNumber(N, randfunc): "Get an N-bit random number" str=randfunc(N/8) char=ord(randfunc(1))>>(8-(N%8)) return Str2Int(chr(char)+str) def Int2Str(n): "Convert an integer to a string form" s='' while n>0: s=chr(n & 255)+s n=n>>8 return s def Str2Int(s): "Convert a string to a long integer" if type(s)!=types.StringType: return s # Integers will be left alone return reduce(lambda x,y : x*256+ord(y), s, 0L) def getPrime(N, randfunc): "Find a prime number measuring N bits" number=RandomNumber(N, randfunc) | 1 while (not isPrime(number)): number=number+2 return number sieve=[2,3,5,7,11,13,17,19,23,29,31,37,41] def isPrime(N): """Test if a number N is prime, using a simple sieve check, followed by a more elaborate Rabin-Miller test.""" for i in sieve: if (N % i)==0: return 0 N1=N - 1L ; n=1L while (n>1L if d!=1L: return 0 return 1 class DSAobj: def size(self): "Return the max. number of bits that can be handled by this key" bits, power = 0,1L while (power=self.q or s<=0 or s>=self.q: 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 sign(self, M, K): if (not self.hasprivate()): raise error, 'Private key not available in this object' if type(M)==types.StringType: M=Str2Int(M) if type(K)==types.StringType: K=Str2Int(K) return self._sign(M, K) def verify(self, M, signature): if type(M)==types.StringType: M=Str2Int(M) return self._verify(M, signature) validate=verify def generate(self, L, randfunc, progress_func=None): """Generate a private key with L bits""" HASHBITS=128 # Number of bits in the hashing algorithm used # (128 for MD5; change to 160 for SHA) if L<512: raise error, 'Key length <512 bits' # Generate string S and prime q if progress_func: apply(progress_func, ('p,q\n',)) while (1): self.q = getPrime(160, randfunc) S = Int2Str(self.q) n=(L-1)/HASHBITS C, N, V = 0, 2, {} # b=(self.q >> 5) & 15 b= (L-1) % HASHBITS powb=pow(long(2), b) powL1=pow(long(2), L-1) while C<4096: for k in range(0, n+1): V[k]=Str2Int(md5.new(S+str(N)+str(k)).digest()) W=V[n] % powb for k in range(n-1, -1, -1): W=(W<< long(HASHBITS) )+V[k] X=W+powL1 p=X-(X%(2*self.q)-1) if powL1<=p and isPrime(p): break C, N = C+1, N+n+1 if C<4096: break if progress_func: apply(progress_func, ('4096 multiples failed\n',) ) self.p = p power=(p-1)/self.q if progress_func: apply(progress_func, ('h,g\n',)) while (1): h=Str2Int(randfunc(L)) % (p-1) g=pow(h, power, p) if 11: break self.g=g if progress_func: apply(progress_func, ('x,y\n',)) while (1): x=Str2Int(randfunc(20)) if 01: BITS=string.atoi(sys.argv[1]) print ' Generating', BITS, 'bit key' key=DSAobj() key.generate(BITS, randfunc, sys.stdout.write) print ' Key data: (the private key is x)' for i in 'xygqp': print '\t', i, ':', hex(getattr(key, i)) plaintext="Hello" if key.cansign(): print ' Signature test' print "Plaintext:", plaintext K=getPrime(30, randfunc) signature=key.sign(plaintext, K) print "Signature:", signature result=key.verify(plaintext, signature) if not result: print " Sig. verification failed when it should have succeeded" else: print 'Signature verified' # Test on a mangled plaintext result=key.verify(plaintext[:-1], signature) if result: print " Sig. verification succeeded when it should have failed" # Change a single bit in the plaintext badtext=plaintext[:-3]+chr( 1 ^ ord(plaintext[-3]) )+plaintext[-3:] result=key.verify(badtext, signature) if result: print " Sig. verification succeeded when it should have failed" print 'Removing private key data' pubonly=key.publickey() result=pubonly.verify(plaintext, signature) if not result: print " Sig. verification failed when it should have succeeded" else: print 'Signature verified'