diff --git a/Client1/client_dynamic.py b/Client1/client_dynamic.py index 7fa4182..556ccb5 100755 --- a/Client1/client_dynamic.py +++ b/Client1/client_dynamic.py @@ -3,11 +3,11 @@ import time import os -print("Running dragonfly Private") +print("Running SIDH Private") while True: # secretkey = os.path.isfile('secret.key.hacklab') # print(secretkey) # if not (secretkey): # while True: - os.system('python3 dragonfly_private_client.py') \ No newline at end of file + os.system('python3 sidh_private_client.py') \ No newline at end of file diff --git a/Client1/client_dynamic2.py b/Client1/client_dynamic2.py index c233803..5cff8e8 100755 --- a/Client1/client_dynamic2.py +++ b/Client1/client_dynamic2.py @@ -3,8 +3,8 @@ import time import os -print("Running dragonfly cipher") +print("Running SIDH cipher") while True: - os.system('python3 dragonfly_cipher_client.py') + os.system('python3 sidh_cipher_client.py') # time.sleep(5) # os.remove('secret.key.hacklab') \ No newline at end of file diff --git a/Client1/declaration.asn b/Client1/declaration.asn index d5b25f1..ab763f0 100755 --- a/Client1/declaration.asn +++ b/Client1/declaration.asn @@ -12,12 +12,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String - } - - DataStaAp ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Client1/dragonfly_private_client.py b/Client1/dragonfly_private_client.py deleted file mode 100755 index 1409bf1..0000000 --- a/Client1/dragonfly_private_client.py +++ /dev/null @@ -1,692 +0,0 @@ -#!/usr/bin/env python3 - -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" -import time -import hashlib -import random -import logging -import socket -import re, uuid -import base64 -import os -import subprocess -from collections import namedtuple -from Cryptodome.Cipher import AES -from Cryptodome import Random -import asn1tools -import sys - -#Compile asn1 file for secret_key -asn1_file = asn1tools.compile_files('declaration.asn') - -#create tcp/ip socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -#retrieve local hostname -local_hostname = socket.gethostname() - -#get fully qualified hostname -local_fqdn = socket.getfqdn() - -#get the according ip address -ip_address = socket.gethostbyname(local_hostname) - -server_address = ('192.168.0.3', 4380) -while True: - try: - sock.connect(server_address) - break - except ConnectionRefusedError as conn_error: - print('A connection error has occured') - print(conn_error) - print('Attempting to connect to server again...') - time.sleep(5) - except KeyboardInterrupt: - print('Ctrl-C pressed to terminate program') - pass - except: - print('Unexpected error:', sys.exc_info()[0]) - -print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) - -logger = logging.getLogger('dragonfly') -logger.setLevel(logging.INFO) -# create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') -fh.setLevel(logging.DEBUG) -# create console handler with a higher log level -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -# create formatter and add it to the handlers -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -ch.setFormatter(formatter) -fh.setFormatter(formatter) -# add the handlers to logger -logger.addHandler(ch) -logger.addHandler(fh) - - -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest - - -def decrypting(key, filename): - chunksize = 64 * 1024 - outputFile = filename.split('.hacklab')[0] - - with open(filename, 'rb') as infile: - filesize = int(infile.read(16)) - IV = infile.read(16) - decryptor = AES.new(key, AES.MODE_CBC, IV) - - with open(outputFile, 'wb') as outfile: - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - outfile.write(decryptor.decrypt(chunk)) - outfile.truncate(filesize) - - return outputFile - -def handshake(): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print (own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - sock.send(own_mac_BER) - raw_other_mac = sock.recv(1024) - - #decode BER and get mac address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Send BER encodewd Scalar / element ap to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement',{'data':scalar_complete}) - sock.sendall(scalar_element_BER) - print() - print('data send', scalar_complete) - logger.info('Computing shared secret...\n') - - - #receive BER encoded scalar / element ap - scalar_element_ap_BER = sock.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_BER) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - # scalar_element_ap = sock.recv(1024).decode() - print('scalar element received ', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode sta_token to be BER encoded and send to peer - staToken_encoded = asn1_file.encode('DataStaAp',{'data':sta_token}) - sock.send(staToken_encoded) - - # sock.send(sta_token.encode()) - print("sta_token", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Receive BER encoded AP Token and decode it - apToken_encoded = sock.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - - #encrypted = sock.recv(1024).decode() - #print ("Encrypted ciphertext: ", encrypted) - - # Decrypt using PMK_Key - #decrypted = decrypt(encrypted, PMK_Key) - #print (decrypted.decode()) - - # Open the received secret file from the key generator - with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: - print ('File opened...\n') - while True: - # print ('Receiving data...\n') - secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) - if (len(secret_key_BER) > 10): - keys_decoded = asn1_file.decode('DataKey', secret_key_BER) - secret_key = keys_decoded.get('key') - nbit_key = keys_decoded.get('nbit') - else: - break - if not (secret_key or nbit_key): - break - s.write(secret_key) - t.write(nbit_key) - - s.close() - t.close() - - print ('Successfully got the files\n') - - print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) - print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) - - print ('Decrypting the files...\n') - - decrypted_secret_key = decrypting(PMK_Key, 'secret.key.hacklab') - print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) - os.system("md5sum secret.key") - - decrypted_nbit_key = decrypting(PMK_Key, 'nbit.key.hacklab') - print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) - os.system("md5sum nbit.key") - -def tests(): - """ - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - - -if __name__ == '__main__': - #tests() - handshake() - - sock.close() diff --git a/Client1/dragonfly_cipher_client.py b/Client1/sidh_cipher_client.py old mode 100755 new mode 100644 similarity index 100% rename from Client1/dragonfly_cipher_client.py rename to Client1/sidh_cipher_client.py diff --git a/Client1/sidh_private_client.py b/Client1/sidh_private_client.py new file mode 100644 index 0000000..aee7424 --- /dev/null +++ b/Client1/sidh_private_client.py @@ -0,0 +1,1047 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +server_address = ('192.168.0.3', 4380) +while True: + try: + sock.connect(server_address) + print("Successfully connected to Keygen") + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to server again...') + time.sleep(5) + except KeyboardInterrupt: + print('Ctrl-C pressed to terminate program') + pass + except: + print('Unexpected error:', sys.exc_info()[0]) + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() diff --git a/Client2/client_dynamic.py b/Client2/client_dynamic.py index 167650e..0636012 100755 --- a/Client2/client_dynamic.py +++ b/Client2/client_dynamic.py @@ -3,11 +3,11 @@ import time import os -print("Running dragonfly Private") +print("Running SIDH Private") while True: # secretkey = os.path.isfile('secret.key.hacklab') # print('secret key exist?',secretkey) # if not (secretkey): # while True: - os.system('python3 dragonfly_private_client.py') + os.system('python3 sidh_private_client.py') diff --git a/Client2/client_dynamic2.py b/Client2/client_dynamic2.py index 2f0a610..a08d44a 100755 --- a/Client2/client_dynamic2.py +++ b/Client2/client_dynamic2.py @@ -3,9 +3,9 @@ import time import os -print("Running dragonfly cipher") +print("Running SIDH cipher") while True: - os.system('python3 dragonfly_cipher_client.py') + os.system('python3 sidh_cipher_client.py') # time.sleep(5) # os.remove('secret.key.hacklab') diff --git a/Client2/declaration.asn b/Client2/declaration.asn index d5b25f1..ab763f0 100755 --- a/Client2/declaration.asn +++ b/Client2/declaration.asn @@ -12,12 +12,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String - } - - DataStaAp ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Client2/dragonfly_private_client.py b/Client2/dragonfly_private_client.py deleted file mode 100755 index 1374bf7..0000000 --- a/Client2/dragonfly_private_client.py +++ /dev/null @@ -1,694 +0,0 @@ -#!/usr/bin/env python3 - -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" -import time -import hashlib -import random -import logging -import socket -import re, uuid -import base64 -import os -import subprocess -from collections import namedtuple -from Cryptodome.Cipher import AES -from Cryptodome import Random -import asn1tools -import sys - - -#Compile asn1 file for secret_key -asn1_file = asn1tools.compile_files('declaration.asn') - -#create tcp/ip socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -#retrieve local hostname -local_hostname = socket.gethostname() - -#get fully qualified hostname -local_fqdn = socket.getfqdn() - -#get the according ip address -ip_address = socket.gethostbyname(local_hostname) - -#bind socket to port -# server_address = ('192.168.0.3', 65433) -server_address = ('192.168.0.3', 4380) -while True: - try: - sock.connect(server_address) - break - except ConnectionRefusedError as conn_error: - print('A connection error has occured') - print(conn_error) - print('Attempting to connect to server again...') - time.sleep(5) - except KeyboardInterrupt: - print('Ctrl-C pressed to terminate program') - pass - except: - print('Unexpected error:', sys.exc_info()[0]) - -print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) - -logger = logging.getLogger('dragonfly') -logger.setLevel(logging.INFO) -# create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') -fh.setLevel(logging.DEBUG) -# create console handler with a higher log level -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -# create formatter and add it to the handlers -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -ch.setFormatter(formatter) -fh.setFormatter(formatter) -# add the handlers to logger -logger.addHandler(ch) -logger.addHandler(fh) - - -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest - - -def decrypting(key, filename): - chunksize = 64 * 1024 - outputFile = filename.split('.hacklab')[0] - - with open(filename, 'rb') as infile: - filesize = int(infile.read(16)) - IV = infile.read(16) - decryptor = AES.new(key, AES.MODE_CBC, IV) - - with open(outputFile, 'wb') as outfile: - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - outfile.write(decryptor.decrypt(chunk)) - outfile.truncate(filesize) - - return outputFile - -def handshake(): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print (own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - sock.send(own_mac_BER) - raw_other_mac = sock.recv(1024) - - #decode BER and get mac address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Send BER encodewd Scalar / element ap to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement',{'data':scalar_complete}) - sock.sendall(scalar_element_BER) - print() - print('data send', scalar_complete) - logger.info('Computing shared secret...\n') - - - #receive BER encoded scalar / element ap - scalar_element_ap_BER = sock.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_BER) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - # scalar_element_ap = sock.recv(1024).decode() - print('scalar element received ', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode sta_token to be BER encoded and send to peer - staToken_encoded = asn1_file.encode('DataStaAp',{'data':sta_token}) - sock.send(staToken_encoded) - - # sock.send(sta_token.encode()) - print("sta_token", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Receive BER encoded AP Token and decode it - apToken_encoded = sock.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - - #encrypted = sock.recv(1024).decode() - #print ("Encrypted ciphertext: ", encrypted) - - # Decrypt using PMK_Key - #decrypted = decrypt(encrypted, PMK_Key) - #print (decrypted.decode()) - - # Open the received secret file from the key generator - with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: - print ('File opened...\n') - while True: - # print ('Receiving data...\n') - secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) - if (len(secret_key_BER) > 10): - keys_decoded = asn1_file.decode('DataKey', secret_key_BER) - secret_key = keys_decoded.get('key') - nbit_key = keys_decoded.get('nbit') - else: - break - if not (secret_key or nbit_key): - break - s.write(secret_key) - t.write(nbit_key) - - s.close() - t.close() - - print ('Successfully got the files\n') - - print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) - print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) - - print ('Decrypting the files...\n') - - decrypted_secret_key = decrypting(PMK_Key, 'secret.key.hacklab') - print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) - os.system("md5sum secret.key") - - decrypted_nbit_key = decrypting(PMK_Key, 'nbit.key.hacklab') - print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) - os.system("md5sum nbit.key") - -def tests(): - """ - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - - -if __name__ == '__main__': - #tests() - handshake() - sock.close() diff --git a/Client2/dragonfly_cipher_client.py b/Client2/sidh_cipher_client.py old mode 100755 new mode 100644 similarity index 100% rename from Client2/dragonfly_cipher_client.py rename to Client2/sidh_cipher_client.py diff --git a/Client2/sidh_private_client.py b/Client2/sidh_private_client.py new file mode 100644 index 0000000..aee7424 --- /dev/null +++ b/Client2/sidh_private_client.py @@ -0,0 +1,1047 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +server_address = ('192.168.0.3', 4380) +while True: + try: + sock.connect(server_address) + print("Successfully connected to Keygen") + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to server again...') + time.sleep(5) + except KeyboardInterrupt: + print('Ctrl-C pressed to terminate program') + pass + except: + print('Unexpected error:', sys.exc_info()[0]) + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() diff --git a/Client3/client_dynamic.py b/Client3/client_dynamic.py index 5e25a4d..38a970e 100755 --- a/Client3/client_dynamic.py +++ b/Client3/client_dynamic.py @@ -3,9 +3,9 @@ import time import os -print("Running dragonfly Private") +print("Running SIDH Private") while True: # secretkey = os.path.isfile('secret.key.hacklab') # print(secretkey) # if not (secretkey): - os.system('python3 dragonfly_private_client.py') + os.system('python3 sidh_private_client.py') diff --git a/Client3/client_dynamic2.py b/Client3/client_dynamic2.py index c233803..5cff8e8 100755 --- a/Client3/client_dynamic2.py +++ b/Client3/client_dynamic2.py @@ -3,8 +3,8 @@ import time import os -print("Running dragonfly cipher") +print("Running SIDH cipher") while True: - os.system('python3 dragonfly_cipher_client.py') + os.system('python3 sidh_cipher_client.py') # time.sleep(5) # os.remove('secret.key.hacklab') \ No newline at end of file diff --git a/Client3/declaration.asn b/Client3/declaration.asn index d5b25f1..ab763f0 100755 --- a/Client3/declaration.asn +++ b/Client3/declaration.asn @@ -12,12 +12,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String - } - - DataStaAp ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Client3/dragonfly_private_client.py b/Client3/dragonfly_private_client.py index 40b0c6d..aee7424 100755 --- a/Client3/dragonfly_private_client.py +++ b/Client3/dragonfly_private_client.py @@ -1,19 +1,11 @@ #!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" import time import hashlib import random @@ -28,6 +20,12 @@ from Cryptodome import Random import asn1tools import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() #Compile asn1 file for secret_key asn1_file = asn1tools.compile_files('declaration.asn') @@ -44,11 +42,11 @@ #get the according ip address ip_address = socket.gethostbyname(local_hostname) -#bind socket to port server_address = ('192.168.0.3', 4380) while True: try: sock.connect(server_address) + print("Successfully connected to Keygen") break except ConnectionRefusedError as conn_error: print('A connection error has occured') @@ -63,10 +61,10 @@ print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) -logger = logging.getLogger('dragonfly') +logger = logging.getLogger('Key Exchange') logger.setLevel(logging.INFO) # create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') +fh = logging.FileHandler('keyexchange.log') fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() @@ -80,453 +78,837 @@ logger.addHandler(fh) -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest - - + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys def decrypting(key, filename): chunksize = 64 * 1024 outputFile = filename.split('.hacklab')[0] @@ -547,147 +929,119 @@ def decrypting(key, filename): return outputFile def handshake(): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print (own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - sock.send(own_mac_BER) - raw_other_mac = sock.recv(1024) - - #decode BER and get mac address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Send BER encodewd Scalar / element ap to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement',{'data':scalar_complete}) - sock.sendall(scalar_element_BER) - print() - print('data send', scalar_complete) - logger.info('Computing shared secret...\n') - - - #receive BER encoded scalar / element ap - scalar_element_ap_BER = sock.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_BER) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - # scalar_element_ap = sock.recv(1024).decode() - print('scalar element received ', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode sta_token to be BER encoded and send to peer - staToken_encoded = asn1_file.encode('DataStaAp',{'data':sta_token}) - sock.send(staToken_encoded) - - # sock.send(sta_token.encode()) - print("sta_token", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Receive BER encoded AP Token and decode it - apToken_encoded = sock.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - - #encrypted = sock.recv(1024).decode() - #print ("Encrypted ciphertext: ", encrypted) - - # Decrypt using PMK_Key - #decrypted = decrypt(encrypted, PMK_Key) - #print (decrypted.decode()) - - # Open the received secret file from the key generator - with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: - print ('File opened...\n') - while True: - # print ('Receiving data...\n') - secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) - if (len(secret_key_BER) > 10): - keys_decoded = asn1_file.decode('DataKey', secret_key_BER) - secret_key = keys_decoded.get('key') - nbit_key = keys_decoded.get('nbit') - else: - break - if not (secret_key or nbit_key): - break - s.write(secret_key) - t.write(nbit_key) - - s.close() - t.close() + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() print ('Successfully got the files\n') - print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) - print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) - print ('Decrypting the files...\n') + print ('Decrypting the files...\n') - decrypted_secret_key = decrypting(PMK_Key, 'secret.key.hacklab') - print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) - os.system("md5sum secret.key") + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") - decrypted_nbit_key = decrypting(PMK_Key, 'nbit.key.hacklab') - print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) - os.system("md5sum nbit.key") - -def tests(): - """ - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") +####################################################################### if __name__ == '__main__': - #tests() handshake() - sock.close() diff --git a/Client3/sidh_cipher_client.py b/Client3/sidh_cipher_client.py new file mode 100644 index 0000000..5985923 --- /dev/null +++ b/Client3/sidh_cipher_client.py @@ -0,0 +1,176 @@ +#!/user/bin/env/python3 + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os, random, struct +import subprocess +import asn1tools +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +from Cryptodome.Hash import SHA256 +from optparse import * +import select +import sys + +asn1_file = asn1tools.compile_files("declaration.asn") + +#Create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = "192.168.0.23" + +#bind socket +own_address = (ip_address, 4381) +print("Starting up on %s port %s" % own_address) +sock.bind(own_address) + + + +def cipher(): + + sock.listen(1) + connection, cloud_address = sock.accept() + with connection: + print("Connecting from", cloud_address) + + + # Run Adder_alice to get ciphertext + print("Getting ciphertext...\n") + subprocess.call("./alice") + print("Printing ciphertext...\n") + cloud_data = "cloud.data" + print("This file ", cloud_data, "is our ciphertext\n") + + f = open(cloud_data, "rb") + content = f.read(8192) + print(content) + + # Send the file size of the data to the cloud server + fsize = os.path.getsize(cloud_data) + print("This is the fsize" ,fsize) + + # Send fsize in BER NOTE: COMPLETE + fsize_encoded = asn1_file.encode('DataFsize',{'data':fsize}) + +##### + while True: + #Send file size to cloud + connection.send(fsize_encoded) + + #Receive msg + encoded_msg = connection.recv(1024) + msg = encoded_msg.decode() + + print(fsize_encoded) + + #If success, break + if (msg == "success"): + break + else: + continue + + #set offset value + offset_value = 0 + + BUFFER_SIZE = 1024 + with open(cloud_data, 'rb') as f: + while True: + print("offset value", f.tell()) + + #Before offset + before_offset = f.tell() + content = f.read(BUFFER_SIZE) + + #BER encode data and send + data_encoded = asn1_file.encode('DataContent', {'data':content}) + connection.send(data_encoded) + + #After offset + after_offset = f.tell() + + #Received msg + encode_msg = connection.recv(1024) + msg = encode_msg.decode() + print("msg", msg) + + if(msg == "success"): + if(int(after_offset == fsize)): + break + else: + continue + else: + #offset differences + offset_differences = int(after_offset) - int(before_offset) + + #offset values + offset_value = int(after_offset) - int(offset_differences) + f.seek(offset_value) + + f.close() +##### + # Get the file size of sent data + print("Original file size: ", os.path.getsize(cloud_data)) + print("The CLOUD will send the computed answer to OUTPUT.\n") +##### + + # buffer_size = 10 + # Decode BER indication data received from cloud + while True: + try: + irecv = connection.recv(1024) + indication = asn1_file.decode('DataIndicator', irecv) + indication_decoded = indication.get('data') + print(indication_decoded) + + msg = "success" + connection.send(msg.encode()) + + #break out of while loop + break + + except: + #print out err msg + print('An error has occured', sys.exc_info()[0]) + + #empty out data from socket + read_con = [connection] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # buffer_size = int(input("size of buffer?")) + + msg = "fail" + connection.send(msg.encode()) + continue +##### + sock.close() + + +if __name__ == '__main__': + cipher() + + + + + + + diff --git a/Client3/sidh_private_client.py b/Client3/sidh_private_client.py new file mode 100644 index 0000000..aee7424 --- /dev/null +++ b/Client3/sidh_private_client.py @@ -0,0 +1,1047 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +server_address = ('192.168.0.3', 4380) +while True: + try: + sock.connect(server_address) + print("Successfully connected to Keygen") + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to server again...') + time.sleep(5) + except KeyboardInterrupt: + print('Ctrl-C pressed to terminate program') + pass + except: + print('Unexpected error:', sys.exc_info()[0]) + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() diff --git a/Cloud/cloud.c b/Cloud/cloud.c index 857a4f8..b2602fd 100755 --- a/Cloud/cloud.c +++ b/Cloud/cloud.c @@ -648,7 +648,7 @@ void mul128(LweSample *result, LweSample *result2,LweSample *result3,LweSample * int main() { - // dragonfly_cipher_cloud should have already appended 2 cipherstreams into cloud.data + // sidh_cipher_cloud should have already appended 2 cipherstreams into cloud.data printf("Reading the key...\n"); diff --git a/Cloud/cloud_dynamic.py b/Cloud/cloud_dynamic.py index 1a40af8..ec6fc19 100755 --- a/Cloud/cloud_dynamic.py +++ b/Cloud/cloud_dynamic.py @@ -7,4 +7,4 @@ # cloudkey = os.path.isfile('cloud.key.hacklab') # print(cloudkey) # if not (cloudkey): - os.system('python3 dragonfly_public_cloud.py') \ No newline at end of file + os.system('python3 sidh_public_cloud.py') \ No newline at end of file diff --git a/Cloud/cloud_dynamic2.py b/Cloud/cloud_dynamic2.py index c6fc06d..1446549 100755 --- a/Cloud/cloud_dynamic2.py +++ b/Cloud/cloud_dynamic2.py @@ -4,7 +4,7 @@ import os while True: - os.system('python3 dragonfly_cipher_cloud.py') + os.system('python3 sidh_cipher_cloud.py') # print("waiting 10 sec") # time.sleep(10) # os.remove('cloud.key.hacklab') \ No newline at end of file diff --git a/Cloud/declaration.asn b/Cloud/declaration.asn index 693767f..172411c 100755 --- a/Cloud/declaration.asn +++ b/Cloud/declaration.asn @@ -34,12 +34,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER } - DataStaAp ::= SEQUENCE { - data IA5String + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Cloud/dragonfly_cipher_cloud.py b/Cloud/dragonfly_cipher_cloud.py index 525edd6..68f95b5 100755 --- a/Cloud/dragonfly_cipher_cloud.py +++ b/Cloud/dragonfly_cipher_cloud.py @@ -5,18 +5,25 @@ import socket import re, uuid import base64 -import os, random, struct +import os import subprocess -import asn1tools from collections import namedtuple from Cryptodome.Cipher import AES from Cryptodome import Random -from Cryptodome.Hash import SHA256 -from optparse import * +import asn1tools import sys -import select +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() -asn1_file = asn1tools.compile_files("declaration.asn") +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #retrieve local hostname local_hostname = socket.gethostname() @@ -24,11 +31,12 @@ #get fully qualified hostname local_fqdn = socket.getfqdn() -########################################################## -logger = logging.getLogger('dragonfly') +#################################################################################### + +logger = logging.getLogger('Key Exchange') logger.setLevel(logging.INFO) # create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') +fh = logging.FileHandler('keyexchange.log') fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() @@ -37,458 +45,842 @@ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) fh.setFormatter(formatter) - +# add the handlers to logger logger.addHandler(ch) logger.addHandler(fh) -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) - assert self.curve.valid(self.peer_element) + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - self.k = K[0] +###################################################################### - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - return self.token +###################################################################### - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK +###################################################################### - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - Key derivation function from Section B.5.1 of [FIPS186-4] +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) +####################################################################### - assert len(binary_repr) == n +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z - logger.debug('Rand={}'.format(binary_repr)) - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) +###################################################################### - logger.debug('C={}'.format(C)) - #k = (C % (n - 1)) + 1 +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob - k = C +###################################################################### - logger.debug('k={}'.format(k)) +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 - return k +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys def decrypting(key, filename): chunksize = 64 * 1024 outputFile = filename.split('.hacklab')[0] @@ -508,227 +900,221 @@ def decrypting(key, filename): return outputFile - def handshake(): - #Own MAC address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print ("my own MAC",own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - #Send own MAC address, BER encoded to peer - sock_output.send(own_mac_BER) - raw_other_mac = sock_output.recv(1024) - - #decode BER and get MAC address from peer - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('MAC Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Attempt to send BER encoded Scalar / Element AP to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement', {'data':scalar_complete}) - sock_output.sendall(scalar_element_BER) - print() - print("Scalar element send", scalar_complete) - logger.info('Computing shared secret...\n') - - #Received BER encoded Scalar / Element AP - scalar_element_ap_encoded = sock_output.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_encoded) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - print('Scalar element received', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode STA_Token to be BER encoded and send to peer - staToken_BER = asn1_file.encode('DataStaAp', {'data':sta_token}) - sock_output.send(staToken_BER) - print("sta_token ", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Received BER encoded AP Token and decode it - apToken_encoded = sock_output.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - # time.sleep(1000) - - # Open the received cloud key from the key generator - # open('OPERATION_AND_BER.hacklab', 'wb') as s: - # print ('File opened...\n') - # while True: + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Output found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock_output.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock_output.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + +####################################################################### + -##### #Testing phase # buffer_size = 1 - buffer_size = 1024 - while True: - try: - #Listening and receive data - OPERATION_AND_IP_BER = sock_output.recv(buffer_size) + buffer_size = 1024 + while True: + try: + #Listening and receive data + OPERATION_AND_IP_BER = sock_output.recv(buffer_size) - print("length of operation and ip", len(OPERATION_AND_IP_BER)) - print("Operation and IP BER", OPERATION_AND_IP_BER) + print("length of operation and ip", len(OPERATION_AND_IP_BER)) + print("Operation and IP BER", OPERATION_AND_IP_BER) - #Decode BER - OPERATION_AND_IP_decoded = asn1_file.decode('DataUserInput', OPERATION_AND_IP_BER) - - #Send success msg to output - msg = "success" - sock_output.send(msg.encode()) + #Decode BER + OPERATION_AND_IP_decoded = asn1_file.decode('DataUserInput', OPERATION_AND_IP_BER) - #Break out of while loop if succeed - break + #Send success msg to output + msg = "success" + sock_output.send(msg.encode()) - except: - #Print error message - print("An error occured", sys.exc_info()[0]) + #Break out of while loop if succeed + break - #Empty out data from socket - read_con = [sock_output] - while True: - r, w, e = select.select(read_con, [], [], 0.0) - if len(r) == 0: - break - else: - for data in r: - data.recv(1024) - - #Testing phase - # buffer_size = int(input("Size of buffer?")) - - #Send fail message to output - msg = "fail" - sock_output.send(msg.encode()) - continue -##### + except: + #Print error message + print("An error occured", sys.exc_info()[0]) - #Print out operation and IP - print("Operation and IP data", OPERATION_AND_IP_decoded) - - # Define global lists for ipaddresses and operation codes - global ipList - ipList = [] - - global opList - opList = [] - - global numClList - numClList = [] - - # Create variables for each ipaddress and operation - for k1, v1 in OPERATION_AND_IP_decoded.items(): - for k2,v2 in v1.items(): - locals()[k2] = k2 - print('key',k2) - - postfix_expr = OPERATION_AND_IP_decoded['postfix']['postfix'] - print('Postfix: ',postfix_expr) - p = open('postfix.hacklab', 'wb') - p.write(postfix_expr) - p.close() - decrypting(PMK_Key, 'postfix.hacklab') - p = open('postfix', 'r') - postfix = str(p.read()) - p.close() - print("The postfix expression is: ",postfix) - global postfixList - postfixList = " ".join(postfix) - postfixList = postfixList.split() - print("POSTFIX LIST", postfixList) - - global flip - flip = False - - numClList = re.findall("[a-zA-Z]", postfix) - sock_output.close() - - # iterate through postfixList to sort alpha char and operators - x = 0 - y = 0 - for i in postfixList: - if (i.isalpha()): - CLI = OPERATION_AND_IP_decoded['ipaddress']['ipaddress{0}'.format(x+1)] - print("Client :", CLI) - c = open("client.hacklab", "wb") - c.write(CLI) - c.close() - decrypting(PMK_Key, 'client.hacklab') - c = open("client", "r") - CLIENT = c.read() - c.close() - ipList.append(CLIENT) - print(ipList) - os.remove('client') - os.remove('client.hacklab') - x = x + 1 - else: - OPCODE = OPERATION_AND_IP_decoded['operation']['operation{0}'.format(y+1)] - print("Opcode:", OPCODE) - o = open("opcode.hacklab","wb") - o.write(OPCODE) - o.close() - decrypting(PMK_Key, 'opcode.hacklab') - o = open("opcode", "r") - OPCODE = o.read() - o.close() - opList.append(OPCODE) - print(opList) - os.remove('opcode') - os.remove('opcode.hacklab') - y = y + 1 - - print(len(ipList)) - if (len(ipList) == 1) or (len(numClList) ==1): - print(ipList) - compute_final() - elif (len(ipList) > 1) and (len(numClList) >1): - if (len(ipList) == 2): - flip = True - else: - flip = False + #Empty out data from socket + read_con = [sock_output] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # buffer_size = int(input("Size of buffer?")) + + #Send fail message to output + msg = "fail" + sock_output.send(msg.encode()) + continue + ##### + + #Print out operation and IP + print("Operation and IP data", OPERATION_AND_IP_decoded) + + # Define global lists for ipaddresses and operation codes + global ipList + ipList = [] + + global opList + opList = [] + + global numClList + numClList = [] + + # Create variables for each ipaddress and operation + for k1, v1 in OPERATION_AND_IP_decoded.items(): + for k2,v2 in v1.items(): + locals()[k2] = k2 + print('key',k2) + + postfix_expr = OPERATION_AND_IP_decoded['postfix']['postfix'] + print('Postfix: ',postfix_expr) + p = open('postfix.hacklab', 'wb') + p.write(postfix_expr) + p.close() + decrypting(SK, 'postfix.hacklab') + p = open('postfix', 'r') + postfix = str(p.read()) + p.close() + print("The postfix expression is: ",postfix) + global postfixList + postfixList = " ".join(postfix) + postfixList = postfixList.split() + print("POSTFIX LIST", postfixList) + + global flip + flip = False + + numClList = re.findall("[a-zA-Z]", postfix) + sock_output.close() + + # iterate through postfixList to sort alpha char and operators + x = 0 + y = 0 + for i in postfixList: + if (i.isalpha()): + CLI = OPERATION_AND_IP_decoded['ipaddress']['ipaddress{0}'.format(x+1)] + print("Client :", CLI) + c = open("client.hacklab", "wb") + c.write(CLI) + c.close() + decrypting(SK, 'client.hacklab') + c = open("client", "r") + CLIENT = c.read() + c.close() + ipList.append(CLIENT) print(ipList) - computation() + os.remove('client') + os.remove('client.hacklab') + x = x + 1 else: - None - answer() - sys.exit() + OPCODE = OPERATION_AND_IP_decoded['operation']['operation{0}'.format(y+1)] + print("Opcode:", OPCODE) + o = open("opcode.hacklab","wb") + o.write(OPCODE) + o.close() + decrypting(SK, 'opcode.hacklab') + o = open("opcode", "r") + OPCODE = o.read() + o.close() + opList.append(OPCODE) + print(opList) + os.remove('opcode') + os.remove('opcode.hacklab') + y = y + 1 + + print(len(ipList)) + if (len(ipList) == 1) or (len(numClList) ==1): + print(ipList) + compute_final() + elif (len(ipList) > 1) and (len(numClList) >1): + if (len(ipList) == 2): + flip = True + else: + flip = False + print(ipList) + computation() + else: + None + answer() + sys.exit() ########################################################## @@ -1077,7 +1463,7 @@ def cipher2(client_address): except: print('Unexpected error: ',sys.exc_info()[0]) -##### + ##### BUFFER_SIZE = 1032 #Testing phase @@ -1085,7 +1471,7 @@ def cipher2(client_address): with open('cloud.data', 'wb') as f: print ('Cloud data file opened...\n') - + #Size of cloud data while True: try: @@ -1099,7 +1485,7 @@ def cipher2(client_address): #Break out of while loop if succeed break - + except: #Print error message print("An error occured", sys.exc_info()[0]) @@ -1113,10 +1499,10 @@ def cipher2(client_address): else: for data in r: data.recv(1024) - + #Testing phase # BUFFER_SIZE = int(input("Size of buffer?")) - + #Send fail message to output msg = "fail" sock.send(msg.encode()) @@ -1124,13 +1510,13 @@ def cipher2(client_address): #Received size rsize = 0 - + # Decode received cloud data from BER while True: try: while True: print ('Receiving cloud data...\n') - + #BER received and decode cloud_data = sock.recv(BUFFER_SIZE) @@ -1181,14 +1567,14 @@ def cipher2(client_address): print ('Received everything.. Breaking out') break -##### + ##### f.close() print ('Successfully got the file\n') # Send notice to the client; indicator = asn1_file.encode('DataIndicator', {'data':"Hello! cloud.data received"}) -###### + ###### while True: sock.send(indicator) #Receive msg @@ -1202,7 +1588,7 @@ def cipher2(client_address): break else: continue -###### + ###### print("Sending an indicator...\n") print('Cloud data file size: ', os.path.getsize('cloud.data')) sock.close() @@ -1371,7 +1757,7 @@ def answer(): # with open('answer.data', 'rb') as f: # answer = f.read(1032) -##### + ##### #Get size of answer.data answ_data_size = os.path.getsize("answer.data") print("Size of answer.data is", answ_data_size) @@ -1390,14 +1776,14 @@ def answer(): #BER encode answer & send answer_encoded = asn1_file.encode('DataAnswer', {'data':answer}) sock_output2.sendall(answer_encoded) - + #after offset after_offset = f.tell() #receving msg encode_msg = sock_output2.recv(1024) msg = encode_msg.decode() - + #success message if (msg == "success"): #close socket if all data send @@ -1413,12 +1799,12 @@ def answer(): else: #offset differences offset_differences = int(after_offset) - int(before_offset) - + #offset value offset_value = int(after_offset) - int(offset_differences) f.seek(offset_value) -##### + ##### f.close() print("File size of computed answer file: ", os.path.getsize(answer_data)) os.system("md5sum answer.data") @@ -1440,6 +1826,7 @@ def answer(): while True: try: sock_output.connect(output_address) + print('Connected to output') break except ConnectionRefusedError as conn_error: print('A connection error has occured') diff --git a/Cloud/dragonfly_public_cloud.py b/Cloud/dragonfly_public_cloud.py index 71323b8..0bce389 100755 --- a/Cloud/dragonfly_public_cloud.py +++ b/Cloud/dragonfly_public_cloud.py @@ -1,19 +1,11 @@ #!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" import time import hashlib import random @@ -22,13 +14,20 @@ import re, uuid import base64 import os +import subprocess from collections import namedtuple from Cryptodome.Cipher import AES from Cryptodome import Random import asn1tools import sys - -#Compile ASN1 file for Sending of cloud.key +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key asn1_file = asn1tools.compile_files('declaration.asn') #create tcp/ip socket @@ -46,18 +45,18 @@ #bind socket to port server_address = ('192.168.0.3', 4380) while True: - try: - sock.connect(server_address) - break - except: - print('Unexpected error: ',sys.exc_info()[0]) - + try: + sock.connect(server_address) + break + except: + print('Unexpected error: ',sys.exc_info()[0]) + print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) -logger = logging.getLogger('dragonfly') +logger = logging.getLogger('Key Exchange') logger.setLevel(logging.INFO) # create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') +fh = logging.FileHandler('keyexchange.log') fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() @@ -71,604 +70,971 @@ logger.addHandler(fh) -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - - Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) -def decrypting(key, filename): - chunksize = 64 * 1024 - outputFile = filename.split('.hacklab')[0] - - with open(filename, 'rb') as infile: - filesize = int(infile.read(16)) - IV = infile.read(16) - decryptor = AES.new(key, AES.MODE_CBC, IV) - - with open(outputFile, 'wb') as outfile: - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - outfile.write(decryptor.decrypt(chunk)) - outfile.truncate(filesize) - - return outputFile + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + return outputFile def handshake(): - #Own MAC address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print ("my own MAC",own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - #Send own MAC address, BER encoded to peer - sock.send(own_mac_BER) - raw_other_mac = sock.recv(1024) - - #decode BER and get MAC address from peer - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('MAC Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Attempt to send BER encoded Scalar / Element AP to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement', {'data':scalar_complete}) - sock.sendall(scalar_element_BER) - print() - print('Scalar element send', scalar_complete) - logger.info('Computing shared secret...\n') - - #Received BER encoded Scalar / Element AP - scalar_element_ap_encoded = sock.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_encoded) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - print('Scalar element received', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode STA_Token to be BER encoded and send to peer - staToken_BER = asn1_file.encode('DataStaAp', {'data':sta_token}) - sock.send(staToken_BER) - - print("sta_token ", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Received BER encoded AP Token and decode it - apToken_encoded = sock.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - - # Open the received cloud key from the key generator - with open('cloud.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: - print ('File opened...\n') - while True: - key_BER = sock.recv(16396, socket.MSG_WAITALL) - if (len(key_BER) > 10): - keys_decoded = asn1_file.decode('DataKey', key_BER) - cloud_key = keys_decoded.get('key') - nbit_key = keys_decoded.get('nbit') - else: - break - if not (cloud_key or nbit_key): - break - s.write(cloud_key) - t.write(nbit_key) - - - s.close() - t.close() - print ('Successfully got the key files\n') - - print ('Encrypted cloudkey file size: ', os.path.getsize('cloud.key.hacklab')) - print ('Encrypted nbitkey file size: ', os.path.getsize('nbit.key.hacklab')) - print ('Decrypting the files...\n') - - decrypted_cloud_key = decrypting(PMK_Key, 'cloud.key.hacklab') - print('Acquired cloud key file size: ', os.path.getsize(decrypted_cloud_key)) - os.system("md5sum cloud.key") - - decrypted_nbit_key = decrypting(PMK_Key, 'nbit.key.hacklab') - print('Acquired nbit key file size: ', os.path.getsize(decrypted_nbit_key)) - os.system("md5sum nbit.key") - -def tests(): - """ - Test the fucking Curve class. - - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('cloud.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + cloud_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (cloud_key or nbit_key): + break + s.write(cloud_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('cloud.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_cloud_key = decrypting(SK, 'cloud.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_cloud_key)) + os.system("md5sum cloud.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### if __name__ == '__main__': - #tests() - handshake() + handshake() + sock.close() + diff --git a/Cloud/reset.py b/Cloud/reset.py index 6e09cdd..3a61e7e 100755 --- a/Cloud/reset.py +++ b/Cloud/reset.py @@ -9,7 +9,7 @@ os.remove("cloud.key.hacklab") os.remove("postfix") os.remove("postfix.hacklab") - os.remove("dragonfly.log") + os.remove("keyexchange.log") os.remove("averagestandard.txt") except: pass diff --git a/Cloud/sidh_cipher_cloud.py b/Cloud/sidh_cipher_cloud.py new file mode 100644 index 0000000..68f95b5 --- /dev/null +++ b/Cloud/sidh_cipher_cloud.py @@ -0,0 +1,1842 @@ +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#################################################################################### + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Output found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock_output.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock_output.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + +####################################################################### + + + #Testing phase + # buffer_size = 1 + + buffer_size = 1024 + while True: + try: + #Listening and receive data + OPERATION_AND_IP_BER = sock_output.recv(buffer_size) + + print("length of operation and ip", len(OPERATION_AND_IP_BER)) + print("Operation and IP BER", OPERATION_AND_IP_BER) + + #Decode BER + OPERATION_AND_IP_decoded = asn1_file.decode('DataUserInput', OPERATION_AND_IP_BER) + + #Send success msg to output + msg = "success" + sock_output.send(msg.encode()) + + #Break out of while loop if succeed + break + + except: + #Print error message + print("An error occured", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sock_output] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # buffer_size = int(input("Size of buffer?")) + + #Send fail message to output + msg = "fail" + sock_output.send(msg.encode()) + continue + ##### + + #Print out operation and IP + print("Operation and IP data", OPERATION_AND_IP_decoded) + + # Define global lists for ipaddresses and operation codes + global ipList + ipList = [] + + global opList + opList = [] + + global numClList + numClList = [] + + # Create variables for each ipaddress and operation + for k1, v1 in OPERATION_AND_IP_decoded.items(): + for k2,v2 in v1.items(): + locals()[k2] = k2 + print('key',k2) + + postfix_expr = OPERATION_AND_IP_decoded['postfix']['postfix'] + print('Postfix: ',postfix_expr) + p = open('postfix.hacklab', 'wb') + p.write(postfix_expr) + p.close() + decrypting(SK, 'postfix.hacklab') + p = open('postfix', 'r') + postfix = str(p.read()) + p.close() + print("The postfix expression is: ",postfix) + global postfixList + postfixList = " ".join(postfix) + postfixList = postfixList.split() + print("POSTFIX LIST", postfixList) + + global flip + flip = False + + numClList = re.findall("[a-zA-Z]", postfix) + sock_output.close() + + # iterate through postfixList to sort alpha char and operators + x = 0 + y = 0 + for i in postfixList: + if (i.isalpha()): + CLI = OPERATION_AND_IP_decoded['ipaddress']['ipaddress{0}'.format(x+1)] + print("Client :", CLI) + c = open("client.hacklab", "wb") + c.write(CLI) + c.close() + decrypting(SK, 'client.hacklab') + c = open("client", "r") + CLIENT = c.read() + c.close() + ipList.append(CLIENT) + print(ipList) + os.remove('client') + os.remove('client.hacklab') + x = x + 1 + else: + OPCODE = OPERATION_AND_IP_decoded['operation']['operation{0}'.format(y+1)] + print("Opcode:", OPCODE) + o = open("opcode.hacklab","wb") + o.write(OPCODE) + o.close() + decrypting(SK, 'opcode.hacklab') + o = open("opcode", "r") + OPCODE = o.read() + o.close() + opList.append(OPCODE) + print(opList) + os.remove('opcode') + os.remove('opcode.hacklab') + y = y + 1 + + print(len(ipList)) + if (len(ipList) == 1) or (len(numClList) ==1): + print(ipList) + compute_final() + elif (len(ipList) > 1) and (len(numClList) >1): + if (len(ipList) == 2): + flip = True + else: + flip = False + print(ipList) + computation() + else: + None + answer() + sys.exit() + +########################################################## + +def computation(): + print("ip list",ipList) + print("op list",opList) + time.sleep(5) + + # pop the last element of ipList + CL_A = ipList.pop() + numClList.pop(0) + + time.sleep(5) + + # pop the next last element of ipList + CL_B = ipList.pop() + numClList.pop(0) + cipher((CL_B, 4381)) + cipher_ab((CL_A, 4381)) + + # At this stage cloud.data should contain the ciphertexts from 2 CL + compute() + +def cipher(client_address): + + data_request_time_start = time.perf_counter() + + while True: + try: + sockA.connect(client_address) + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to client again') + time.sleep(5) + except KeyboardInterrupt: + print('Crtl-C pressed to terminate program') + pass + except: + print('Unexpected error: ',sys.exc_info()[0]) + + # Open the cloud data received from client and write to cloud.data + +##### + BUFFER_SIZE = 1032 + with open('cloud.data', 'wb') as f: + print ('Cloud data file opened...\n') + + #Size of cloud data + while True: + try: + msg = sockA.recv(BUFFER_SIZE) + fsize_decoded = asn1_file.decode("DataFsize", msg) + fsize = int(fsize_decoded.get("data")) + + #Send success msg to output + msg = "success" + sockA.send(msg.encode()) + + #Break out of while loop if succeed + break + + except: + #Print error message + print("An error occured", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sockA] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # buffer_size = int(input("Size of buffer?")) + + #Send fail message to output + msg = "fail" + sockA.send(msg.encode()) + continue + + #Received size + rsize = 0 + + # Decode received cloud data from BER + while True: + try: + while True: + print ('Receiving cloud data...\n') + + #BER receive data and decode + cloud_data = sockA.recv(BUFFER_SIZE) + + #Testing phase + # BUFFER_SIZE -=10 + + print("Length of cloud data",len(cloud_data)) + #print(cloud_data) + cloud_data_decoded_dict = asn1_file.decode("DataContent", cloud_data) + cloud_data_decoded_raw = cloud_data_decoded_dict.get("data") + + #Writing data + f.write(cloud_data_decoded_raw) + + #Send success message to client + msg = "success" + sockA.sendall(msg.encode()) + + #Break occur if all data is received + rsize = rsize + len(cloud_data_decoded_raw) + if rsize >= fsize: + print ('Breaking from file write') + break + except: + #Print error message + print("An error occured:", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sockA] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # BUFFER_SIZE = int(input("sock a, Size of buffer size?\n")) + + #Send fail message to client + msg = "fail" + sockA.send(msg.encode()) + continue + + else: + #Break out of while loop + if rsize >= fsize: + print ('Received everything.. Breaking out') + break +##### + f.close() + print ('Successfully got the file\n') + + # Send notice to the client; + indicator = asn1_file.encode('DataIndicator', {'data':"Hello! cloud.data received"}) +###### + while True: + sockA.send(indicator) + + #Receive msg + encode_msg = sockA.recv(1024) + msg = encode_msg.decode() + + print(msg) + + #if success break + if (msg == "success"): + break + else: + continue +###### + print("Sending an indicator...\n") + print('Cloud data file size: ', os.path.getsize('cloud.data')) + sockA.close() + + data_request_time_stop = time.perf_counter() + global data_request_time1 + data_request_time1 = round((data_request_time_stop - data_request_time_start), 3) + f = open('timings.txt', 'a') + f.write('\nThe total time elapsed for data request for client 1 is: ') + f.write(str(data_request_time1)) + f.close + +def cipher_ab(client_address): + # This function appends to cloud.data instead of overwriting it. + + data_request_time_start2 = time.perf_counter() + + while True: + try: + sockB.connect(client_address) + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to client again') + time.sleep(5) + except KeyboardInterrupt: + print('Crtl-C pressed to terminate program') + pass + except: + print('Unexpected error: ',sys.exc_info()[0]) + # Open the cloud data received from client and write to cloud.data + +##### + BUFFER_SIZE = 1032 + with open('cloud.data', 'ab') as f: + print ('Cloud data file opened...\n') + + while True: + try: + msg = sockB.recv(BUFFER_SIZE) + fsize_decoded = asn1_file.decode("DataFsize", msg) + fsize = int(fsize_decoded.get("data")) + + #Send success + msg = "success" + sockB.send(msg.encode()) + + #Break out of while loop if succeed + break + + except: + #Print error message + print("An error occured", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sockB] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # BUFFER_SIZE = int(input("fSize of buffer?")) + + #Send fail message to output + msg = "fail" + sockB.send(msg.encode()) + continue + + #Received size + rsize = 0 + + while True: + try: + while True: + print ('Receiving cloud data...\n') + + #Received BER data and decode + cloud_data = sockB.recv(BUFFER_SIZE) + + #Testing phase + # BUFFER_SIZE -=10 + + cloud_data_decoded_dict = asn1_file.decode("DataContent", cloud_data) + cloud_data_decoded_raw = cloud_data_decoded_dict.get("data") + + + #Writing data + f.write(cloud_data_decoded_raw) + + #Send success message to client + msg = "success" + sockB.sendall(msg.encode()) + + #Break occur if all data is received + rsize = rsize + len(cloud_data_decoded_raw) + if rsize >= fsize: + print ('Breaking from file write') + break + + except: + #Print error message + print("An error has occured:", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sockB] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # BUFFER_SIZE = int(input("sock a, Size of buffer size?\n")) + + #Send fail message to client + msg = "fail" + sockB.send(msg.encode()) + continue + + else: + #Break out of while loop + if rsize >= fsize: + print ('Received everything.. Breaking out') + break + +##### + f.close() + print ('Successfully got the file\n') + + # Send notice to the client; + indicator = asn1_file.encode('DataIndicator', {'data':"Hello! cloud.data received"}) +###### + while True: + sockB.send(indicator) + + #Receive msg + encode_msg = sockB.recv(1024) + msg = encode_msg.decode() + + print(msg) + + #if success break + if (msg == "success"): + break + else: + continue +###### + print("Sending an indicator...\n") + print('Cloud data file size: ', os.path.getsize('cloud.data')) + sockB.close() + data_request_time_stop2 = time.perf_counter() + data_request_time2 = round((data_request_time_stop2 - data_request_time_start2), 3) + f = open('timings.txt', 'a') + f.write('\nThe total time elapsed for data request for client2 is: ') + f.write(str(data_request_time2)) + f.close + + +def cipher2(client_address): + data_request_time_start = time.perf_counter() + while True: + try: + sock.connect(client_address) + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to client again') + time.sleep(5) + except KeyboardInterrupt: + print('Crtl-C pressed to terminate program') + pass + except: + print('Unexpected error: ',sys.exc_info()[0]) + + ##### + BUFFER_SIZE = 1032 + + #Testing phase + # BUFFER_SIZE = 3 + + with open('cloud.data', 'wb') as f: + print ('Cloud data file opened...\n') + + #Size of cloud data + while True: + try: + msg = sock.recv(BUFFER_SIZE) + fsize_decoded = asn1_file.decode("DataFsize", msg) + fsize = int(fsize_decoded.get("data")) + + #Send success msg to output + msg = "success" + sock.send(msg.encode()) + + #Break out of while loop if succeed + break + + except: + #Print error message + print("An error occured", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sock] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # BUFFER_SIZE = int(input("Size of buffer?")) + + #Send fail message to output + msg = "fail" + sock.send(msg.encode()) + continue + + #Received size + rsize = 0 + + # Decode received cloud data from BER + while True: + try: + while True: + print ('Receiving cloud data...\n') + + #BER received and decode + cloud_data = sock.recv(BUFFER_SIZE) + + #Testing phase + # BUFFER_SIZE -=10 + + cloud_data_decoded_dict = asn1_file.decode("DataContent", cloud_data) + cloud_data_decoded_raw = cloud_data_decoded_dict.get("data") + #print(cloud_data) + + #Writing to file + f.write(cloud_data_decoded_raw) + + #sending success message to client + msg = "success" + sock.sendall(msg.encode()) + + #Break occur if all data is received + rsize = rsize + len(cloud_data_decoded_raw) + if rsize >= fsize: + print ('Breaking from file write') + break + except: + #Print error message + print("An error occured:", sys.exc_info()[0]) + + #Empty out data from socket + read_con = [sock] + while True: + r, w, e = select.select(read_con, [], [], 0.0) + if len(r) == 0: + break + else: + for data in r: + data.recv(1024) + + #Testing phase + # BUFFER_SIZE = int(input("sock a, Size of buffer size?\n")) + + #Send fail message to client + msg = "fail" + sock.send(msg.encode()) + continue + + else: + #Break out of while loop + if rsize >= fsize: + print ('Received everything.. Breaking out') + break + + ##### + f.close() + print ('Successfully got the file\n') + + # Send notice to the client; + indicator = asn1_file.encode('DataIndicator', {'data':"Hello! cloud.data received"}) + + ###### + while True: + sock.send(indicator) + #Receive msg + encode_msg = sock.recv(1024) + msg = encode_msg.decode() + + print(msg) + + #if success break + if (msg == "success"): + break + else: + continue + ###### + print("Sending an indicator...\n") + print('Cloud data file size: ', os.path.getsize('cloud.data')) + sock.close() + + data_request_time_stop = time.perf_counter() + data_request_time3 = round((data_request_time_stop - data_request_time_start), 3) + f = open('timings.txt', 'a') + f.write('\nThe total time elapsed for data request for client 3 is: ') + f.write(str(data_request_time3)) + f.close + + + +def compute(): + + operator = int(opList.pop(0)) + + print("Starting computation process") + + # NOTE: change the subprocess file as required, when available + if operator == 1: + o = open('operator.txt', 'w') + o.write("1") + o.close() + compute_time_start = time.perf_counter() + # The user has chosen the Addition function + # Run C++ Adder_cloud to compute answer + subprocess.call("./cloud") + print("Printing addition answer data...\n") + compute_time_stop = time.perf_counter() + compute_time_final = round((compute_time_stop - compute_time_start), 3) + f = open('timings.txt','a') + f.write('\nComputation time: ') + f.write(str(compute_time_final)) + f.close() + elif operator == 2: + o = open('operator.txt', 'w') + o.write("2") + o.close() + compute_time_start = time.perf_counter() + # The user has chosen the Subtraction function + # Run C++ subtract_cloud to compute answer + subprocess.call("./cloud") + print("Printing subtraction answer data...\n") + compute_time_stop = time.perf_counter() + compute_time_final = round((compute_time_stop - compute_time_start), 3) + f = open('timings.txt','a') + f.write('\nComputation time: ') + f.write(str(compute_time_final)) + f.close() + elif operator == 3: + o = open('operator.txt', 'w') + o.write("4") # cloud uses 4 to denote multiplication + o.close() + compute_time_start = time.perf_counter() + # The user has chosen the Multiplication function + # Run C++ Multiply_cloud to compute answer + subprocess.call("./cloud") + print("Printing multiplication answer data...\n") + compute_time_stop = time.perf_counter() + compute_time_final = round((compute_time_stop - compute_time_start), 3) + f = open('timings.txt','a') + f.write('\nComputation time: ') + f.write(str(compute_time_final)) + f.close() + elif operator == 4: + o = open('operator.txt', 'w') + o.write("4") # cloud uses 4 to denote multiplication + o.close() + compute_time_start = time.perf_counter() + # The user has chosen the Division function + # Run C++ Divide_cloud to compute answer + subprocess.call("./cloud") + print("Printing Multiplication answer data...\n") + compute_time_stop = time.perf_counter() + compute_time_final = round((compute_time_stop - compute_time_start), 3) + f = open('timings.txt','a') + f.write('\nComputation time: ') + f.write(str(compute_time_final)) + f.close() + else: + None + + + # Print size of answer.data + answer_data = 'answer.data' + ans_size = os.path.getsize(answer_data) + print("File size of computed answer file: ", ans_size) + + if ans_size <= 162304: + answer() + sys.exit() + + +def compute_final(): + + CL_C = ipList.pop() + numClList.pop(0) + cipher2((CL_C, 4381)) + + if flip == True: + with open('cloud.data', 'rb') as c: + cloud = c.read(8192) + with open('answer.data', 'ab') as a: + while cloud: + a.write(cloud) + cloud = c.read(8192) + os.system('cp answer.data cloud.data') + os.remove('answer.data') + compute() + + else: + + # copy binary contents of answer.data to append to cloud.data + with open('answer.data', 'rb') as a: + answer = a.read(8192) + with open('cloud.data', 'ab') as c: + while answer: + c.write(answer) + answer = a.read(8192) + os.remove('answer.data') + compute() + +def answer(): + + answer_data = "answer.data" + ans_size = os.path.getsize(answer_data) + print("This file ", answer_data, "is our computed answer\n") + + + # Open the file and read its content + # f = open('answer.data', 'rb') + # answer = f.read(8192) + + # Open socket connection to OUTPUT to send Answer + output_ipaddr = "192.168.0.4" + output_address =(output_ipaddr, 4381) + + sock_output2.connect(output_address) + print ("connecting to", output_ipaddr) + + # Encode ans_size in BER + # Send size of answer file to the client + # Send answer file to output + ans_size = os.path.getsize(answer_data) + ans_size_encoded = asn1_file.encode('DataAnsSize', {'data':ans_size}) + + while True: + sock_output2.send(ans_size_encoded) + + #receive msg + encode_msg = sock_output2.recv(1024) + msg = encode_msg.decode() + + print(ans_size_encoded) + + #if success, break + if (msg == "success"): + break + else: + continue + + print("Sending answer...\n") + + # Read the final answer file and send it to the OUTPUT + # with open('answer.data', 'rb') as f: + # answer = f.read(1032) + + ##### + #Get size of answer.data + answ_data_size = os.path.getsize("answer.data") + print("Size of answer.data is", answ_data_size) + + #set offset value + offset_value = 0 + + #Testing x value + with open('answer.data', 'rb') as f: + while True: + print("offset",f.tell()) + #Before offset + before_offset = f.tell() + answer = f.read(1032) + + #BER encode answer & send + answer_encoded = asn1_file.encode('DataAnswer', {'data':answer}) + sock_output2.sendall(answer_encoded) + + #after offset + after_offset = f.tell() + + #receving msg + encode_msg = sock_output2.recv(1024) + msg = encode_msg.decode() + + #success message + if (msg == "success"): + #close socket if all data send + print(answ_data_size) + print(after_offset) + if (int(after_offset == answ_data_size)): + sock_output2.shutdown(socket.SHUT_RDWR) + sock_output2.close() + break + #fail message + else: + continue + else: + #offset differences + offset_differences = int(after_offset) - int(before_offset) + + #offset value + offset_value = int(after_offset) - int(offset_differences) + f.seek(offset_value) + + ##### + f.close() + print("File size of computed answer file: ", os.path.getsize(answer_data)) + os.system("md5sum answer.data") + os.remove('answer.data') + sock_output2.close() + +if __name__ == '__main__': + + sockA = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sockB = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_output = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock_output2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + output_ipaddr = "192.168.0.4" + output_address =(output_ipaddr, 4381) + while True: + try: + sock_output.connect(output_address) + print('Connected to output') + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to OUTPUT again') + time.sleep(5) + except KeyboardInterrupt: + print('Crtl-C pressed to terminate program') + pass + except: + print ("connecting to", output_ipaddr) + + handshake() diff --git a/Cloud/sidh_public_cloud.py b/Cloud/sidh_public_cloud.py new file mode 100644 index 0000000..0bce389 --- /dev/null +++ b/Cloud/sidh_public_cloud.py @@ -0,0 +1,1040 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +#bind socket to port +server_address = ('192.168.0.3', 4380) +while True: + try: + sock.connect(server_address) + break + except: + print('Unexpected error: ',sys.exc_info()[0]) + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('cloud.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + cloud_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (cloud_key or nbit_key): + break + s.write(cloud_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('cloud.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_cloud_key = decrypting(SK, 'cloud.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_cloud_key)) + os.system("md5sum cloud.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() + diff --git a/Keygen/declaration.asn b/Keygen/declaration.asn index 1644bec..2c22c17 100755 --- a/Keygen/declaration.asn +++ b/Keygen/declaration.asn @@ -20,12 +20,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String - } - - DataStaAp ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Keygen/dragonfly_private_keygen.py b/Keygen/dragonfly_private_keygen.py deleted file mode 100755 index f508ac1..0000000 --- a/Keygen/dragonfly_private_keygen.py +++ /dev/null @@ -1,753 +0,0 @@ -#!/usr/bin/env python3 - -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" -import time -import hashlib -import random -import logging -import socket -import re, uuid -import base64 -import os, random, struct -import subprocess -from collections import namedtuple -from Cryptodome.Cipher import AES -from Cryptodome import Random -from Cryptodome.Hash import SHA256 -from optparse import * -from _thread import * -import asn1tools -import threading - -lock = threading.Lock() - -#Compile asn1 file for secret_key -asn1_file = asn1tools.compile_files('declaration.asn') - -#create tcp/ip socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -#retrieve local hostname -local_hostname = socket.gethostname() - -#get fully qualified hostname -local_fqdn = socket.getfqdn() - -#get the according ip address -ip_address = socket.gethostbyname(local_hostname) - -#define thread -ThreadCount = 0 - -#output hostname, domain name, ip address -print ("Working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) - -#bind socket to port -server_address = ('192.168.0.3', 4380) -print ("Starting up on %s port %s" % server_address) -sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -sock.bind(server_address) - -logger = logging.getLogger('dragonfly') -logger.setLevel(logging.INFO) -# create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') -fh.setLevel(logging.DEBUG) -# create console handler with a higher log level -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -# create formatter and add it to the handlers -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -ch.setFormatter(formatter) -fh.setFormatter(formatter) -# add the handlers to logger -logger.addHandler(ch) -logger.addHandler(fh) - - -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest - -def encrypting(key, filename): - chunksize = 64*1024 - outputFile = filename+".hacklab" - filesize = str(os.path.getsize(filename)).zfill(16) - IV = Random.new().read(16) - - encryptor = AES.new(key, AES.MODE_CBC, IV) - with open(filename, 'rb') as infile: - with open(outputFile, 'wb') as outfile: - outfile.write(filesize.encode('utf-8')) - outfile.write(IV) - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - elif len(chunk) % 16 != 0: - chunk += b' ' * (16 - (len(chunk) % 16)) - outfile.write(encryptor.encrypt(chunk)) - - return outputFile - -class ClientThread(threading.Thread): - def __init__(self,connection,clientAddr): - threading.Thread.__init__(self) - self.clientAddr = clientAddr - self.connection = connection - print("Connection coming from", connection) - - def run(self): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - - print (own_mac) - ap = Peer('abc1238', own_mac, 'AP') - - logger.info('Starting hunting and pecking to derive PE...\n') - # print ("Connecting from", client_address) - - with self.connection: - raw_other_mac = self.connection.recv(1024) - - #decode BER and get MAC address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ("Other MAC", other_mac) - - #Sending BER encoded MAC address to peer - self.connection.send(own_mac_BER) - - ap.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_ap, element_ap = ap.commit_exchange() - - #encode scalar_ap / element_ap - scalar_complete = ("\n".join([str(scalar_ap), str(element_ap)])) - encoded = asn1_file.encode('DataScalarElement',{'data': scalar_complete}) - - print('data send', scalar_complete) - - #Send BER encoded scalar / element ap to peer - self.connection.sendall(encoded) - print() - - logger.info('Computing shared secret...\n') - - #received BER encoded scalar / element and decoded - scalar_element_ap_encoded= self.connection.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_encoded) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - print('scalar element received', scalar_element_ap) - - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_sta = data[0] - element_sta = data[1] - print() - print ('scalar_sta recv:',scalar_sta) - print() - print ('element_sta recv:',element_sta) - print () - print () - namedtuple_element_sta = eval(element_sta) - print(namedtuple_element_sta.y, namedtuple_element_sta.x) - print () - print () - ap_token = ap.compute_shared_secret(namedtuple_element_sta, int(scalar_sta), other_mac) - - #Encode ap_token to be BER and send to peer - apToken_encoded = asn1_file.encode('DataStaAp',{'data':ap_token}) - self.connection.send(apToken_encoded) - - # connection.send(ap_token.encode()) - print("ap_token data being send over", ap_token) - - print() - logger.info('Confirm Exchange...\n') - - #Received BER encoded STA token and decode it - staToken_encoded = self.connection.recv(1024) - staToken_decoded = asn1_file.decode('DataStaAp', staToken_encoded) - sta_token = staToken_decoded.get('data') - - print('received STA token', sta_token) - - PMK_Key = ap.confirm_exchange(sta_token) - - # Sending keys to OUTPUT and CLIENTs - print ("Getting keys...\n") - lock.acquire() - - print("Printing secret key...\n") - secret_key = "secret.key" - - print("Printing nbit key...\n") - nbit_key = "nbit.key" - - output_secret_key = encrypting(PMK_Key, secret_key) - print("This file ", output_secret_key, " is encrypted secret key\n") - - output_nbit_key = encrypting(PMK_Key, nbit_key) - print("This file ", output_nbit_key, " is encrypted nbit key\n") - - s = open(output_secret_key, "rb") - keycontent = s.read(8192) - - t = open(output_nbit_key, "rb") - nbitcontent = t.read(8192) - - #Encode key in BER format - priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) - - # Send the BER encoded file to the peer - while (keycontent and nbitcontent): - self.connection.sendall(priv_key_BER) - keycontent = s.read(8192) - nbitcontent = t.read(8192) - priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) - s.close() - print('Original secret key file size: ', os.path.getsize(secret_key)) - print ('Encrypted secret key file size: ', os.path.getsize(output_secret_key)) - os.system("md5sum secret.key") - - print('Original nbit key file size: ', os.path.getsize(nbit_key)) - print ('Encrypted nbit key file size: ', os.path.getsize(output_nbit_key)) - os.system("md5sum nbit.key") - - lock.release() - -def handshake(): - HOSTUP1 = True if os.system("ping -c 2 192.168.0.21 > /dev/null 2>&1") == 0 else False - HOSTUP2 = True if os.system("ping -c 2 192.168.0.22 > /dev/null 2>&1") == 0 else False - HOSTUP3 = True if os.system("ping -c 2 192.168.0.23 > /dev/null 2>&1") == 0 else False - - hostup = int(sum([HOSTUP1, HOSTUP2, HOSTUP3]) + 1) - position = 1 - - #dragon_time_start = time.perf_counter() - - # Generate keys once only - subprocess.call("./keygen") - - #alice_done = time.perf_counter() - #f = open('timings.txt' ,'a') - #alice = round((alice_done - dragon_time_start), 3) - #f.write('alice:') - #f.write(str(alice)) - #f.close() - - while True: - sock.listen() - connection, client_address = sock.accept() - threading_name = str(hostup) - if (client_address[0]) == "192.168.0.4" and position == 1: - newThread = ClientThread(connection, client_address) - newThread.start() - hostup -= 1 - position = 0 - elif hostup != 0 and position == 0 and (client_address[0]) != "192.168.0.1": - newThread = ClientThread(connection, client_address) - newThread.start() - hostup -=1 - elif hostup == 0: - position = 1 - #f = open('timings.txt', 'a') - #dragon_time_stop = time.perf_counter() - #dragon_time_total = round((dragon_time_stop - dragon_time_start), 3) - #f.write('\ndragon_time + alice:') - #f.write(str(dragon_time_total)) - #f.close() - break - else: - connection.close() - continue - - -def tests(): - """ - Test the Curve class. - - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - - -if __name__ == '__main__': - #tests() - handshake() - sock.close() diff --git a/Keygen/dragonfly_public_keygen.py b/Keygen/dragonfly_public_keygen.py deleted file mode 100755 index e252f14..0000000 --- a/Keygen/dragonfly_public_keygen.py +++ /dev/null @@ -1,722 +0,0 @@ -#!/usr/bin/env python3 -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - -# THIS FILE IS RESPONSIBLE FOR THE EXCHANGE OF PUBLIC (CLOUD) KEYS BETWEEN KEYGEN AND CLOUD NODES -#""" -import time -import hashlib -import random -import logging -import socket -import re, uuid -import base64 -import os, random, struct -import subprocess -from collections import namedtuple -from Cryptodome.Cipher import AES -from Cryptodome import Random -from Cryptodome.Hash import SHA256 -from optparse import * -import asn1tools - -#Compile asn1 file for cloud_key -asn1_file = asn1tools.compile_files('declaration.asn') - -#create tcp/ip socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -#retrieve local hostname -local_hostname = socket.gethostname() - -#get fully qualified hostname -local_fqdn = socket.getfqdn() - -#get the according ip address -ip_address = socket.gethostbyname(local_hostname) - -#output hostname, domain name, ip address -print ("Working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) - -#bind socket to port -server_address = ('192.168.0.3', 4380) -print ("Starting up on %s port %s" % server_address) -sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -sock.bind(server_address) - -logger = logging.getLogger('dragonfly') -logger.setLevel(logging.INFO) -# create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') -fh.setLevel(logging.DEBUG) -# create console handler with a higher log level -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -# create formatter and add it to the handlers -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -ch.setFormatter(formatter) -fh.setFormatter(formatter) -# add the handlers to logger -logger.addHandler(ch) -logger.addHandler(fh) - - -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - - Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest -''' -BLOCK_SIZE = 16 -pad = lambda s: bytes(s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE), 'utf-8') -unpad = lambda s: s[:-ord(s[-1:])] - - -def encrypt(raw, PMK): - raw = pad(raw) - iv = Random.new().read(AES.block_size) - cipher = AES.new(PMK, AES.MODE_CBC, iv) - return base64.b64encode(iv + cipher.encrypt(raw)) -''' -def encrypting(key, filename): - chunksize = 64*1024 - outputFile = filename+".hacklab" - filesize = str(os.path.getsize(filename)).zfill(16) - IV = Random.new().read(16) - - encryptor = AES.new(key, AES.MODE_CBC, IV) - with open(filename, 'rb') as infile: - with open(outputFile, 'wb') as outfile: - outfile.write(filesize.encode('utf-8')) - outfile.write(IV) - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - elif len(chunk) % 16 != 0: - chunk += b' ' * (16 - (len(chunk) % 16)) - outfile.write(encryptor.encrypt(chunk)) - - return outputFile - -def handshake(): - #Own MAC address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - - print ("My own MAC",own_mac) - ap = Peer('abc1238', own_mac, 'AP') - - logger.info('Starting hunting and pecking to derive PE...\n') - - # Connect to the cloud server - while True: - print("Waiting for cloud") - sock.listen(1) - connection, client_address = sock.accept() - if (client_address[0]) != '192.168.0.1': - connection.close() - continue - else: - sock.close() - with connection: - print ("Connecting from", client_address) - raw_other_mac = connection.recv(1024) - - #decode BER and get MAC address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - #Send MAC address to peer - connection.send(own_mac_BER) - print ("Other MAC: ",other_mac) - - ap.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_ap, element_ap = ap.commit_exchange() - - #BER encode scalar_ap / element_ap - scalar_complete = ("\n".join([str(scalar_ap), str(element_ap)])) - scalar_element_BER = asn1_file.encode('DataScalarElement',{'data': scalar_complete}) - - #Scalar / element send - print('Scalar / element data send', scalar_complete) - - #Send Scalar / element ap data - connection.sendall(scalar_element_BER) - print() - logger.info('Computing shared secret...\n') - - #Scalar / element BER encoded received and decoded - scalar_element_ap_encoded= connection.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_encoded) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - print('scalar element received', scalar_element_ap) - - # scalar_element_ap = connection.recv(1024).decode() - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_sta = data[0] - element_sta = data[1] - print() - print ('Scalar_sta received:',scalar_sta) - print() - print ('Element_sta received:',element_sta) - print () - print () - namedtuple_element_sta = eval(element_sta) - print(namedtuple_element_sta.y, namedtuple_element_sta.x) - print () - print () - ap_token = ap.compute_shared_secret(namedtuple_element_sta, int(scalar_sta), other_mac) - - #Encode ap_token to be BER and send to peer - apToken_encoded = asn1_file.encode('DataStaAp',{'data':ap_token}) - connection.send(apToken_encoded) - - print("ap_token data being send over", ap_token) - - print() - logger.info('Confirm Exchange...\n') - - #Received BER encoded STA token and decode it - staToken_encoded = connection.recv(1024) - staToken_decoded = asn1_file.decode('DataStaAp', staToken_encoded) - sta_token = staToken_decoded.get('data') - - print('received STA token', sta_token) - - PMK_Key = ap.confirm_exchange(sta_token) - #print (PMK_Key) - - # First let us encrypt secret message - #encrypted = encrypt("This is a secret message", PMK_Key) - #print("Encrypted ciphertext: ", encrypted.decode('utf-8')) - #connection.send(encrypted) - - # Running c++ Adder_alice to get the public key - print ("Getting keys...\n") - print("Printing cloud key...\n") - cloud_key = "cloud.key" - nbit_key = "nbit.key" - - # encrypt cloudkey - cloudkey = encrypting(PMK_Key, cloud_key) - print("This file ", cloudkey, " is encrypted cloud key\n") - - # encrypt nbitkey - nbitkey = encrypting(PMK_Key, nbit_key) - print("This file ", nbitkey, " is encrypted nbit key\n") - - # Open the cloudkey file and read its content - s = open(cloudkey, "rb") - keycontent = s.read(8192) - - # Open the nbitkey file and read its content - t = open(nbitkey, "rb") - nbitkeycontent = t.read(8192) - - # BER encoded data of cloud.key and nbit.key, encode first 8192 bits - encoded_keys = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitkeycontent}) - - # Send the file to the cloud server - while (keycontent and nbitkeycontent): - connection.send(encoded_keys) - keycontent = s.read(8192) - nbitkeycontent = t.read(8192) - encoded_keys = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitkeycontent}) - s.close() - t.close() - - print('Original cloud file size: ', os.path.getsize(cloud_key)) - print ('Encrypted cloud file size: ', os.path.getsize(cloudkey)) - os.system("md5sum cloud.key") - - print('Original nbit key file size: ', os.path.getsize(nbit_key)) - print('Encrypted nbit key file size: ', os.path.getsize(nbitkey)) - os.system("md5sum nbit.key") - - break - - - -def tests(): - """ - Test the fucking Curve class. - - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - - -if __name__ == '__main__': - #tests() - handshake() diff --git a/Keygen/keygen_dynamic.py b/Keygen/keygen_dynamic.py index be11396..a19638e 100755 --- a/Keygen/keygen_dynamic.py +++ b/Keygen/keygen_dynamic.py @@ -17,28 +17,28 @@ from optparse import * import logging -# THE PURPOSE OF THIS FILE IS TO INITATE DRAGONFLY KEY EXHANGE AND VERIFY ITS COMPLETION +# THE PURPOSE OF THIS FILE IS TO INITATE SIDH KEY EXHANGE AND VERIFY ITS COMPLETION -def dragonfly(): +def sidh(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) output_address = ("192.168.0.4", 4380) print('Listening...') - logging.info('STARTING dragonfly') + logging.info('Starting SIDH') # else: - #Execute dragonfly private for output machine - print ('Executing dragonfly code for Output machine.') + #Execute sidh private for output machine + print ('Executing SIDH code for Output machine.') - os.system('python3 dragonfly_private_keygen.py') + os.system('python3 sidh_private_keygen.py') - #Execute dragonfly public for cloud machine - logging.info("dragonfly cloud") - print ('executing dragonfly code for CLOUD machine') - os.system('python3 dragonfly_public_keygen.py') + #Execute sidh public for cloud machine + logging.info("SIDH cloud") + print ('executing SIDH code for CLOUD machine') + os.system('python3 sidh_public_keygen.py') time.sleep(10) try: @@ -52,4 +52,4 @@ def dragonfly(): while True: - dragonfly() + sidh() diff --git a/Keygen/reset.py b/Keygen/reset.py index 391f817..c45062f 100755 --- a/Keygen/reset.py +++ b/Keygen/reset.py @@ -3,6 +3,6 @@ os.remove("cloud.data") os.remove("cloud.key") os.remove("cloud.key.hacklab") -os.remove("dragonfly.log") +os.remove("keyexchange.log") os.remove("secret.key") os.remove("secret.key.hacklab") diff --git a/Keygen/sidh_private_keygen.py b/Keygen/sidh_private_keygen.py new file mode 100644 index 0000000..b03c13a --- /dev/null +++ b/Keygen/sidh_private_keygen.py @@ -0,0 +1,1103 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os, random, struct +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +from Cryptodome.Hash import SHA256 +from optparse import * +from _thread import * +import asn1tools +import threading +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +lock = threading.Lock() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +#define thread +ThreadCount = 0 + +#output hostname, domain name, ip address +print ("Working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +#bind socket to port +server_address = ('192.168.0.3', 4380) +print ("Starting up on %s port %s" % server_address) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.bind(server_address) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Alice(SK_Alice, params, splits, MAX): + #input: secret random even number in (1,oA-1) + # public parameters [XPB, XPA, YPA] + #output: public key [phi_A(x(PB)),phi_A(x(QB)),phi_A(x(QB-PB))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secre_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Alice, 'Alice') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + #first 4-isogeny (different from rest) + phiPX, phiPZ, A2, C2 = first_4_isog(phiPX, phiPZ, A) + phiQX, phiQZ, A2, C2 = first_4_isog(phiQX, phiQZ, A) + phiDX, phiDZ, A2, C2 = first_4_isog(phiDX, phiDZ, A) + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 4 + + pts = [] + index = 0 + + #Alice's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 4-isogeny at Bob's points + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Alices's public key, values in Fp2 + PK_Alice = [phiPX, phiQX, phiDX] + + msg="Alice's keygen needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Alice's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Alice + + +###################################################################### + +def shared_secret_Alice(SK_Alice, PK_Bob, splits, MAX): + #input: Alices's secret key SK_Alice + # Bob's public key + #output: Alice's shared secret: j-invariant of E_AB + + A = get_A(PK_Bob[0], PK_Bob[1], PK_Bob[2]) + C = Complex(1) #start on Bob's curve + + #compute R=phi_B(xPA)+SK_Alice*phi_B(xQA) + RX, RZ = LADDER_3_pt(SK_Alice, PK_Bob[0], PK_Bob[1], PK_Bob[2], A, 'Alice') + iso, mul = 0, 0 #counters + + #first isogeny + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 1 + + pts = [] + index = 0 + + #main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + + secret_Alice = j_inv(A, C) + + msg="Alice's secret needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + + return secret_Alice + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +params_Alice = [XPB, XPA, YPA] +# params_Bob = [XPA, XPB, YPB] + +################################################################# +#strategy paramters from MSR +splits_Alice = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11,\ +12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21,\ +20, 21, 21, 21, 21, 21, 22, 25, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32,\ +32, 32, 32, 32, 33, 33, 33, 35, 36, 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38,\ +38, 39, 40, 41, 42, 38, 39, 40, 41, 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48,\ +48, 48, 49, 49, 48, 53, 54, 51, 52, 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62,\ +63, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66,\ +69, 70, 66, 67, 66, 69, 70, 69, 70, 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72,\ +74, 74, 75, 72, 72, 74, 75, 75, 72, 72, 74, 75, 75, 77, 77, 79, 80, 80, 82 ] + +MAX_Alice = 185 + +####################################################################### + +def encrypting(key, filename): + chunksize = 64*1024 + outputFile = filename+".hacklab" + filesize = str(os.path.getsize(filename)).zfill(16) + IV = Random.new().read(16) + + encryptor = AES.new(key, AES.MODE_CBC, IV) + with open(filename, 'rb') as infile: + with open(outputFile, 'wb') as outfile: + outfile.write(filesize.encode('utf-8')) + outfile.write(IV) + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + elif len(chunk) % 16 != 0: + chunk += b' ' * (16 - (len(chunk) % 16)) + outfile.write(encryptor.encrypt(chunk)) + + return outputFile + +#Handles connections received from Clients +class ClientThread(threading.Thread): + def __init__(self,connection,clientAddr): + threading.Thread.__init__(self) + self.clientAddr = clientAddr + self.connection = connection + logger.info("Connection coming from %s", connection) + + def run(self): + + logger.info('Starting Key Exchange...\n') + + with self.connection: + + print() + logger.info('Client found. Key Exchange Begins...\n') + + #Calculates Secret and Public Keys + n_Alice = randint(0,(lA**eA)/2) + n_Alice = 2*n_Alice + logger.info("Alice's secret key:") + logger.info(n_Alice) + print('') + PKA = keygen_Alice(n_Alice, params_Alice, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's Public Key:") + logger.info('%s',(PKA[0])) + logger.info('%s',(PKA[1])) + logger.info('%s',(PKA[2])) + #print((PKA[1])) + #print((PKA[2])) + print('') + + keyreal1 = PKA[0].re + keyimag1 = PKA[0].im + keyreal2 = PKA[1].re + keyimag2 = PKA[1].im + keyreal3 = PKA[2].re + keyimag3 = PKA[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + logger.info('Data Sent %s %s %s', PKA[0], PKA[1], PKA[2]) + + #Sends Alice's encoded Public key to Bob + self.connection.sendall(encoded) + print() + + logger.info('Receiving Clients Public Key...\n') + + #Received Bob's encoded Public Key and decodes it + PKB_encoded = self.connection.recv(2048) + PKB_decoded = asn1_file.decode('DataPublicKey', PKB_encoded) + #Retrieving Bob's public key in INT Form + keyreal1B = PKB_decoded.get('keyreal1') + keyimag1B = PKB_decoded.get('keyimag1') + keyreal2B = PKB_decoded.get('keyreal2') + keyimag2B = PKB_decoded.get('keyimag2') + keyreal3B = PKB_decoded.get('keyreal3') + keyimag3B = PKB_decoded.get('keyimag3') + #Forming Bob's public key into complex form for calculations + phiPX = Complex(keyreal1B, keyimag1B) + phiQX = Complex(keyreal2B, keyimag2B) + phiDX = Complex(keyreal3B, keyimag3B) + + PKB = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKB[0]) + logger.info(PKB[1]) + logger.info(PKB[2]) + + print() + logger.info('Computing shared secret...\n') + + SKA = shared_secret_Alice(n_Alice, PKB, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's shared secret:") + logger.info(SKA) + print('') + + #Hashing Shared Secret + SKA_ComplexToString = secretKeyEncoder().encode(SKA) + SKA_StringToBytes = SKA_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKA_StringToBytes).digest() + + print ("Getting keys...\n") + lock.acquire() + + print("Printing secret key...\n") + secret_key = "secret.key" + + print("Printing nbit key...\n") + nbit_key = "nbit.key" + + output_secret_key = encrypting(SK, secret_key) + print("This file", output_secret_key, "is encrypted secret key\n") + + output_nbit_key = encrypting(SK, nbit_key) + print("This file", output_nbit_key, "is encrypted nbit key\n") + + s = open(output_secret_key, "rb") + keycontent = s.read(8192) + + t = open(output_nbit_key, "rb") + nbitcontent = t.read(8192) + + #Encode key in BER format + priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) + + # Send the BER encoded file to the peer + while (keycontent and nbitcontent): + self.connection.sendall(priv_key_BER) + keycontent = s.read(8192) + nbitcontent = t.read(8192) + priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) + s.close() + print('Original secret key file size: ', os.path.getsize(secret_key)) + print ('Encrypted secret key file size: ', os.path.getsize(output_secret_key)) + os.system("md5sum secret.key") + + print('Original nbit key file size: ', os.path.getsize(nbit_key)) + print ('Encrypted nbit key file size: ', os.path.getsize(output_nbit_key)) + os.system("md5sum nbit.key") + + lock.release() + +####################################################################### + +def handshake(): + HOSTUP1 = True if os.system("ping -c 2 192.168.0.21 > /dev/null 2>&1") == 0 else False + HOSTUP2 = True if os.system("ping -c 2 192.168.0.22 > /dev/null 2>&1") == 0 else False + HOSTUP3 = True if os.system("ping -c 2 192.168.0.23 > /dev/null 2>&1") == 0 else False + + hostup = int(sum([HOSTUP1, HOSTUP2, HOSTUP3]) + 1) + position = 1 + + # Generate keys once only + subprocess.call("./keygen") + + while True: + sock.listen() + connection, client_address = sock.accept() + threading_name = str(hostup) + if (client_address[0]) == "192.168.0.4" and position == 1: + newThread = ClientThread(connection, client_address) + newThread.start() + hostup -= 1 + position = 0 + elif hostup != 0 and position == 0 and (client_address[0]) != "192.168.0.1": + newThread = ClientThread(connection, client_address) + newThread.start() + hostup -=1 + elif hostup == 0: + position = 1 + break + else: + connection.close() + continue + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() diff --git a/Keygen/sidh_public_keygen.py b/Keygen/sidh_public_keygen.py new file mode 100644 index 0000000..d1b929b --- /dev/null +++ b/Keygen/sidh_public_keygen.py @@ -0,0 +1,1063 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for cloud_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +#output hostname, domain name, ip address +print ("Working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +#bind socket to port +server_address = ('192.168.0.3', 4380) +print ("Starting up on %s port %s" % server_address) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.bind(server_address) + + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Alice(SK_Alice, params, splits, MAX): + #input: secret random even number in (1,oA-1) + # public parameters [XPB, XPA, YPA] + #output: public key [phi_A(x(PB)),phi_A(x(QB)),phi_A(x(QB-PB))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secre_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Alice, 'Alice') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + #first 4-isogeny (different from rest) + phiPX, phiPZ, A2, C2 = first_4_isog(phiPX, phiPZ, A) + phiQX, phiQZ, A2, C2 = first_4_isog(phiQX, phiQZ, A) + phiDX, phiDZ, A2, C2 = first_4_isog(phiDX, phiDZ, A) + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 4 + + pts = [] + index = 0 + + #Alice's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 4-isogeny at Bob's points + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Alices's public key, values in Fp2 + PK_Alice = [phiPX, phiQX, phiDX] + + msg="Alice's keygen needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Alice's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Alice + + +###################################################################### + +def shared_secret_Alice(SK_Alice, PK_Bob, splits, MAX): + #input: Alices's secret key SK_Alice + # Bob's public key + #output: Alice's shared secret: j-invariant of E_AB + + A = get_A(PK_Bob[0], PK_Bob[1], PK_Bob[2]) + C = Complex(1) #start on Bob's curve + + #compute R=phi_B(xPA)+SK_Alice*phi_B(xQA) + RX, RZ = LADDER_3_pt(SK_Alice, PK_Bob[0], PK_Bob[1], PK_Bob[2], A, 'Alice') + iso, mul = 0, 0 #counters + + #first isogeny + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 1 + + pts = [] + index = 0 + + #main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + + secret_Alice = j_inv(A, C) + + msg="Alice's secret needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + + return secret_Alice + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +params_Alice = [XPB, XPA, YPA] +# params_Bob = [XPA, XPB, YPB] + +################################################################# +#strategy paramters from MSR +splits_Alice = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11,\ +12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21,\ +20, 21, 21, 21, 21, 21, 22, 25, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32,\ +32, 32, 32, 32, 33, 33, 33, 35, 36, 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38,\ +38, 39, 40, 41, 42, 38, 39, 40, 41, 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48,\ +48, 48, 49, 49, 48, 53, 54, 51, 52, 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62,\ +63, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66,\ +69, 70, 66, 67, 66, 69, 70, 69, 70, 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72,\ +74, 74, 75, 72, 72, 74, 75, 75, 72, 72, 74, 75, 75, 77, 77, 79, 80, 80, 82 ] + +MAX_Alice = 185 + +####################################################################### + +#encrypts received secret & nbit keys +def encrypting(key, filename): + chunksize = 64*1024 + outputFile = filename+".hacklab" + filesize = str(os.path.getsize(filename)).zfill(16) + IV = Random.new().read(16) + + encryptor = AES.new(key, AES.MODE_CBC, IV) + with open(filename, 'rb') as infile: + with open(outputFile, 'wb') as outfile: + outfile.write(filesize.encode('utf-8')) + outfile.write(IV) + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + elif len(chunk) % 16 != 0: + chunk += b' ' * (16 - (len(chunk) % 16)) + outfile.write(encryptor.encrypt(chunk)) + + return outputFile + +def handshake(): + while True: + print("Waiting for cloud") + sock.listen(1) + connection, client_address = sock.accept() + if (client_address[0]) != '192.168.0.1': + connection.close() + continue + else: + sock.close() + with connection: + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Cloud Machine found. Key exchange begins...\n') + + #Calculates Secret and Public Keys + n_Alice = randint(0,(lA**eA)/2) + n_Alice = 2*n_Alice + logger.info("Alice's secret key:") + logger.info(n_Alice) + print('') + PKA = keygen_Alice(n_Alice, params_Alice, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's Public Key:") + logger.info('%s',(PKA[0])) + logger.info('%s',(PKA[1])) + logger.info('%s',(PKA[2])) + #print((PKA[1])) + #print((PKA[2])) + print('') + + keyreal1 = PKA[0].re + keyimag1 = PKA[0].im + keyreal2 = PKA[1].re + keyimag2 = PKA[1].im + keyreal3 = PKA[2].re + keyimag3 = PKA[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + logger.info('Data Sent %s %s %s', PKA[0], PKA[1], PKA[2]) + + #Sends Alice's encoded Public key to Bob + connection.sendall(encoded) + print() + + logger.info('Receiving Clients Public Key...\n') + + #Received Bob's encoded Public Key and decodes it + PKB_encoded = connection.recv(2048) + PKB_decoded = asn1_file.decode('DataPublicKey', PKB_encoded) + #Retrieving Bob's public key in INT Form + keyreal1B = PKB_decoded.get('keyreal1') + keyimag1B = PKB_decoded.get('keyimag1') + keyreal2B = PKB_decoded.get('keyreal2') + keyimag2B = PKB_decoded.get('keyimag2') + keyreal3B = PKB_decoded.get('keyreal3') + keyimag3B = PKB_decoded.get('keyimag3') + #Forming Bob's public key into complex form for calculations + phiPX = Complex(keyreal1B, keyimag1B) + phiQX = Complex(keyreal2B, keyimag2B) + phiDX = Complex(keyreal3B, keyimag3B) + + PKB = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKB[0]) + logger.info(PKB[1]) + logger.info(PKB[2]) + + print() + logger.info('Computing shared secret...\n') + + SKA = shared_secret_Alice(n_Alice, PKB, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's shared secret:") + logger.info(SKA) + print('') + + #Hashing Shared Secret + SKA_ComplexToString = secretKeyEncoder().encode(SKA) + SKA_StringToBytes = SKA_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKA_StringToBytes).digest() + + print ("Getting keys...\n") + print ("Printing cloud key...\n") + cloud_key = "cloud.key" + print("Printing nbit key...\n") + nbit_key = "nbit.key" + + # encrypt cloudkey + cloudkey = encrypting(SK, cloud_key) + print("This file", cloudkey, "is encrypted secret key\n") + #encrypt nbitkey + nbitkey = encrypting(SK, nbit_key) + print("This file", nbitkey, "is encrypted nbit key\n") + + #Open the cloudkey file and read its content + s = open(cloudkey, "rb") + keycontent = s.read(8192) + + #open the nbitkey file and read its content + t = open(nbitkey, "rb") + nbitkeycontent = t.read(8192) + + #Encode key in BER format + encoded_keys = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitkeycontent}) + + # Send the BER encoded file to the peer + while (keycontent and nbitkeycontent): + connection.send(encoded_keys) + keycontent = s.read(8192) + nbitkeycontent = t.read(8192) + encoded_keys = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitkeycontent}) + s.close() + t.close() + + print('Original cloud file size: ', os.path.getsize(cloud_key)) + print ('Encrypted cloud file size: ', os.path.getsize(cloudkey)) + os.system("md5sum cloud.key") + + print('Original nbit key file size: ', os.path.getsize(nbit_key)) + print('Encrypted nbit key file size: ', os.path.getsize(nbitkey)) + os.system("md5sum nbit.key") + + break +####################################################################### + +if __name__ == '__main__': + handshake() + diff --git a/Output/declaration.asn b/Output/declaration.asn index d70e5ef..4c77444 100755 --- a/Output/declaration.asn +++ b/Output/declaration.asn @@ -41,12 +41,18 @@ TEST DEFINITIONS ::= BEGIN nbit OCTET STRING } - DataScalarElement ::= SEQUENCE { - data IA5String - } - - DataStaAp ::= SEQUENCE { - data IA5String + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER } DataFsize ::= SEQUENCE { diff --git a/Output/dragonfly_private_Output.py b/Output/dragonfly_private_Output.py deleted file mode 100755 index 6aabfa0..0000000 --- a/Output/dragonfly_private_Output.py +++ /dev/null @@ -1,691 +0,0 @@ -#!/usr/bin/env python3 - -#""" -#Implements the Dragonfly (SAE) handshake. - -#Instead of using a client (STA) and a access point (AP), we -#just programmatically create a peer to peer network of two participiants. -#Either party may initiate the SAE protocol, either party can be the client and server. - -#In a mesh scenario, where two APs (two equals) are trying to establish a connection -#between each other and each one could have the role of supplicant or authenticator. - -#SAE is build upon the Dragonfly Key Exchange, which is described in https://tools.ietf.org/html/rfc7664. - -#https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python -#""" -import time -import hashlib -import random -import logging -import socket -import re, uuid -import base64 -import os -import subprocess -from collections import namedtuple -from Cryptodome.Cipher import AES -from Cryptodome import Random -import asn1tools -import sys - -#Compile asn1 file for secret_key -asn1_file = asn1tools.compile_files('declaration.asn') - -#create tcp/ip socket -sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - -#retrieve local hostname -local_hostname = socket.gethostname() - -#get fully qualified hostname -local_fqdn = socket.getfqdn() - -#get the according ip address -ip_address = socket.gethostbyname(local_hostname) - -#bind socket to port -server_address = ('192.168.0.3', 4380) - -while True: - try: - sock.connect(server_address) - break - except ConnectionRefusedError as conn_error: - print("Attempting to connect to server...") - time.sleep(5) - except: - # print("Unexpected error", sys.exc_info()[0]) - continue - -print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) - -logger = logging.getLogger('dragonfly') -logger.setLevel(logging.INFO) -# create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') -fh.setLevel(logging.DEBUG) -# create console handler with a higher log level -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -# create formatter and add it to the handlers -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -ch.setFormatter(formatter) -fh.setFormatter(formatter) -# add the handlers to logger -logger.addHandler(ch) -logger.addHandler(fh) - - -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True - else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O - else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - - Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) - - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) - - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: - - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - - self.k = K[0] - - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) - - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() - - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() - - return self.token - - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) - - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() - - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK - - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - - Key derivation function from Section B.5.1 of [FIPS186-4] - - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() - - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. - - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - - assert len(binary_repr) == n - - logger.debug('Rand={}'.format(binary_repr)) - - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) - - logger.debug('C={}'.format(C)) - - #k = (C % (n - 1)) + 1 - - k = C - - logger.debug('k={}'.format(k)) - - return k - - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest - - -def decrypting(key, filename): - chunksize = 64 * 1024 - outputFile = filename.split('.hacklab')[0] - - with open(filename, 'rb') as infile: - filesize = int(infile.read(16)) - IV = infile.read(16) - decryptor = AES.new(key, AES.MODE_CBC, IV) - - with open(outputFile, 'wb') as outfile: - while True: - chunk = infile.read(chunksize) - if len(chunk) == 0: - break - outfile.write(decryptor.decrypt(chunk)) - outfile.truncate(filesize) - - return outputFile - - -def handshake(): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - print (own_mac) - sta = Peer('abc1238', own_mac, 'STA') - - logger.info('Starting hunting and pecking to derive PE...\n') - - sock.send(own_mac_BER) - raw_other_mac = sock.recv(1024) - - #decode BER and get mac address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ('Received', other_mac) - - sta.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_sta, element_sta = sta.commit_exchange() - - #Send BER encodewd Scalar / element ap to peer - scalar_complete = ("\n".join([str(scalar_sta), str(element_sta)])) - scalar_element_BER = asn1_file.encode('DataScalarElement',{'data':scalar_complete}) - sock.sendall(scalar_element_BER) - print() - print('data send', scalar_complete) - logger.info('Computing shared secret...\n') - - - #receive BER encoded scalar / element ap - scalar_element_ap_BER = sock.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_BER) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - # scalar_element_ap = sock.recv(1024).decode() - print('scalar element received ', scalar_element_ap) - data = scalar_element_ap.split('\n') - # print (data[0]) - # print (data[1]) - scalar_ap = data[0] - element_ap = data[1] - print() - print ('scalar_ap recv:',scalar_ap) - print() - print ('element_ap recv:',element_ap) - print () - print () - namedtuple_element_ap = eval(element_ap) - print (namedtuple_element_ap.y, namedtuple_element_ap.x) - print () - print () - - sta_token = sta.compute_shared_secret(namedtuple_element_ap, int(scalar_ap), other_mac) - - #Encode sta_token to be BER encoded and send to peer - staToken_encoded = asn1_file.encode('DataStaAp',{'data':sta_token}) - sock.send(staToken_encoded) - - # sock.send(sta_token.encode()) - print("sta_token", sta_token) - - print() - logger.info('Confirm Exchange...\n') - - #Receive BER encoded AP Token and decode it - apToken_encoded = sock.recv(1024) - apToken_decoded = asn1_file.decode('DataStaAp', apToken_encoded) - ap_token = apToken_decoded.get('data') - - print('received ap token', ap_token) - - PMK_Key = sta.confirm_exchange(ap_token) - #print (PMK_Key) - - #encrypted = sock.recv(1024).decode() - #print ("Encrypted ciphertext: ", encrypted) - - # Decrypt using PMK_Key - #decrypted = decrypt(encrypted, PMK_Key) - #print (decrypted.decode()) - - # Open the received secret file from the key generator - with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: - print ('File opened...\n') - while True: - # print ('Receiving data...\n') - keys_BER = sock.recv(16396, socket.MSG_WAITALL) - if (len(keys_BER) > 10): - keys_decoded = asn1_file.decode('DataKey', keys_BER) - secret_key = keys_decoded.get('key') - nbit_key = keys_decoded.get('nbit') - time.sleep(0.001) - else: - break - if not (secret_key or nbit_key): - break - s.write(secret_key) - t.write(nbit_key) - - s.close() - t.close() - print ('Successfully got the file\n') - - print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) - print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) - print ('Decrypting the files...\n') - - decrypted_secret_key = decrypting(PMK_Key, 'secret.key.hacklab') - print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) - os.system("md5sum secret.key") - - decrypted_nbit_key = decrypting(PMK_Key, 'nbit.key.hacklab') - print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) - os.system("md5sum nbit.key") - - -def tests(): - """ - Test the Curve class. - - See Understanding Cryptography ECC Section. - """ - a, b, p = 2, 2, 17 - curve = Curve(a, b, p) - - P = Point(5, 1) - assert curve.double_add_algorithm(19, P) == O - - T = P - for i in range(p+1): - T = curve.ec_add(T, P) - - assert curve.double_add_algorithm(19, P) == T - - -if __name__ == '__main__': - #tests() - handshake() diff --git a/Output/output_dynamic.py b/Output/output_dynamic.py index 567d2a8..32567b3 100755 --- a/Output/output_dynamic.py +++ b/Output/output_dynamic.py @@ -2,6 +2,7 @@ import time import hashlib import random +from random import randint import logging import socket import re, uuid @@ -18,8 +19,10 @@ import sys from ipaddress import ip_address, IPv6Address import select +import json +from json import JSONEncoder -# THE PURPOSE OF THIS FILE IS TO HANDLE USER INPUT AND REQUEST FOR DRAGONFLY KEY EXCHANGE TO BE COMPLETED +# THE PURPOSE OF THIS FILE IS TO HANDLE USER INPUT AND REQUEST FOR SUPERSINGULAR ISOGENY KEY EXCHANGE TO BE COMPLETED asn1_file = asn1tools.compile_files("declaration.asn") @@ -37,20 +40,20 @@ def generateMd5(file_name): return hash_md5.hexdigest() -def dragonfly(): - # Request Keygen to initiate Dragonfly SAE - print("Starting Dragonfly Key Exchange ") +def sidh(): + # Request Keygen to initiate SIDH + print("Starting Supersingular Isogeny Key Exchange ") # Wait - print('Executing dragonfly code for Private key') + print('Executing SIDH code for Private key') time.sleep(1) - #Executing Dragonfly code for private key - subprocess.run('./dragonfly_private_Output.py') + #Executing SIDH code for private key + subprocess.run('./sidh_private_Output.py') MD5_PrivKey = generateMd5("secret.key") print ("MD5 of private key:", MD5_PrivKey) - print("Waiting for dragonfly to finish...") + print("Waiting for SIDH to finish...") sock_message.bind(own_address_kg) sock_message.listen(1) @@ -65,15 +68,6 @@ def dragonfly(): handshake("client1", "client2", "client3", "client4", "opcode1", "opcode2", "opcode3", "postfix") else: None - - - #while True: - # confirm_code = input("Has dragonfly occured between client / output / cloud? (yes/no)") - # if str(confirm_code).lower() == "yes": - # handshake("client1", "client2", "client3", "opcode1", "opcode2", "postfix") - # break - # else: - # None ###############################################33 @@ -158,10 +152,10 @@ def convert(self, expr): ############################################# -logger = logging.getLogger('dragonfly') +logger = logging.getLogger('Key Exchange') logger.setLevel(logging.INFO) # create file handler which logs even debug messages -fh = logging.FileHandler('dragonfly.log') +fh = logging.FileHandler('keyexchange.log') fh.setLevel(logging.DEBUG) # create console handler with a higher log level ch = logging.StreamHandler() @@ -175,451 +169,843 @@ def convert(self, expr): logger.addHandler(fh) -Point = namedtuple("Point", "x y") -# The point at infinity (origin for the group law). -O = 'Origin' - -def lsb(x): - binary = bin(x).lstrip('0b') - return binary[0] - -def legendre(a, p): - return pow(a, (p - 1) // 2, p) - -def tonelli_shanks(n, p): - """ - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm#Python - """ - assert legendre(n, p) == 1, "not a square (mod p)" - q = p - 1 - s = 0 - while q % 2 == 0: - q //= 2 - s += 1 - if s == 1: - return pow(n, (p + 1) // 4, p) - for z in range(2, p): - if p - 1 == legendre(z, p): - break - c = pow(z, q, p) - r = pow(n, (q + 1) // 2, p) - t = pow(n, q, p) - m = s - t2 = 0 - while (t - 1) % p != 0: - t2 = (t * t) % p - for i in range(1, m): - if (t2 - 1) % p == 0: - break - t2 = (t2 * t2) % p - b = pow(c, 1 << (m - i - 1), p) - r = (r * b) % p - c = (b * b) % p - t = (t * c) % p - m = i - return r - -class Curve(): - """ - Mathematical operations on a Elliptic Curve. - - A lot of code taken from: - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - - def __init__(self, a, b, p): - self.a = a - self.b = b - self.p = p - - def curve_equation(self, x): - """ - We currently use the elliptic curve - NIST P-384 - """ - return (pow(x, 3) + (self.a * x) + self.b) % self.p - - def is_quadratic_residue(self, x): - """ - https://en.wikipedia.org/wiki/Euler%27s_criterion - Computes Legendre Symbol. - """ - return pow(x, (self.p-1) // 2, self.p) == 1 - - def valid(self, P): - """ - Determine whether we have a valid representation of a point - on our curve. We assume that the x and y coordinates - are always reduced modulo p, so that we can compare - two points for equality with a simple ==. - """ - if P == O: - return True +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) else: - return ( - (P.y**2 - (P.x**3 + self.a*P.x + self.b)) % self.p == 0 and - 0 <= P.x < self.p and 0 <= P.y < self.p) - - def inv_mod_p(self, x): - """ - Compute an inverse for x modulo p, assuming that x - is not divisible by p. - """ - if x % self.p == 0: - raise ZeroDivisionError("Impossible inverse") - return pow(x, self.p-2, self.p) - - def ec_inv(self, P): - """ - Inverse of the point P on the elliptic curve y^2 = x^3 + ax + b. - """ - if P == O: - return P - return Point(P.x, (-P.y) % self.p) - - def ec_add(self, P, Q): - """ - Sum of the points P and Q on the elliptic curve y^2 = x^3 + ax + b. - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - if not (self.valid(P) and self.valid(Q)): - raise ValueError("Invalid inputs") - - # Deal with the special cases where either P, Q, or P + Q is - # the origin. - if P == O: - result = Q - elif Q == O: - result = P - elif Q == self.ec_inv(P): - result = O + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) else: - # Cases not involving the origin. - if P == Q: - dydx = (3 * P.x**2 + self.a) * self.inv_mod_p(2 * P.y) - else: - dydx = (Q.y - P.y) * self.inv_mod_p(Q.x - P.x) - x = (dydx**2 - P.x - Q.x) % self.p - y = (dydx * (P.x - x) - P.y) % self.p - result = Point(x, y) - - # The above computations *should* have given us another point - # on the curve. - assert self.valid(result) - return result - - def double_add_algorithm(self, scalar, P): - """ - Double-and-Add Algorithm for Point Multiplication - Input: A scalar in the range 0-p and a point on the elliptic curve P - https://stackoverflow.com/questions/31074172/elliptic-curve-point-addition-over-a-finite-field-in-python - """ - assert self.valid(P) - - b = bin(scalar).lstrip('0b') - T = P - for i in b[1:]: - T = self.ec_add(T, T) - if i == '1': - T = self.ec_add(T, P) - - assert self.valid(T) - return T - -class Peer: - """ - Implements https://wlan1nde.wordpress.com/2018/09/14/wpa3-improving-your-wlan-security/ - Take a ECC curve from here: https://safecurves.cr.yp.to/ - - Example: NIST P-384 - y^2 = x^3-3x+27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575 - modulo p = 2^384 - 2^128 - 2^96 + 2^32 - 1 - 2000 NIST; also in SEC 2 and NSA Suite B - - See here: https://www.rfc-editor.org/rfc/rfc5639.txt - -Curve-ID: brainpoolP256r1 - p = - A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 - A = - 7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 - B = - 26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 - x = - 8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 - y = - 547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 - q = - A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - h = 1 - """ - - def __init__(self, password, mac_address, name): - self.name = name - self.password = password - self.mac_address = mac_address - - # Try out Curve-ID: brainpoolP256t1 - self.p = int('A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377', 16) - self.a = int('7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9', 16) - self.b = int('26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6', 16) - self.q = int('A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7', 16) - self.curve = Curve(self.a, self.b, self.p) - - # A toy curve - # self.a, self.b, self.p = 2, 2, 17 - # self.q = 19 - # self.curve = Curve(self.a, self.b, self.p) - - def initiate(self, other_mac, k=40): - """ - See algorithm in https://tools.ietf.org/html/rfc7664 - in section 3.2.1 - """ - self.other_mac = other_mac - found = 0 - num_valid_points = 0 - counter = 1 - n = self.p.bit_length() + 64 - - while counter <= k: - base = self.compute_hashed_password(counter) - temp = self.key_derivation_function(n, base, 'Dragonfly Hunting And Pecking') - seed = (temp % (self.p - 1)) + 1 - val = self.curve.curve_equation(seed) - if self.curve.is_quadratic_residue(val): - if num_valid_points < 5: - x = seed - save = base - found = 1 - num_valid_points += 1 - logger.debug('Got point after {} iterations'.format(counter)) - - counter = counter + 1 - - if found == 0: - logger.error('No valid point found after {} iterations'.format(k)) - elif found == 1: - # https://crypto.stackexchange.com/questions/6777/how-to-calculate-y-value-from-yy-mod-prime-efficiently - # https://rosettacode.org/wiki/Tonelli-Shanks_algorithm - y = tonelli_shanks(self.curve.curve_equation(x), self.p) - - PE = Point(x, y) - - # check valid point - assert self.curve.curve_equation(x) == pow(y, 2, self.p) - - logger.info('[{}] Using {}-th valid Point={}'.format(self.name, num_valid_points, PE)) - logger.info('[{}] Point is on curve: {}'.format(self.name, self.curve.valid(PE))) - - self.PE = PE - assert self.curve.valid(self.PE) - - def commit_exchange(self): - """ - This is basically Diffie Hellman Key Exchange (or in our case ECCDH) - - In the Commit Exchange, both sides commit to a single guess of the - password. The peers generate a scalar and an element, exchange them - with each other, and process the other's scalar and element to - generate a common and shared secret. - - If we go back to elliptic curves over the real numbers, there is a nice geometric - interpretation for the ECDLP: given a starting point P, we compute 2P, 3P, . . ., - d P = T , effectively hopping back and forth on the elliptic curve. We then publish - the starting point P (a public parameter) and the final point T (the public key). In - order to break the cryptosystem, an attacker has to figure out how often we “jumped” - on the elliptic curve. The number of hops is the secret d, the private key. - """ - # seed the PBG before picking a new random number - # random.seed(time.process_time()) - - # None or no argument seeds from current time or from an operating - # system specific randomness source if available. - random.seed() - - # Otherwise, each party chooses two random numbers, private and mask - self.private = random.randrange(1, self.p) - self.mask = random.randrange(1, self.p) - - logger.debug('[{}] private={}'.format(self.name, self.private)) - logger.debug('[{}] mask={}'.format(self.name, self.mask)) - - # These two secrets and the Password Element are then used to construct - # the scalar and element: - - # what is q? - # o A point, G, on the elliptic curve, which serves as a generator for - # the ECC group. G is chosen such that its order, with respect to - # elliptic curve addition, is a sufficiently large prime. - # - # o A prime, q, which is the order of G, and thus is also the size of - # the cryptographic subgroup that is generated by G. - - # https://math.stackexchange.com/questions/331329/is-it-possible-to-compute-order-of-a-point-over-elliptic-curve - # In the elliptic Curve cryptography, it is said that the order of base point - # should be a prime number, and order of a point P is defined as k, where kP=O. - - # Theorem 9.2.1 The points on an elliptic curve together with O - # have cyclic subgroups. Under certain conditions all points on an - # elliptic curve form a cyclic group. - # For this specific curve the group order is a prime and, according to Theo- - # rem 8.2.4, every element is primitive. - - # Question: What is the order of our PE? - # the order must be p, since p is a prime - - self.scalar = (self.private + self.mask) % self.q - - # If the scalar is less than two (2), the private and mask MUST be - # thrown away and new values generated. Once a valid scalar and - # Element are generated, the mask is no longer needed and MUST be - # irretrievably destroyed. - if self.scalar < 2: - raise ValueError('Scalar is {}, regenerating...'.format(self.scalar)) - - P = self.curve.double_add_algorithm(self.mask, self.PE) - - # get the inverse of res - # −P = (x_p , p − y_p ). - self.element = self.curve.ec_inv(P) - - assert self.curve.valid(self.element) - - # The peers exchange their scalar and Element and check the peer's - # scalar and Element, deemed peer-scalar and Peer-Element. If the peer - # has sent an identical scalar and Element -- i.e., if scalar equals - # peer-scalar and Element equals Peer-Element -- it is sign of a - # reflection attack, and the exchange MUST be aborted. If the values - # differ, peer-scalar and Peer-Element must be validated. - - logger.info('[{}] Sending scalar and element to the Peer!'.format(self.name)) - logger.info('[{}] Scalar={}'.format(self.name, self.scalar)) - logger.info('[{}] Element={}'.format(self.name, self.element)) - - return self.scalar, self.element - - def compute_shared_secret(self, peer_element, peer_scalar, peer_mac): - """ - ss = F(scalar-op(private, - element-op(peer-Element, - scalar-op(peer-scalar, PE)))) + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a - AP1: K = private(AP1) • (scal(AP2) • P(x, y) ◊ new_point(AP2)) - = private(AP1) • private(AP2) • P(x, y) - AP2: K = private(AP2) • (scal(AP1) • P(x, y) ◊ new_point(AP1)) - = private(AP2) • private(AP1) • P(x, y) - A shared secret element is computed using one’s rand and - the other peer’s element and scalar: - Alice: K = rand A • (scal B • PW + elemB ) - Bob: K = rand B • (scal A • PW + elemA ) +###################################################################### - Since scal(APx) • P(x, y) is another point, the scalar multiplied point - of e.g. scal(AP1) • P(x, y) is added to the new_point(AP2) and afterwards - multiplied by private(AP1). - """ - self.peer_element = peer_element - self.peer_scalar = peer_scalar - self.peer_mac = peer_mac - assert self.curve.valid(self.peer_element) - - # If both the peer-scalar and Peer-Element are - # valid, they are used with the Password Element to derive a shared - # secret, ss: +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a - Z = self.curve.double_add_algorithm(self.peer_scalar, self.PE) - ZZ = self.curve.ec_add(self.peer_element, Z) - K = self.curve.double_add_algorithm(self.private, ZZ) - self.k = K[0] +###################################################################### - logger.info('[{}] Shared Secret ss={}'.format(self.name, self.k)) +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### - own_message = '{}{}{}{}{}{}'.format(self.k , self.scalar , self.peer_scalar , self.element[0] , self.peer_element[0] , self.mac_address).encode() +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + - H = hashlib.sha256() - H.update(own_message) - self.token = H.hexdigest() +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + - return self.token +###################################################################### - def confirm_exchange(self, peer_token): - """ - In the Confirm Exchange, both sides confirm that they derived the - same secret, and therefore, are in possession of the same password. - """ - peer_message = '{}{}{}{}{}{}'.format(self.k , self.peer_scalar , self.scalar , self.peer_element[0] , self.element[0] , self.peer_mac).encode() - H = hashlib.sha256() - H.update(peer_message) - self.peer_token_computed = H.hexdigest() - logger.info('[{}] Computed Token from Peer={}'.format(self.name, self.peer_token_computed)) - logger.info('[{}] Received Token from Peer={}'.format(self.name, peer_token)) +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map - # Pairwise Master Key” (PMK) - # compute PMK = H(k | scal(AP1) + scal(AP2) mod q) - pmk_message = '{}{}'.format(self.k, (self.scalar + self.peer_scalar) % self.q).encode() - #H = hashlib.sha256() - #H.update(pmk_message) - self.PMK = hashlib.sha256(pmk_message).digest() +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### - logger.info('[{}] Pairwise Master Key(PMK)={}'.format(self.name, self.PMK)) - return self.PMK +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I - def key_derivation_function(self, n, base, seed): - """ - B.5.1 Per-Message Secret Number Generation Using Extra Random Bits - Key derivation function from Section B.5.1 of [FIPS186-4] +####################################################################### - The key derivation function, KDF, is used to produce a - bitstream whose length is equal to the length of the prime from the - group's domain parameter set plus the constant sixty-four (64) to - derive a temporary value, and the temporary value is modularly - reduced to produce a seed. - """ - combined_seed = '{}{}'.format(base, seed).encode() +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z - # base and seed concatenated are the input to the RGB - random.seed(combined_seed) - # Obtain a string of N+64 returned_bits from an RBG with a security strength of - # requested_security_strength or more. +###################################################################### - randbits = random.getrandbits(n) - binary_repr = format(randbits, '0{}b'.format(n)) - assert len(binary_repr) == n +def keygen_Alice(SK_Alice, params, splits, MAX): + #input: secret random even number in (1,oA-1) + # public parameters [XPB, XPA, YPA] + #output: public key [phi_A(x(PB)),phi_A(x(QB)),phi_A(x(QB-PB))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secre_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Alice, 'Alice') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + #first 4-isogeny (different from rest) + phiPX, phiPZ, A2, C2 = first_4_isog(phiPX, phiPZ, A) + phiQX, phiQZ, A2, C2 = first_4_isog(phiQX, phiQZ, A) + phiDX, phiDZ, A2, C2 = first_4_isog(phiDX, phiDZ, A) + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 4 + + pts = [] + index = 0 + + #Alice's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 4-isogeny at Bob's points + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Alices's public key, values in Fp2 + PK_Alice = [phiPX, phiQX, phiDX] + + msg="Alice's keygen needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Alice's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Alice + - logger.debug('Rand={}'.format(binary_repr)) +###################################################################### - # Convert returned_bits to the non-negative integer c (see Appendix C.2.1). - C = 0 - for i in range(n): - if int(binary_repr[i]) == 1: - C += pow(2, n-i) +def shared_secret_Alice(SK_Alice, PK_Bob, splits, MAX): + #input: Alices's secret key SK_Alice + # Bob's public key + #output: Alice's shared secret: j-invariant of E_AB + + A = get_A(PK_Bob[0], PK_Bob[1], PK_Bob[2]) + C = Complex(1) #start on Bob's curve + + #compute R=phi_B(xPA)+SK_Alice*phi_B(xQA) + RX, RZ = LADDER_3_pt(SK_Alice, PK_Bob[0], PK_Bob[1], PK_Bob[2], A, 'Alice') + iso, mul = 0, 0 #counters + + #first isogeny + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 1 + + pts = [] + index = 0 + + #main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + + secret_Alice = j_inv(A, C) + + msg="Alice's secret needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + + return secret_Alice - logger.debug('C={}'.format(C)) +###################################################################### - #k = (C % (n - 1)) + 1 +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 - k = C +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] - logger.debug('k={}'.format(k)) +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) - return k +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 - def compute_hashed_password(self, counter): - maxm = max(self.mac_address, self.other_mac) - minm = min(self.mac_address, self.other_mac) - message = '{}{}{}{}'.format(maxm, minm, self.password, counter).encode() - logger.debug('Message to hash is: {}'.format(message)) - H = hashlib.sha256() - H.update(message) - digest = H.digest() - return digest +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +params_Alice = [XPB, XPA, YPA] +# params_Bob = [XPA, XPB, YPB] + +################################################################# +#strategy paramters from MSR +splits_Alice = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11,\ +12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21,\ +20, 21, 21, 21, 21, 21, 22, 25, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32,\ +32, 32, 32, 32, 33, 33, 33, 35, 36, 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38,\ +38, 39, 40, 41, 42, 38, 39, 40, 41, 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48,\ +48, 48, 49, 49, 48, 53, 54, 51, 52, 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62,\ +63, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66,\ +69, 70, 66, 67, 66, 69, 70, 69, 70, 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72,\ +74, 74, 75, 72, 72, 74, 75, 75, 72, 72, 74, 75, 75, 77, 77, 79, 80, 80, 82 ] + +MAX_Alice = 185 + +####################################################################### def encrypting(key, filename): chunksize = 64*1024 @@ -642,17 +1028,10 @@ def encrypting(key, filename): return outputFile -def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POSTFIX): - #Own mac address - own_mac = (':'.join(re.findall('..', '%012x' % uuid.getnode()))) - #Encode MAC address with BER - own_mac_BER = asn1_file.encode('DataMac', {'data': own_mac}) - - print (own_mac) - ap = Peer('abc1238', own_mac, 'AP') - - logger.info('Attempting Dragonfly Key Exchange with cloud\n') +def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POSTFIX): + + logger.info('Attempting Supersingular Key Exchange with cloud\n') logger.info('Starting hunting and pecking to derive PE...\n') own_ipaddr = "192.168.0.4" @@ -664,80 +1043,85 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS connection, output_address = own_sock.accept() with connection: print ("Connecting from", output_address) - raw_other_mac = connection.recv(1024) - #decode BER and get MAC address - other_decode_mac = asn1_file.decode('DataMac', raw_other_mac) - other_mac = other_decode_mac.get('data') - - print ("Other MAC", other_mac) - - #Sending BER encoded - connection.send(own_mac_BER) - - ap.initiate(other_mac) - - print() - logger.info('Starting dragonfly commit exchange...\n') - - scalar_ap, element_ap = ap.commit_exchange() - - #encode scalar_ap / element_ap - scalar_complete = ("\n".join([str(scalar_ap), str(element_ap)])) - encoded = asn1_file.encode('DataScalarElement',{'data': scalar_complete}) - - print('data send', scalar_complete) - - #Send BER encoded scalar / element ap to peer + + logger.info('Starting SIDH commit exchange...\n') + + #Calculates Secret and Public Keys + n_Alice = randint(0,(lA**eA)/2) + n_Alice = 2*n_Alice + logger.info("Alice's secret key:") + logger.info(n_Alice) + print('') + PKA = keygen_Alice(n_Alice, params_Alice, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's Public Key:") + logger.info('%s',(PKA[0])) + logger.info('%s',(PKA[1])) + logger.info('%s',(PKA[2])) + #print((PKA[1])) + #print((PKA[2])) + print('') + + keyreal1 = PKA[0].re + keyimag1 = PKA[0].im + keyreal2 = PKA[1].re + keyimag2 = PKA[1].im + keyreal3 = PKA[2].re + keyimag3 = PKA[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + logger.info('Data Sent %s %s %s', PKA[0], PKA[1], PKA[2]) + + #Sends Alice's encoded Public key to Bob connection.sendall(encoded) print() - logger.info('Computing shared secret...\n') - - #recevied BER encode scalar / element and decoded - scalar_element_ap_encoded= connection.recv(1024) - scalar_element_ap_decoded = asn1_file.decode('DataScalarElement', scalar_element_ap_encoded) - scalar_element_ap = scalar_element_ap_decoded.get('data') - - print('scalar element received', scalar_element_ap) + logger.info('Receiving Clients Public Key...\n') + + #Received Bob's encoded Public Key and decodes it + PKB_encoded = connection.recv(2048) + PKB_decoded = asn1_file.decode('DataPublicKey', PKB_encoded) + #Retrieving Bob's public key in INT Form + keyreal1B = PKB_decoded.get('keyreal1') + keyimag1B = PKB_decoded.get('keyimag1') + keyreal2B = PKB_decoded.get('keyreal2') + keyimag2B = PKB_decoded.get('keyimag2') + keyreal3B = PKB_decoded.get('keyreal3') + keyimag3B = PKB_decoded.get('keyimag3') + #Forming Bob's public key into complex form for calculations + phiPX = Complex(keyreal1B, keyimag1B) + phiQX = Complex(keyreal2B, keyimag2B) + phiDX = Complex(keyreal3B, keyimag3B) + + PKB = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKB[0]) + logger.info(PKB[1]) + logger.info(PKB[2]) - data = scalar_element_ap.split('\n') - scalar_sta = data[0] - element_sta = data[1] - print ('scalar_sta recv:',scalar_sta) - print() - print ('element_sta recv:',element_sta) - print () - namedtuple_element_sta = eval(element_sta) - print(namedtuple_element_sta.y, namedtuple_element_sta.x) - print () - print () - ap_token = ap.compute_shared_secret(namedtuple_element_sta, int(scalar_sta), other_mac) - - #Encode ap_token to be BER and send to peer - apToken_encoded = asn1_file.encode('DataStaAp',{'data':ap_token}) - connection.send(apToken_encoded) - - print("ap_token data being send over", ap_token) - print() - logger.info('Confirm Exchange...\n') - - #Received BER encoded STA token and decode it - staToken_encoded = connection.recv(1024) - staToken_decoded = asn1_file.decode('DataStaAp', staToken_encoded) - sta_token = staToken_decoded.get('data') - - print('received STA token', sta_token) - - PMK_Key = ap.confirm_exchange(sta_token) - #print (PMK_Key) + logger.info('Computing shared secret...\n') - dragonfly_time = time.perf_counter() - time_elapsed = round((dragonfly_time - start), 3) - print('Total time elapsed for Dragonfly Key Exchange:', time_elapsed, 's') + SKA = shared_secret_Alice(n_Alice, PKB, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's shared secret:") + logger.info(SKA) + print('') + + #Hashing Shared Secret + SKA_ComplexToString = secretKeyEncoder().encode(SKA) + SKA_StringToBytes = SKA_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKA_StringToBytes).digest() + + + sidh_time = time.perf_counter() + time_elapsed = round((sidh_time - start), 3) + print('Total time elapsed for Supersingular Isogeny Key Exchange:', time_elapsed, 's') f = open('timings.txt', 'a') - f.write('Total time elapsed for Dragonfly Key Exchange:') + f.write('Total time elapsed for SIDH Key Exchange:') f.write(str(time_elapsed)) f.write("") f.close @@ -756,7 +1140,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS c1 = open("client1", "w") c1.write(client1_ipaddr) c1.close() - output_client1 = encrypting(PMK_Key, CLIENT1) + output_client1 = encrypting(SK, CLIENT1) c1 = open("client1.hacklab", "rb") client1_ipaddr_read = c1.read() cl_ip.append(client1_ipaddr_read) @@ -765,7 +1149,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS c2 = open("client2", "w") c2.write(client2_ipaddr) c2.close() - output_client2 = encrypting(PMK_Key, CLIENT2) + output_client2 = encrypting(SK, CLIENT2) c2 = open("client2.hacklab", "rb") client2_ipaddr_read = c2.read() cl_ip.append(client2_ipaddr_read) @@ -775,7 +1159,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS c3 = open("client3", "w") c3.write(client3_ipaddr) c3.close() - output_client3 = encrypting(PMK_Key, CLIENT3) + output_client3 = encrypting(SK, CLIENT3) c3 = open("client3.hacklab", "rb") client3_ipaddr_read = c3.read() cl_ip.append(client3_ipaddr_read) @@ -787,7 +1171,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS c4 = open("client4", "w") c4.write(client4_ipaddr) c4.close() - output_client4 = encrypting(PMK_Key, CLIENT4) + output_client4 = encrypting(SK, CLIENT4) c4 = open("client4.hacklab", "rb") client4_ipaddr_read = c4.read() cl_ip.append(client4_ipaddr_read) @@ -798,7 +1182,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS o1 = open("opcode1", "w") o1.write(OPERATION1) o1.close() - output_operator1 = encrypting(PMK_Key, OPCODE1) + output_operator1 = encrypting(SK, OPCODE1) o1 = open("opcode1.hacklab", "rb") operator1_read = o1.read() cl_op.append(operator1_read) @@ -811,7 +1195,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS o2 = open("opcode2", "w") o2.write(OPERATION2) o2.close() - output_operator2 = encrypting(PMK_Key, OPCODE2) + output_operator2 = encrypting(SKy, OPCODE2) o2 = open("opcode2.hacklab", "rb") operator2_read = o2.read() cl_op.append(operator2_read) @@ -826,7 +1210,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS o3 = open("opcode3", "w") o3.write(OPERATION3) o3.close() - output_operator3 = encrypting(PMK_Key, OPCODE3) + output_operator3 = encrypting(SK, OPCODE3) o3 = open("opcode3.hacklab", "rb") operator3_read = o3.read() cl_op.append(operator3_read) @@ -840,7 +1224,7 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS p = open("postfix", "w") p.write(postfix_expr) p.close() - output_postfix = encrypting(PMK_Key, POSTFIX) + output_postfix = encrypting(SK, POSTFIX) p = open("postfix.hacklab", "rb") postfix_read = p.read() postfix_dict['postfix'] = postfix_read @@ -1035,10 +1419,10 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS #subprocess.call('./Div_verif') stop = time.perf_counter() - time_elapsed1 = round((stop - dragonfly_time), 3) + time_elapsed1 = round((stop - sidh_time), 3) time_elapsed2 = round((stop - start), 3) - print('Total time elapsed excluding Dragonfly Key Exchange:', time_elapsed1, 's') - print('Total time elapsed including Dragonfly Key Exchange:', time_elapsed2, 's') + print('Total time elapsed excluding SIDH Key Exchange:', time_elapsed1, 's') + print('Total time elapsed including SIDH Key Exchange:', time_elapsed2, 's') os.system('python3 reset.py') @@ -1249,4 +1633,4 @@ def handshake(CLIENT1, CLIENT2, CLIENT3, CLIENT4, OPCODE1, OPCODE2, OPCODE3, POS usr_input_time1 = int(usr_input_time_stop - usr_input_time) -dragonfly() +sidh() diff --git a/Output/reset.py b/Output/reset.py index 2867a79..31c4fef 100755 --- a/Output/reset.py +++ b/Output/reset.py @@ -60,6 +60,6 @@ except: None try: - os.remove("dragonfly.log") + os.remove("keyexchange.log") except: None diff --git a/Output/sidh_private_Output.py b/Output/sidh_private_Output.py new file mode 100644 index 0000000..193bb13 --- /dev/null +++ b/Output/sidh_private_Output.py @@ -0,0 +1,1043 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +server_address = ('192.168.0.3', 4380) + +while True: + try: + sock.connect(server_address) + break + except ConnectionRefusedError as conn_error: + print("Attempting to connect to server...") + time.sleep(5) + except: + # print("Unexpected error", sys.exc_info()[0]) + continue + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': + handshake() + sock.close() diff --git a/SIDHAliceCleaned.py b/SIDHAliceCleaned.py new file mode 100644 index 0000000..0e6fa85 --- /dev/null +++ b/SIDHAliceCleaned.py @@ -0,0 +1,1104 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os, random, struct +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +from Cryptodome.Hash import SHA256 +from optparse import * +from _thread import * +import asn1tools +import threading +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +lock = threading.Lock() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +#define thread +ThreadCount = 0 + +#output hostname, domain name, ip address +print ("Working on %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +#bind socket to port +server_address = ('192.168.0.3', 4380) +print ("Starting up on %s port %s" % server_address) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.bind(server_address) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Alice(SK_Alice, params, splits, MAX): + #input: secret random even number in (1,oA-1) + # public parameters [XPB, XPA, YPA] + #output: public key [phi_A(x(PB)),phi_A(x(QB)),phi_A(x(QB-PB))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secre_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Alice, 'Alice') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + #first 4-isogeny (different from rest) + phiPX, phiPZ, A2, C2 = first_4_isog(phiPX, phiPZ, A) + phiQX, phiQZ, A2, C2 = first_4_isog(phiQX, phiQZ, A) + phiDX, phiDZ, A2, C2 = first_4_isog(phiDX, phiDZ, A) + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 4 + + pts = [] + index = 0 + + #Alice's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 4-isogeny at Bob's points + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + phiPX, phiPZ = eval_4_isog(consts, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_4_isog(consts, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_4_isog(consts, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Alices's public key, values in Fp2 + PK_Alice = [phiPX, phiQX, phiDX] + + msg="Alice's keygen needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Alice's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Alice + + +###################################################################### + +def shared_secret_Alice(SK_Alice, PK_Bob, splits, MAX): + #input: Alices's secret key SK_Alice + # Bob's public key + #output: Alice's shared secret: j-invariant of E_AB + + A = get_A(PK_Bob[0], PK_Bob[1], PK_Bob[2]) + C = Complex(1) #start on Bob's curve + + #compute R=phi_B(xPA)+SK_Alice*phi_B(xQA) + RX, RZ = LADDER_3_pt(SK_Alice, PK_Bob[0], PK_Bob[1], PK_Bob[2], A, 'Alice') + iso, mul = 0, 0 #counters + + #first isogeny + RX, RZ, A, C = first_4_isog(RX, RZ, A) + iso = iso + 1 + + pts = [] + index = 0 + + #main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 4. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = edDBLe(RX, RZ, A, C, 2*m) + mul = mul + m + index = index + m + + #compute isogeny + A, C, consts = get_4_isog(RX, RZ) + + #evaluate 4-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_4_isog(consts, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C, consts = get_4_isog(RX, RZ) + + secret_Alice = j_inv(A, C) + + msg="Alice's secret needs "+str(mul)+" multiplications by 4 and "+str(iso)+" isogenies" + print(msg) + + return secret_Alice + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +params_Alice = [XPB, XPA, YPA] +# params_Bob = [XPA, XPB, YPB] + +################################################################# +#strategy paramters from MSR +splits_Alice = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11,\ +12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21,\ +20, 21, 21, 21, 21, 21, 22, 25, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32,\ +32, 32, 32, 32, 33, 33, 33, 35, 36, 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38,\ +38, 39, 40, 41, 42, 38, 39, 40, 41, 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48,\ +48, 48, 49, 49, 48, 53, 54, 51, 52, 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62,\ +63, 64, 64, 64, 64, 64, 64, 64, 64, 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66,\ +69, 70, 66, 67, 66, 69, 70, 69, 70, 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72,\ +74, 74, 75, 72, 72, 74, 75, 75, 72, 72, 74, 75, 75, 77, 77, 79, 80, 80, 82 ] + +MAX_Alice = 185 + +####################################################################### + +def encrypting(key, filename): + chunksize = 64*1024 + outputFile = filename+".hacklab" + filesize = str(os.path.getsize(filename)).zfill(16) + IV = Random.new().read(16) + + encryptor = AES.new(key, AES.MODE_CBC, IV) + with open(filename, 'rb') as infile: + with open(outputFile, 'wb') as outfile: + outfile.write(filesize.encode('utf-8')) + outfile.write(IV) + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + elif len(chunk) % 16 != 0: + chunk += b' ' * (16 - (len(chunk) % 16)) + outfile.write(encryptor.encrypt(chunk)) + + return outputFile + +#Handles connections received from Clients +class ClientThread(threading.Thread): + def __init__(self,connection,clientAddr): + threading.Thread.__init__(self) + self.clientAddr = clientAddr + self.connection = connection + logger.info("Connection coming from %s", connection) + + def run(self): + + logger.info('Starting Key Exchange...\n') + + with self.connection: + + print() + logger.info('Client found. Key Exchange Begins...\n') + + #Calculates Secret and Public Keys + n_Alice = randint(0,(lA**eA)/2) + n_Alice = 2*n_Alice + logger.info("Alice's secret key:") + logger.info(n_Alice) + print('') + PKA = keygen_Alice(n_Alice, params_Alice, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's Public Key:") + logger.info('%s',(PKA[0])) + logger.info('%s',(PKA[1])) + logger.info('%s',(PKA[2])) + #print((PKA[1])) + #print((PKA[2])) + print('') + + keyreal1 = PKA[0].re + keyimag1 = PKA[0].im + keyreal2 = PKA[1].re + keyimag2 = PKA[1].im + keyreal3 = PKA[2].re + keyimag3 = PKA[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + logger.info('Data Sent %s %s %s', PKA[0], PKA[1], PKA[2]) + + #Sends Alice's encoded Public key to Bob + self.connection.sendall(encoded) + print() + + logger.info('Receiving Clients Public Key...\n') + + #Received Bob's encoded Public Key and decodes it + PKB_encoded = self.connection.recv(2048) + PKB_decoded = asn1_file.decode('DataPublicKey', PKB_encoded) + #Retrieving Bob's public key in INT Form + keyreal1B = PKB_decoded.get('keyreal1') + keyimag1B = PKB_decoded.get('keyimag1') + keyreal2B = PKB_decoded.get('keyreal2') + keyimag2B = PKB_decoded.get('keyimag2') + keyreal3B = PKB_decoded.get('keyreal3') + keyimag3B = PKB_decoded.get('keyimag3') + #Forming Bob's public key into complex form for calculations + phiPX = Complex(keyreal1B, keyimag1B) + phiQX = Complex(keyreal2B, keyimag2B) + phiDX = Complex(keyreal3B, keyimag3B) + + PKB = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKB[0]) + logger.info(PKB[1]) + logger.info(PKB[2]) + + print() + logger.info('Computing shared secret...\n') + + SKA = shared_secret_Alice(n_Alice, PKB, splits_Alice, MAX_Alice) + print('') + logger.info("Alice's shared secret:") + logger.info(SKA) + print('') + + #Hashing Shared Secret + SKA_ComplexToString = secretKeyEncoder().encode(SKA) + SKA_StringToBytes = SKA_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKA_StringToBytes).digest() + + print ("Getting keys...\n") + lock.acquire() + + print("Printing secret key...\n") + secret_key = "secret.key" + + print("Printing nbit key...\n") + nbit_key = "nbit.key" + + output_secret_key = encrypting(SK, secret_key) + print("This file", output_secret_key, "is encrypted secret key\n") + + output_nbit_key = encrypting(SK, nbit_key) + print("This file", output_nbit_key, "is encrypted nbit key\n") + + s = open(output_secret_key, "rb") + keycontent = s.read(8192) + + t = open(output_nbit_key, "rb") + nbitcontent = t.read(8192) + + #Encode key in BER format + priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) + + # Send the BER encoded file to the peer + while (keycontent and nbitcontent): + self.connection.sendall(priv_key_BER) + keycontent = s.read(8192) + nbitcontent = t.read(8192) + priv_key_BER = asn1_file.encode('DataKey', {'key': keycontent, 'nbit': nbitcontent}) + s.close() + print('Original secret key file size: ', os.path.getsize(secret_key)) + print ('Encrypted secret key file size: ', os.path.getsize(output_secret_key)) + os.system("md5sum secret.key") + + print('Original nbit key file size: ', os.path.getsize(nbit_key)) + print ('Encrypted nbit key file size: ', os.path.getsize(output_nbit_key)) + os.system("md5sum nbit.key") + + lock.release() + +####################################################################### + +def handshake(): + HOSTUP1 = True if os.system("ping -c 2 192.168.0.21 > /dev/null 2>&1") == 0 else False + HOSTUP2 = True if os.system("ping -c 2 192.168.0.22 > /dev/null 2>&1") == 0 else False + HOSTUP3 = True if os.system("ping -c 2 192.168.0.23 > /dev/null 2>&1") == 0 else False + + hostup = int(sum([HOSTUP1, HOSTUP2, HOSTUP3]) + 1) + position = 1 + + # Generate keys once only + # subprocess.call("./keygen") + + while True: + sock.listen() + connection, client_address = sock.accept() + threading_name = str(hostup) + if (client_address[0]) == "192.168.0.4" and position == 1: + newThread = ClientThread(connection, client_address) + newThread.start() + hostup -= 1 + position = 0 + elif hostup != 0 and position == 0 and (client_address[0]) != "192.168.0.1": + newThread = ClientThread(connection, client_address) + newThread.start() + hostup -=1 + elif hostup == 0: + position = 1 + break + else: + connection.close() + sock.close() + continue + +####################################################################### + +if __name__ == '__main__': +handshake() +sock.close() diff --git a/SIDHBobCleaned.py b/SIDHBobCleaned.py new file mode 100644 index 0000000..68be483 --- /dev/null +++ b/SIDHBobCleaned.py @@ -0,0 +1,1047 @@ +#!/usr/bin/env python3 +# implementation of SIDH with optimal strategies +# and Edwards arithmetic +# +# based on code from https://github.com/Microsoft/PQCrypto-SIDH +# by Costello, Longa, Naehrig (Microsoft Research) +# + +import time +import hashlib +import random +import logging +import socket +import re, uuid +import base64 +import os +import subprocess +from collections import namedtuple +from Cryptodome.Cipher import AES +from Cryptodome import Random +import asn1tools +import sys +import cProfile, pstats, io +from random import randint +import json +from json import JSONEncoder +pr = cProfile.Profile() +pr.enable() + +#Compile asn1 file for secret_key +asn1_file = asn1tools.compile_files('declaration.asn') + +#create tcp/ip socket +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +#retrieve local hostname +local_hostname = socket.gethostname() + +#get fully qualified hostname +local_fqdn = socket.getfqdn() + +#get the according ip address +ip_address = socket.gethostbyname(local_hostname) + +server_address = ('192.168.0.3', 4380) +while True: + try: + sock.connect(server_address) + print("Successfully connected to Keygen") + break + except ConnectionRefusedError as conn_error: + print('A connection error has occured') + print(conn_error) + print('Attempting to connect to server again...') + time.sleep(5) + except KeyboardInterrupt: + print('Ctrl-C pressed to terminate program') + pass + except: + print('Unexpected error:', sys.exc_info()[0]) + +print ("Connecting to %s (%s) with %s" % (local_hostname, local_fqdn, ip_address)) + +logger = logging.getLogger('Key Exchange') +logger.setLevel(logging.INFO) +# create file handler which logs even debug messages +fh = logging.FileHandler('keyexchange.log') +fh.setLevel(logging.DEBUG) +# create console handler with a higher log level +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +# create formatter and add it to the handlers +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +ch.setFormatter(formatter) +fh.setFormatter(formatter) +# add the handlers to logger +logger.addHandler(ch) +logger.addHandler(fh) + + +class Complex(object): + def __init__(self, real, imag=0): + self.re = int(real) + self.im = int(imag) + + def __add__(self, other): + return Complex(self.re + other.re, self.im + other.im) + + def __sub__(self, other): + return Complex(self.re - other.re, self.im - other.im) + + def __mul__(self, other): + ab0=self.re*other.re + ab1=self.im*other.im + c=(self.re+self.im)*(other.re+other.im) + return Complex((ab0-ab1)%p, (c-ab0-ab1)%p) + + + def __neg__(self): + return Complex(-self.re, -self.im) + + def __eq__(self, other): + return self.re == other.re and self.im == other.im + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return '(%u, %u)' % (self.re %p, self.im %p) + + def __repr__(self): + return 'Complex' + str(self.re ,self.im) + + def __pow__(self, power): #only squares required + return Complex(((self.re+self.im)*(self.re-self.im))%p, (2*self.im*self.re)%p) + + def __mod__(self, p): + return Complex(self.re % p, self.im % p) + +class secretKeyEncoder(JSONEncoder): + def default(self, o): + return o.__dict__ +#################################################################### + +def j_inv(A, C): + #input: parameters (A:C) of projective curve + #output: j-invariant + jinv = A**2 + t1 = C**2 + t0 = t1 + t1 + t0 = jinv - t0 + t0 = t0 - t1 + jinv = t0 - t1 + t1 = t1**2 + jinv = jinv * t1 + t0 = t0 + t0 + t0 = t0 + t0 + t1 = t0**2 + t0 = t0 * t1 + t0 = t0 + t0 + t0 = t0 + t0 + jinv = inv(jinv) + jinv = t0 * jinv + + return jinv #cost: 3M+4S+8a+1I + + +########################################################### + + +#function for Montgomery differential addition +def xADD(XP, ZP, XQ, ZQ, xPQ): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ + # affine difference x(P-Q)=xPQ + #output: projective coordinates x(Q+P)=XQP/XZP + t0 = XP + ZP + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t0 = (XP * t0)%p + t1 = (ZP * t1)%p + ZP = t0 - t1 + XP = t0 + t1 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + return XQP, ZQP #cost: 3M+2S+6a + + +############################################################## + + +#function for Montgomery doubling with projective curve constant +def xDBL(X, Z, A24, C24): + #input: projective coordinates xP=X/Z + # curve constant A24/C24 = (A/C+2)/4 + #output: projective coordinates x(2P)=X2/Z2 + t0 = X - Z #code from msr + t1 = X + Z + t0 = t0**2 + t1 = t1**2 + Z2 = C24 * t0 + X2 = Z2 * t1 + t1 = t1 - t0 + t0 = A24 * t1 + Z2 = Z2 + t0 + Z2 = Z2 * t1 + + return X2, Z2 #cost: 4M+2S+4a + +######################################################## + +#Edwards-version of xDBL +def edDBL(Y,Z,AE,DE): + t0=Y**2 + t1=Z**2 + t2=t0+t1 + t2=t2**2 + t0=t0**2 + t1=t1**2 + t2=t2-t0 + t2=t2-t1 + Y2=t2*AE #Y2=2a*Y^2Z^2 + t2=t2*DE #t2=2d*Y^2Z^2 + t0=t0*DE #t0=d*Y^4 + t1=t1*AE #t1=a*Z^4 + t0=t0+t1 #t0=d*Y^4+a*Z^4 + Z2=t0-t2 # + Y2=Y2-t0 #cost: 5S+4M + + return Y2,Z2 + +###################################################### + +#Edwards-version of xDBLe +def edDBLe(XP,ZP,A,C,e): + + C2=C+C + AE=A+C2 + DE=A-C2 + + YeP = XP-ZP + ZeP = ZP+XP + + for i in range(0,e): + YeP, ZeP = edDBL(YeP, ZeP, AE, DE) + + XeP=YeP+ZeP + ZeP=ZeP-YeP + return XeP, ZeP #cost:4eM+5eS + +############################################################### + + +#function for step in Montgomery ladder +#simultaneous doubling and differential addition +def xDBLADD(XP,ZP,XQ,ZQ,xPQ,A24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + t0 = XP + ZP #code from msr + t1 = XP - ZP + X2P = t0**2 + t2 = XQ - ZQ + XQP = XQ + ZQ + t0 = t0 * t2 + Z2P = t1**2 + t1 = t1 * XQP + t2 = X2P - Z2P + X2P = X2P * Z2P + XQP = A24 * t2 + ZQP = t0 - t1 + Z2P = XQP + Z2P + XQP = t0 + t1 + Z2P = Z2P * t2 + ZQP = ZQP**2 + XQP = XQP**2 + ZQP = xPQ * ZQP + + return X2P, Z2P, XQP, ZQP #cost: 6M+4S+8a + + +################################################################ + + +#function for computing [2^e](X:Z) via repeated doublings +def xDBLe(XP,ZP,A,C,e): + #input: projective coordinates xP=XP/ZP + # curve constant A/C + #output: projective coordinates of x(2^e*P)=XeP/ZeP + A24num = C + C #code from msr + A24den = A24num + A24num + A24num = A24num + A + + XeP = XP + ZeP = ZP + + for i in range(0,e): + XeP, ZeP = xDBL(XeP, ZeP, A24num, A24den) + + return XeP, ZeP #cost:4eM+2eS+(4e+3)a + + +#################################################################### + +#edwards-montgomery step in basefield +def xDBLADD_basefield(XP,ZP,XQ,ZQ,xPQ,A24,C24): + #input: projective coordinates xP=XP/ZP and xQ=XQ/ZQ, + # affine difference x(P-Q)=xPQ and + # curve constant A24=(A+2)/4. + #output: projective coordinates of x(2P)=X2P/Z2P + # and x(Q+P)=XQP/ZQP + # function assumes A24=1, C24=2 fixed + + t0 = XP + ZP #Montgomery-addition + t1 = XP - ZP + XP = XQ - ZQ + ZP = XQ + ZQ + t2 = (XP * t0)%p + t3 = (ZP * t1)%p + ZP = t2 - t3 + XP = t2 + t3 + ZP = (ZP**2)%p + XQP = (XP**2)%p + ZQP = (xPQ * ZP)%p + + t1=(t1**2)%p #Y^2 #edwards doubling + t0=(t0**2)%p #Z^2 + t2=t0+t1 #+ + t2=(t2**2)%p #y^4+z^4+2y^2z^2 + t0=(t0**2)%p #z^4 + t1=(t1**2)%p #y^4 + t2=t2-t1 + X2P=(t2-t0)%p #2y^2z^2 + Z2P=(t0-t1)%p + + return X2P, Z2P, XQP, ZQP #cost: 3M+7S+8a + + +#################################################################### + + +#triple point in Edwards-coordinates +def xTPL(X, Z, AE, DE): + #input: projective x-coordinates of xP=X/Z + # curve constant A/C + #output: projective x-coordinates of x(3P)=X3/Z3 + + Ye = X-Z #transformation to Edwards + Ze = Z+X + + Ye, Ze = edDBL(Ye, Ze, AE, DE) #Edwards doubling + + t0 = Ze + Ze #Montgomery addition + t1 = Ye + Ye + XP = X - Z + ZP = X + Z + t0 = XP * t0 + t1 = ZP * t1 + ZP = t0 - t1 + XP = t0 + t1 + ZP = ZP**2 + X3 = XP**2 + Z3 = X * ZP + X3 = X3 * Z #cost:7S+8M + + + + return X3, Z3 + + +################################################################# + +#triple point e times -> [3^e](X:Z) +def xTPLe(X, Z, A, C, e): + #input: projective x-coordinates (X:Z) of P + # curve constant A/C + #output: projective x-coordinates of x([3^e]P)=Xep/ZeP + + XeP = X + ZeP = Z + + C2=C+C #Edwards coefficients + AE=A+C2 + DE=A-C2 + + for i in range (0, e): + XeP, ZeP = xTPL(XeP, ZeP, AE, DE) + + return XeP, ZeP #cost:8eM+4eS+(8e+3)a + + +###################################################################### + +#montgomery-ladder +def LADDER(x, m, A24, C24, AliceorBob): + #input: affine x-coordinate of a point on E: B*y^2=x^3+A*x^2+x, + # scalar m, curve constant (A+2)/4 + #output: projective coordinates x(mP)=X0/Z0 and x((m+1)P)=X1/Z1 + # function assumes A24=1, C24=2 fixed + #if A24 != 1: + # print('wrong A24-value') + #if C24 != 2: + # print('wrong C24-value') + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24, C24 = 1, 2 + X0, Z0 = 1, 0 #initialization with infinity + X1, Z1 = x, 1 + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + X0, Z0, X1, Z1 = xDBLADD_basefield(X0, Z0, X1, Z1, x, A24, C24) + else: + X1, Z1, X0, Z0 = xDBLADD_basefield(X1, Z1, X0, Z0, x, A24, C24) + + return X0, Z0, X1, Z1 #cost:5nM+4nS+9na + + +######################################################################### + +#function for computing P+[m]Q +def secret_pt(x, y, m, AliceorBob): + #input: point P=(x,y) in base field subgroup, Q=(-x,iy), scalar m + #output: RX0, RX1, RZ in Fp with (RX0+iRX1)/RZ is x-coordinate of P+[m]Q + + A24, C24 = 1, 2 + X0, Z0, X1, Z1 = LADDER(-x, m, A24, C24, AliceorBob) + + RZ = (x * Z0)%p + RX0 = (X0 * x)%p + t4 = (X0 + RZ)%p + RZ = (X0 - RZ)%p + t0 = (t4**2)%p + RX0 = Z0 - RX0 + t0 = (t0 * X1)%p + RX0 = (RX0 * RZ)%p + t2 = (y * Z1)%p + t1 = (y * Z0)%p + t2 = t2 + t2 + RX1 = (t2 * Z0)%p + RX0 = (RX0 * Z1)%p + RX0 = RX0 - t0 + t1 = (t1 * RX1)%p + t0 = (RX1**2)%p + t2 = (t2 * RX1)%p + RX1 = (t1 * RX0)%p + t3 = t1 + RX0 + RX1 = RX1 + RX1 + t1 = t1 - RX0 + t1 = (t1 * t3)%p + RZ = (RZ**2)%p + t2 = (t2 * t4)%p + t2 = (t2 * RZ)%p + RZ = (t0 * RZ)%p + RX0 = t1 - t2 + RX0 = RX0 % p + RX1 = RX1 % p + return RX0, RX1, RZ #cost: 15M+3S+9a + + +##################################################################### + + +#three-point-ladder by de feo et al. calculates P+[m]Q +def LADDER_3_pt(m, xP, xQ, xPQ, A, AliceorBob): + #input: affine x-coordinates xP, xQ, xPQ + # curve constant A, scalar m + #output: projectove x.coordinate x(P+[m]Q)=WX/WZ + + binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + bits = binary(m) + + A24 = A + Complex(2) + A24 = A24 * inv4 + + UX = Complex(1) + UZ = Complex(0) #initialization with infinity + VX = xQ + VZ = Complex(1) + WX = xP + WZ = Complex(1) + + if AliceorBob == 'Alice': + nbits = eAbits + else: + nbits = eBbits + + #for i in range(0, nbits - len(bits)): + # WX, WZ = xADD(UX, UZ, WX, WZ, xP) + # UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + + for i in range(len(bits)-1, -1, -1): + if bits[i] == 0: + WX, WZ = xADD(UX, UZ, WX, WZ, xP) + UX, UZ, VX, VZ = xDBLADD(UX, UZ, VX, VZ, xQ, A24) + else: + UX, UZ = xADD(UX, UZ, VX, VZ, xQ) + VX, VZ, WX, WZ = xDBLADD(VX, VZ, WX, WZ, xPQ, A24) + + return WX, WZ #cost:9nM+6nS+(14n+3)a + + +##################################################################### + +#calculate 4-isogenies +def get_4_isog(X4, Z4): + #input: projective point of order four (X4:Z4) + #output: 4-isog curve with projective coefficient A/C and + # 5 coefficients for evaluating + + coeff0 = X4 + Z4 + coeff3 = X4**2 + coeff4 = Z4**2 + coeff0 = coeff0**2 + coeff1 = coeff3 + coeff4 + coeff2 = coeff3 - coeff4 + coeff3 = coeff3**2 + coeff4 = coeff4**2 + A = coeff3 + coeff3 + coeff0 = coeff0 - coeff1 + A = A - coeff4 + C = coeff4 + A = A + A + + return A, C, [coeff0, coeff1, coeff2, coeff3, coeff4] #cost:5S+7a + + +###################################################################### + + +#evaluate 4-isogenies: given coefficients from get_4_isog, evaluate at point in domain +def eval_4_isog(coeff, X, Z): + #input: coefficients from get_4_isog + # projective point P=(X:Z) + #output: projective point phi(P)=(X:Z) (overwritten since they replace inputs) + + X = coeff[0] * X + t0 = coeff[1] * Z + X = X - t0 + Z = coeff[2] * Z + t0 = X - Z + Z = X * Z + t0 = t0**2 + Z = Z + Z + Z = Z + Z + X = t0 + Z + Z = t0 * Z + Z = coeff[4] * Z + t0 = t0 * coeff[4] + t1 = X * coeff[3] + t0 = t0 - t1 + X = X * t0 + + return X, Z #cost:9M+1S+6a + + +###################################################################### + +#first 4-isogenie is different +def first_4_isog(X4, Z4, A): + #input: projective point (X4:Z4) and affine curve constant A (start parameter is affine) + #output: projective point (X:Z) in the codomain + # isogenous curve constant A/C (inputs overwritten) + + t0 = X4**2 + X = Z4**2 + Z = X4 * Z4 + X4 = A * Z + Z = Z + Z + Z4 = Z - X4 + t0 = t0 + X + X = t0 + Z + Z = t0 - Z + X4 = X4 + t0 + X = X * X4 + Z = Z4 * Z + C = Complex(A.re - 2,A.im) + An = Complex(0) + An.re = A.re + 6 + An.re = An.re + An.re + An.im = A.im + A.im + An = Complex(An.re , An.im) + + return X, Z, An, C #cost: 4M+2S+9a + + +#################################################################### + +#compute 3-isogenies +def get_3_isog(X3, Z3): + #input: projective point (X3:Z3) of order 3 + #ouput: coefficient A/C of 3-isog curve. no other coeff needed + + t0 = X3**2 + t1 = t0 + t0 + t0 = t0 + t1 + t1 = Z3**2 + A = t1**2 + t1 = t1 + t1 + C = t1 + t1 + t1 = t0 - t1 + t1 = t1 * t0 + A = A - t1 + A = A - t1 + A = A - t1 + t1 = X3 * Z3 + C = C * t1 + + return A, C #cost: 3M+3S+8a + + +#################################################################### + +#evaluate 3-isogenies +def eval_3_isog(X3, Z3, X, Z): + #input: projective point (X3:Z3) of order 3 + # projective x coordinate x(P)=X/Z + #output: projective x-coordinate of phi(X:Z) + t0 = X3 * X + t1 = Z3 * X + t2 = Z3 * Z + t0 = t0 - t2 + t2 = Z * X3 + t1 = t1 - t2 + t0 = t0**2 + t1 = t1**2 + X = X * t0 + Z = Z * t1 + + return X, Z #cost: 6M+2S+2a + + +###################################################################### + + +#with affine x-coordinate of P, return projective x-coordinates of Q-P where +#Q=tau(P) is image of the distortion map + +def distort_and_diff(xP): + #input: affine x-coordinate xP + #output: point (x(Q-P),z(Q-P)) with Q=tau(P) + + XD = (xP**2)%p + XD = XD + 1 + XD = Complex(0, XD) + ZD = (xP + xP)%p + ZD = Complex(ZD) + + return XD, ZD + + +###################################################################### + +#compute inverses of 3 elements +def inv_3_way(z1, z2, z3): + #input: 3 values z1, z2, z3 + #output: their inverses, inputs overwritten + + t0 = z1 * z2 + t1 = t0 * z3 + t1 = inv(t1) + t2 = z3 * t1 + t3 = t2 * z2 + z2 = t2 * z1 + z3 = t0 * t1 + z1 = t3 + + return z1, z2, z3 #cost: 6M+1I + + +####################################################################### + +#calculate A from x-coordinates of P, Q, R, such that R=Q-P is on the +#montgomery-curve E_A: y^2=x^3+A*x^2+x +def get_A(xP, xQ, xR): + #input: x-coordinates xP, xQ, xR + #output: coefficient A + + t1 = xP + xQ + t0 = xP * xQ + A = xR * t1 + A = A + t0 + t0 = t0 * xR + A = A - Complex(1) + t0 = t0 + t0 + t1 = t1 + xR + t0 = t0 + t0 + A = A**2 + t0 = inv(t0) + A = A * t0 + A = A - t1 + + return A #cost: 4M+1S+7a+1I +########################################################################## + +#caluclate the inverse of an element +def inv(z): + re = z.re + im = z.im + den = re**2 + t0 = im**2 + den = den + t0 + den = int(den) + den = pow(den, p-2, p) + re = re * den + im = im * den + z = Complex(re, -im) + + return z + + +###################################################################### + + +def keygen_Bob(SK_Bob, params, splits, MAX): + #input: secret random number in (1,oB-1) + # public parameters [XPA, XPB, YPB] + #output: public key [phi_B(x(PA)),phi_B(x(QA)),phi_B(x(QA-PA))] + + A, C = Complex(0), Complex(1) #starting montgomery curve + phiPX = params[0] #Bob's starting points -> public key + phiPZ = Complex(1) + phiQX = Complex(-phiPX) + phiQZ = Complex(1) + + phiDX, phiDZ = distort_and_diff(phiPX) #(phiDX:phiDZ)=x(Q-P) + phiPX = Complex(phiPX) + + #compute the point x(R)=(RX:RZ) via secret_pt, R=P+[SK_Alice]Q + RX0, RX1, RZ = secret_pt(params[1], params[2], SK_Bob, 'Bob') + RX = Complex(RX0, RX1) + RZ = Complex(RZ) + + #counters + iso, mul = 0, 0 + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #evaluate 3-isogeny at Alice's points + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + phiPX, phiPZ = eval_3_isog(RX, RZ, phiPX, phiPZ) #P=phi(P) + phiQX, phiQZ = eval_3_isog(RX, RZ, phiQX, phiQZ) #Q=phi(Q) + phiDX, phiDZ = eval_3_isog(RX, RZ, phiDX, phiDZ) #D=phi(D) + iso = iso + 3 + + #compute affine x-coordinates + phiPZ, phiQZ, phiDZ = inv_3_way(phiPZ, phiQZ, phiDZ) + phiPX = phiPX * phiPZ + phiQX = phiQX * phiQZ + phiDX = phiDX * phiDZ + + #Bob's public key, values in Fp2 + PK_Bob = [phiPX, phiQX, phiDX] + + msg="Bob's keygen needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + print('') + keysize = len(binary(phiPX.re)) + len(binary(phiPX.im)) + len(binary(phiQX.re)) + len(binary(phiQX.im))+ len(binary(phiDX.re)) + len(binary(phiDX.im)) + msg="Keysize of Bob's public key: " + str(keysize) + " bits" + print(msg) + + return PK_Bob + + +###################################################################### + +def shared_secret_Bob(SK_Bob, PK_Alice, splits, MAX): + #input: Bob's secret key SK_Alice + # Alices's public key + #output: Bob's shared secret: j-invariant of E_BA + + A = get_A(PK_Alice[0], PK_Alice[1], PK_Alice[2]) + C = Complex(1) #start on Alice's curve + + #compute R=phi_A(xPB)+SK_Bob*phi_A(xQB) + RX, RZ = LADDER_3_pt(SK_Bob, PK_Alice[0], PK_Alice[1], PK_Alice[2], A, 'Bob') + iso, mul = 0, 0 #counters + + pts = [] + index = 0 + + #Bob's main loop + for row in range(1, MAX): + + #multiply (RX:RZ) until it has order 3. store intermediate pts + while index < (MAX - row): + pts.append([RX, RZ, index]) + m = splits[MAX-index-row] + RX, RZ = xTPLe(RX, RZ, A, C, m) + mul = mul + m + index = index + m + + #compute isogeny + A, C = get_3_isog(RX, RZ) + + #evaluate 3-isogeny at every point in pts + for i in range(0, len(pts)): + pts[i][0], pts[i][1] = eval_3_isog(RX, RZ, pts[i][0], pts[i][1]) + iso = iso + 1 + + #R becomes last entry of pts, last entry is removed from pts + RX = pts[len(pts)-1][0] + RZ = pts[len(pts)-1][1] + index = pts[len(pts)-1][2] + del pts[-1] + + #compute last isogeny + A, C = get_3_isog(RX, RZ) + + secret_Bob = j_inv(A, C) + + msg="Bob's secret needs "+str(mul)+" multiplications by 3 and "+str(iso)+" isogenies" + print(msg) + + return secret_Bob + +###################################################################### + +#parameters defining prime p=lA^eA*lB^e_B*f-1 +lA=2 +lB=3 +eA=372 +eB=239 +#eA=15 +#eB=8 +f=1 + +binary = lambda n: n>0 and [n&1]+binary(n>>1) or [] + +eAbits = len(binary(lA**eA)) +eBbits = len(binary(lB**eB)) + +#defining p (must be prime!) +p=(lA**eA)*(lB**eB)*f-1 + +#inverse of 4, needed for 3-pt-ladder +inv4 = inv(Complex(4)) + +#values for eA=372, eB=239 +XPA = 5784307033157574162391672474522522983832304511218905707704962058799572462719474192769980361922537187309960524475241186527300549088533941865412874661143122262830946833377212881592965099601886901183961091839303261748866970694633 +YPA = 5528941793184617364511452300962695084942165460078897881580666552736555418273496645894674314774001072353816966764689493098122556662755842001969781687909521301233517912821073526079191975713749455487083964491867894271185073160661 +XPB = 4359917396849101231053336763700300892915096700013704210194781457801412731643988367389870886884336453245156775454336249199185654250159051929975600857047173121187832546031604804277991148436536445770452624367894371450077315674371 +YPB = 106866937607440797536385002617766720826944674650271400721039514250889186719923133049487966730514682296643039694531052672873754128006844434636819566554364257913332237123293860767683395958817983684370065598726191088239028762772 + +#values for eA=8, eB=5 +#XPA = 4437 +#YPA = 55467 +#XPB = 24048 +#YPB = 57850 + +#values for eA=13, eB=7 +#XPA = 4698102 +#YPA = 1371375 +#XPB = 1258275 +#YPB = 10923545 + +#values for eA=15, eB=8 +#XPA = 144036251 +#YPA = 40988612 +#XPB = 4982149 +#YPB = 74338728 + +# params_Alice = [XPB, XPA, YPA] +params_Bob = [XPA, XPB, YPB] + +################################################################# + +splits_Bob = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10,\ +12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, 16, 16, 16, 16, 17, 16, 16, 17, 19,\ +19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 24, 24, 25, 27, 27, 28, 28,\ +29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, 30, 33, 33, 33, 33, 34, 35, 37, 37,\ +37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, 43, 38, 38, 38, 38, 43, 40, 41, 42,\ +43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 50, 49, 49, 49, 49, 51, 49, 53,\ +50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, 56, 56, 56, 56, 58, 58, 61, 61, 61,\ +63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, 65, 66, 66, 66, 66, 66, 66, 66, 71,\ +66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 68, 68, 71, 71, 73, 73, 73, 75,\ +75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, 84, 85, 86, 86, 86, 86, 86, 87, 86,\ +88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, 88, 88, 86, 86, 86, 93, 90, 90, 92,\ +92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, 97, 97 ] + +MAX_Bob = 239 + +####################################################################### + +#Decrypts received secret & nbit keys +def decrypting(key, filename): + chunksize = 64 * 1024 + outputFile = filename.split('.hacklab')[0] + + with open(filename, 'rb') as infile: + filesize = int(infile.read(16)) + IV = infile.read(16) + decryptor = AES.new(key, AES.MODE_CBC, IV) + + with open(outputFile, 'wb') as outfile: + while True: + chunk = infile.read(chunksize) + if len(chunk) == 0: + break + outfile.write(decryptor.decrypt(chunk)) + outfile.truncate(filesize) + + return outputFile + +def handshake(): + logger.info('Starting Key Exchange...\n') + + print() + logger.info('Key Gen found. Key exchange begins...\n') + + n_Bob= randint(0,lB**eB) + logger.info("Bob's secret key:") + logger.info(n_Bob) + print('') + + PKB = keygen_Bob(n_Bob, params_Bob, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's Public Key:") + logger.info('%s',(PKB[0])) + logger.info('%s',(PKB[1])) + logger.info('%s',(PKB[2])) + keyreal1 = PKB[0].re + keyimag1 = PKB[0].im + keyreal2 = PKB[1].re + keyimag2 = PKB[1].im + keyreal3 = PKB[2].re + keyimag3 = PKB[2].im + encoded = asn1_file.encode('DataPublicKey',{'keyreal1': keyreal1, 'keyimag1': keyimag1, 'keyreal2': keyreal2, 'keyimag2': keyimag2,'keyreal3': keyreal3, 'keyimag3': keyimag3}) + + print('') + print('') + logger.info('Data Sent %s %s %s', PKB[0], PKB[1], PKB[2]) + + #Sends Bob's encoded public key to Key Gen. + sock.sendall(encoded) + print() + + logger.info('Receiving Key Gens Public Key...\n') + + #Receives Key Gen's public key. + PKA_encoded = sock.recv(2048) + + PKA_decoded = asn1_file.decode('DataPublicKey', PKA_encoded) + #Retrieving Key Gen's public key in INT Form + keyreal1A = PKA_decoded.get('keyreal1') + keyimag1A = PKA_decoded.get('keyimag1') + keyreal2A = PKA_decoded.get('keyreal2') + keyimag2A = PKA_decoded.get('keyimag2') + keyreal3A = PKA_decoded.get('keyreal3') + keyimag3A = PKA_decoded.get('keyimag3') + + + #Forming Key Gen's public key into complex form for calculations + phiPX = Complex(keyreal1A, keyimag1A) + phiQX = Complex(keyreal2A, keyimag2A) + phiDX = Complex(keyreal3A, keyimag3A) + + PKA = [phiPX, phiQX, phiDX] + + logger.info('Public Key Received: ') + logger.info(PKA[0]) + logger.info(PKA[1]) + logger.info(PKA[2]) + + print() + logger.info('Computing shared secret...\n') + + #Calculates shared secret based off received public key + + SKB = shared_secret_Bob(n_Bob, PKA, splits_Bob, MAX_Bob) + print('') + logger.info("Bob's shared secret:") + logger.info(SKB) + print('') + + #Hashing Shared Secret + SKB_ComplexToString = secretKeyEncoder().encode(SKB) + SKB_StringToBytes = SKB_ComplexToString.encode() + #SK = Skeleton Key + SK = hashlib.sha256(SKB_StringToBytes).digest() + + #Open the received secret file from the key generator + with open('secret.key.hacklab', 'wb') as s, open('nbit.key.hacklab', 'wb') as t: + print ('File opened...\n') + while True: + # print ('Receiving data...\n') + secret_key_BER = sock.recv(16396, socket.MSG_WAITALL) + if (len(secret_key_BER) > 10): + keys_decoded = asn1_file.decode('DataKey', secret_key_BER) + secret_key = keys_decoded.get('key') + nbit_key = keys_decoded.get('nbit') + else: + break + if not (secret_key or nbit_key): + break + s.write(secret_key) + t.write(nbit_key) + + s.close() + t.close() + + print ('Successfully got the files\n') + + print ('Encrypted secret file size: ', os.path.getsize('secret.key.hacklab')) + print ('Encrypted nbit file size: ', os.path.getsize('nbit.key.hacklab')) + + print ('Decrypting the files...\n') + + decrypted_secret_key = decrypting(SK, 'secret.key.hacklab') + print('Acquired original secret key file size: ', os.path.getsize(decrypted_secret_key)) + os.system("md5sum secret.key") + + decrypted_nbit_key = decrypting(SK, 'nbit.key.hacklab') + print('Acquired original nbit key file size: ', os.path.getsize(decrypted_nbit_key)) + os.system("md5sum nbit.key") + +####################################################################### + +if __name__ == '__main__': +handshake() +sock.close() diff --git a/declaration.asn b/declaration.asn new file mode 100644 index 0000000..2c22c17 --- /dev/null +++ b/declaration.asn @@ -0,0 +1,57 @@ +TEST DEFINITIONS ::= BEGIN + DataMd5 ::= SEQUENCE{ + data IA5String + } + + DataDragonflyVerif ::= SEQUENCE{ + code INTEGER + } + + DataIntiate ::= SEQUENCE{ + code INTEGER + } + + DataMac ::= SEQUENCE { + data IA5String + } + + DataKey ::= SEQUENCE { + key OCTET STRING, + nbit OCTET STRING + } + + DataPublicKey ::= SEQUENCE { + keyreal1 INTEGER, + keyimag1 INTEGER, + keyreal2 INTEGER, + keyimag2 INTEGER, + keyreal3 INTEGER, + keyimag3 INTEGER + } + + DataSharedKey ::= SEQUENCE { + sharedKeyReal INTEGER, + sharedKeyImag INTEGER + } + + DataFsize ::= SEQUENCE { + data INTEGER + } + + DataContent ::= SEQUENCE{ + data OCTET STRING + } + + DataIndicator ::= SEQUENCE { + data IA5String + } + + DataAnsSize ::= SEQUENCE { + data INTEGER + } + + DataAnswer ::= SEQUENCE { + data OCTET STRING + } + +END