-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
dcc38b4
commit 3d65e39
Showing
6 changed files
with
426 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
''' | ||
Created on Dec 22, 2011 | ||
@author: pablocelayes | ||
''' | ||
|
||
def egcd(a,b): | ||
''' | ||
Extended Euclidean Algorithm | ||
returns x, y, gcd(a,b) such that ax + by = gcd(a,b) | ||
''' | ||
u, u1 = 1, 0 | ||
v, v1 = 0, 1 | ||
while b: | ||
q = a // b | ||
u, u1 = u1, u - q * u1 | ||
v, v1 = v1, v - q * v1 | ||
a, b = b, a - q * b | ||
return u, v, a | ||
|
||
def gcd(a,b): | ||
''' | ||
2.8 times faster than egcd(a,b)[2] | ||
''' | ||
a,b=(b,a) if a<b else (a,b) | ||
while b: | ||
a,b=b,a%b | ||
return a | ||
|
||
def modInverse(e,n): | ||
''' | ||
d such that de = 1 (mod n) | ||
e must be coprime to n | ||
this is assumed to be true | ||
''' | ||
return egcd(e,n)[0]%n | ||
|
||
def totient(p,q): | ||
''' | ||
Calculates the totient of pq | ||
''' | ||
return (p-1)*(q-1) | ||
|
||
def bitlength(x): | ||
''' | ||
Calculates the bitlength of x | ||
''' | ||
assert x >= 0 | ||
n = 0 | ||
while x > 0: | ||
n = n+1 | ||
x = x>>1 | ||
return n | ||
|
||
|
||
def isqrt(n): | ||
''' | ||
Calculates the integer square root | ||
for arbitrary large nonnegative integers | ||
''' | ||
if n < 0: | ||
raise ValueError('square root not defined for negative numbers') | ||
|
||
if n == 0: | ||
return 0 | ||
a, b = divmod(bitlength(n), 2) | ||
x = 2**(a+b) | ||
while True: | ||
y = (x + n//x)//2 | ||
if y >= x: | ||
return x | ||
x = y | ||
|
||
|
||
def is_perfect_square(n): | ||
''' | ||
If n is a perfect square it returns sqrt(n), | ||
otherwise returns -1 | ||
''' | ||
h = n & 0xF; #last hexadecimal "digit" | ||
|
||
if h > 9: | ||
return -1 # return immediately in 6 cases out of 16. | ||
|
||
# Take advantage of Boolean short-circuit evaluation | ||
if ( h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8 ): | ||
# take square root if you must | ||
t = isqrt(n) | ||
if t*t == n: | ||
return t | ||
else: | ||
return -1 | ||
|
||
return -1 | ||
|
||
#TEST functions | ||
|
||
def test_is_perfect_square(): | ||
print("Testing is_perfect_square") | ||
testsuit = [4, 0, 15, 25, 18, 901, 1000, 1024] | ||
|
||
for n in testsuit: | ||
print("Is ", n, " a perfect square?") | ||
if is_perfect_square(n)!= -1: | ||
print("Yes!") | ||
else: | ||
print("Nope") | ||
|
||
if __name__ == "__main__": | ||
test_is_perfect_square() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
''' | ||
Created on Dec 14, 2011 | ||
@author: pablocelayes | ||
''' | ||
|
||
def rational_to_contfrac (x, y): | ||
''' | ||
Converts a rational x/y fraction into | ||
a list of partial quotients [a0, ..., an] | ||
''' | ||
a = x//y | ||
if a * y == x: | ||
return [a] | ||
else: | ||
pquotients = rational_to_contfrac(y, x - a * y) | ||
pquotients.insert(0, a) | ||
return pquotients | ||
|
||
#TODO: efficient method that calculates convergents on-the-go, without doing partial quotients first | ||
def convergents_from_contfrac(frac): | ||
''' | ||
computes the list of convergents | ||
using the list of partial quotients | ||
''' | ||
convs = []; | ||
for i in range(len(frac)): | ||
convs.append(contfrac_to_rational(frac[0:i])) | ||
return convs | ||
|
||
def contfrac_to_rational (frac): | ||
'''Converts a finite continued fraction [a0, ..., an] | ||
to an x/y rational. | ||
''' | ||
if len(frac) == 0: | ||
return (0,1) | ||
elif len(frac) == 1: | ||
return (frac[0], 1) | ||
else: | ||
remainder = frac[1:len(frac)] | ||
(num, denom) = contfrac_to_rational(remainder) | ||
# fraction is now frac[0] + 1/(num/denom), which is | ||
# frac[0] + denom/num. | ||
return (frac[0] * num + denom, num) | ||
|
||
def test1(): | ||
''' | ||
Verify that the basic continued-fraction manipulation stuff works. | ||
''' | ||
testnums = [(1, 1), (1, 2), (5, 15), (27, 73), (73, 27)] | ||
for r in testnums: | ||
(num, denom) = r | ||
print('rational number:') | ||
print(r) | ||
|
||
contfrac = rational_to_contfrac (num, denom) | ||
print('continued fraction:') | ||
print(contfrac) | ||
|
||
print('convergents:') | ||
print(convergents_from_contfrac(contfrac)) | ||
print('***********************************') | ||
|
||
if __name__ == "__main__": | ||
test1() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import random, sys | ||
|
||
def miller_rabin_pass(a, s, d, n): | ||
''' | ||
n is an odd number with | ||
n-1 = (2^s)d, and d odd | ||
and a is the base: 1 < a < n-1 | ||
returns True iff n passes the MillerRabinTest for a | ||
''' | ||
a_to_power = pow(a, d, n) | ||
i=0 | ||
#Invariant: a_to_power = a^(d*2^i) mod n | ||
|
||
# we test whether (a^d) = 1 mod n | ||
if a_to_power == 1: | ||
return True | ||
|
||
# we test whether a^(d*2^i) = n-1 mod n | ||
# for 0<=i<=s-1 | ||
while(i < s-1): | ||
if a_to_power == n - 1: | ||
return True | ||
a_to_power = (a_to_power * a_to_power) % n | ||
i+=1 | ||
|
||
# we reach here if the test failed until i=s-2 | ||
return a_to_power == n - 1 | ||
|
||
def miller_rabin(n): | ||
''' | ||
Applies the MillerRabin Test to n (odd) | ||
returns True iff n passes the MillerRabinTest for | ||
K random bases | ||
''' | ||
#Compute s and d such that n-1 = (2^s)d, with d odd | ||
d = n-1 | ||
s = 0 | ||
while d%2 == 0: | ||
d >>= 1 | ||
s+=1 | ||
|
||
#Applies the test K times | ||
#The probability of a false positive is less than (1/4)^K | ||
K = 20 | ||
|
||
i=1 | ||
while(i<=K): | ||
# 1 < a < n-1 | ||
a = random.randrange(2,n-1) | ||
if not miller_rabin_pass(a, s, d, n): | ||
return False | ||
i += 1 | ||
|
||
return True | ||
|
||
def gen_prime(nbits): | ||
''' | ||
Generates a prime of b bits using the | ||
miller_rabin_test | ||
''' | ||
while True: | ||
p = random.getrandbits(nbits) | ||
#force p to have nbits and be odd | ||
p |= 2**nbits | 1 | ||
if miller_rabin(p): | ||
return p | ||
break | ||
|
||
def gen_prime_range(start, stop): | ||
''' | ||
Generates a prime within the given range | ||
using the miller_rabin_test | ||
''' | ||
while True: | ||
p = random.randrange(start,stop-1) | ||
p |= 1 | ||
if miller_rabin(p): | ||
return p | ||
break | ||
|
||
if __name__ == "__main__": | ||
if sys.argv[1] == "test": | ||
n = sys.argv[2] | ||
print (miller_rabin(n) and "PRIME" or "COMPOSITE") | ||
elif sys.argv[1] == "genprime": | ||
nbits = int(sys.argv[2]) | ||
print(gen_prime(nbits)) | ||
|
||
|
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
''' | ||
Created on Dec 14, 2011 | ||
@author: pablocelayes | ||
''' | ||
|
||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
"""\ | ||
This module generates RSA-keys which are vulnerable to | ||
the Wiener continued fraction attack | ||
(see RSAfracCont.pdf) | ||
The RSA keys are obtained as follows: | ||
1. Choose two prime numbers p and q | ||
2. Compute n=pq | ||
3. Compute phi(n)=(p-1)(q-1) | ||
4. Choose e coprime to phi(n) such that gcd(e,n)=1 | ||
5. Compute d = e^(-1) mod (phi(n)) | ||
6. e is the publickey; n is also made public (determines the block size); d is the privatekey | ||
Encryption is as follows: | ||
1. Size of data to be encrypted must be less than n | ||
2. ciphertext=pow(plaintext,publickey,n) | ||
Decryption is as follows: | ||
1. Size of data to be decrypted must be less than n | ||
2. plaintext=pow(ciphertext,privatekey,n) | ||
------------------------------- | ||
RSA-keys are Wiener-vulnerable if d < (n^(1/4))/sqrt(6) | ||
""" | ||
|
||
import random, MillerRabin, Arithmetic | ||
|
||
def getPrimePair(bits=512): | ||
''' | ||
genera un par de primos p , q con | ||
p de nbits y | ||
p < q < 2p | ||
''' | ||
|
||
assert bits%4==0 | ||
|
||
p = MillerRabin.gen_prime(bits) | ||
q = MillerRabin.gen_prime_range(p+1, 2*p) | ||
|
||
return p,q | ||
|
||
def generateKeys(nbits=1024): | ||
''' | ||
Generates a key pair | ||
public = (e,n) | ||
private = d | ||
such that | ||
n is nbits long | ||
(e,n) is vulnerable to the Wiener Continued Fraction Attack | ||
''' | ||
# nbits >= 1024 is recommended | ||
assert nbits%4==0 | ||
|
||
p,q = getPrimePair(nbits//2) | ||
n = p*q | ||
phi = Arithmetic.totient(p, q) | ||
|
||
# generate a d such that: | ||
# (d,n) = 1 | ||
# 36d^4 < n | ||
good_d = False | ||
while not good_d: | ||
d = random.getrandbits(nbits//4) | ||
if (Arithmetic.gcd(d,phi) == 1 and 36*pow(d,4) < n): | ||
good_d = True | ||
|
||
e = Arithmetic.modInverse(d,phi) | ||
return e,n,d | ||
|
||
if __name__ == "__main__": | ||
print("hey") | ||
for i in range(5): | ||
e,n,d = generateKeys() | ||
print ("Clave Publica:") | ||
print("e =") | ||
print(e) | ||
print("n =") | ||
print(n) | ||
print ("Clave Privada:") | ||
print("d =") | ||
print(d) | ||
print("-----------------------") |
Oops, something went wrong.