input: public_key (e,n) , data
It calculates the fingerprint of the provided public key sha1(public_key)
And then searches in ( fingerprint, d ) pair database for it.
Then it calculates $$m = sha1(current\_timestamp,data)$$ and $$signature = m^d (mod\ n)$$
output: current_timestamp, signature
Our task is to sign data with timestamp from 21.10.2005.
Signing service has a bug in searching procedure: only two bytes of fingerprint are compared. So it was possible to create public key with chosen n and bruteforce e with the same fingerprint which is used in real public_key.
After sending fake private key we are receiving: $$c=m^d mod\ n_{fake}$$ For given m,c,n when n is small we can bruteforce all x which satisfy conditions:
- $$0<=x<=\varphi(n)$$
- $$m^x = c\ ( mod\ n ) $$
- $$0<=x<=\varphi(n)$$
- $$m^x = c\ (\ mod\ n )$$
#!/usr/bin/env python
import sys
import hashlib
import os
import socket
import time
import base64
import random
import datetime
from crt import ChineseRemainder
hostport = ('54.217.0.233', 2407)
# interpret string as little-endian long integer
def bin2int(s):
return int(s[::-1].encode('hex'), 16)
# store integer as little-endian binary string of given length
def int2bin(i,n):
return ("%x" % i).rjust(n*2,'0').decode('hex')[::-1]
def solve_dlp(x,y,n,p):
solutions = set()
for i in xrange(0,n):
if pow(x,i,n) == y:
solutions.add(i % p)
return solutions
# read public key from file
with open("./pubkey0") as f:
n = bin2int(f.read(1024 / 8))
e = bin2int(f.read(32 / 8))
pubkeystring = int2bin(n,1024/8) + int2bin(e,32/8)
fingerprint=hashlib.sha1(pubkeystring).digest()
SIEVE_LEN = 1000000
sieve = [0]*SIEVE_LEN
for i in xrange(2,SIEVE_LEN):
if sieve[i] == 0:
j=2*i
while j < SIEVE_LEN:
sieve[j] = 1
j+=i
primes_product = 1
crt = []
last_index = 100000
while primes_product < n:
while sieve[last_index] == 1 or sieve[last_index*2+1] == 1:
last_index+=1
fake_n = 2*last_index+1
prime = last_index
last_index+=1
print 'using primes: ',fake_n,prime
fake_e = 0
while 1:
fake_pubkeystring = int2bin(fake_n,1024/8) + int2bin(fake_e,32/8)
fake_fingerprint=hashlib.sha1(fake_pubkeystring).digest()
if fake_fingerprint[:2] == fingerprint[:2]:
break
fake_e += 1
s = socket.create_connection(hostport)
data = str(random.randint(0,1000))
s.send(fake_pubkeystring + int2bin(len(data),32/8) + data)
responsestring = ''
while True:
tmp = s.recv(1024)
if not tmp: break
responsestring += tmp
ts = bin2int(responsestring[:4])
sig = bin2int(responsestring[4:])
if sig==0:
continue
m = bin2int(hashlib.sha1(int2bin(ts, 32/8) + data).digest() )
m %= fake_n
print 'received sig: ',sig
print 'm: ',m
x = solve_dlp(m,sig,fake_n,prime)
if len(x)==1:
x=x.pop()
print 'solved: ',x
crt.append( (x, prime) )
primes_product *= prime
d = ChineseRemainder(crt)[0]
print 'private_key: ',d
ts = int(( datetime.datetime(2015, 10, 21) - datetime.datetime(1970, 1, 1)).total_seconds())
m = bin2int(hashlib.sha1(int2bin(ts, 32/8) + data).digest() )
sig = pow(m,d,n)
responsestring = int2bin(ts, 4) + int2bin(sig, 1024/8)
print base64.b64encode(pubkeystring + responsestring + data)

No comments:
Post a Comment