def invertGalois2(toInv: object): """ Invert given {array of bits, bytes, int} in GF() ! You need to initialize the Galois_Field before ! ! You need to have a dictionary file available ! Output: bytes """ d = int(config.DEGREE / 8) toInv = bm.mult_to_bytes(toInv) if config.GALOIS_WATCH or config.IN_CREATION: config.WATCH_INVERSION_NUMBER += 1 exTime = time.time() # A(x) . A(x)^-1 congruent to 1 mod P(x) # where P(x) irreductible polynomial of given degree # A ^ p^n - 2 = inverted inv = poly_exp_mod_2(bm.bytes_to_int(toInv), config.NBR_ELEMENTS - 2, config.IRRED_POLYNOMIAL) inv = inv.to_bytes(bm.bytes_needed(inv), "big") config.WATCH_GLOBAL_INVERSION += time.time() - exTime else: inv = config.INVERSIONS_BOX[bm.bytes_to_int(toInv)] return bm.zfill_b(inv, d)
def GHASH64(H, A, C, X, i): n = len(C) m = len(A) if i <= m: # A1 = A[1-1] return gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, A[i - 1], "XOR")), H, p) if i <= m + n: return gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, C[i - m - 1], "XOR")), H, p) if i == m + n + 1: return gz2.poly_mult_mod_2( a=bm.bytes_to_int( bm.b_op( b1=gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, lenb(A), "XOR")), H, p).to_bytes(8, "big"), b2=lenb(C), ope="XOR", )), b=H, mod=p, )
def encrypt(M: bytes, publicKey, saving: bool = False): """ Encrypt a message M to make him sendable. """ assert isinstance(M, bytes) n, e = publicKey def process(m): return ut.square_and_multiply(m, e, n) # First, turn M into int Mint = bm.bytes_to_int(M) if Mint < n: # That's a short message m = Mint e = process(m) else: # M is a longer message, so it's divided into blocks size = (it.getKeySize(publicKey) // 8) - 1 e = [process(bm.bytes_to_int(elt)) for elt in bm.splitBytes(M, size)] if saving: e = it.writeKeytoFile(e, "encrypted", config.DIRECTORY_PROCESSING, ".kat") return e
def IV(arr, key="y/B?E(H+MbQeThVm".encode()): """ The IV must, in addition to being unique, be unpredictable at encryption time. Return a 8 bytes array. """ # Select a random encrypted message as initial vector to transform. import secrets as sr r1 = sr.randbelow(len(arr)) r2 = sr.randbits(8) message = bm.bytes_to_int(arr[r1]) ^ r2 message = bm.mult_to_bytes(message) # Let's use the principle of hmac # The basic idea is to concatenate the key and the message, and hash them together. # https://pymotw.com/3/hmac/ import hmac import hashlib # Default algorithm for hmac is MD5, it's not the most secure # so let's use SHA-1 digest_maker = hmac.new(key, message, hashlib.sha1) digest = digest_maker.hexdigest() return bytearray(bytes.fromhex(digest)[:8])
def verifying(M: bytes, sign: int, pK: tuple = None): """ Verify given signature of message M with corresponding public key's. """ assert isinstance(M, (bytes, bytearray)) from ..hashbased import hashFunctions as hashF if not pK: pK = it.extractKeyFromFile("public_key") size = it.getKeySize(pK) hm = hashF.sponge(M, size) # base64 to int hm = bm.bytes_to_int(bm.mult_to_bytes(hm)) # If the signature is in base64 if not isinstance(sign, int): sign = it.getIntKey(sign) n, e = pK # raises the signature to the power of e (modulo n) # (as when encrypting a message) if sign > n: print("Signature > modulus") test = ut.square_and_multiply(sign, e, n) if test == (hm % n): return True return False
def signing(M: bytes, privateK: tuple = None, saving: bool = False, Verbose: bool = False): """ Signing the message (M). You need to attach this signature to the message. """ assert isinstance(M, bytes) from ..hashbased import hashFunctions as hashF if not privateK: privateK = it.extractKeyFromFile("private_key") size = it.getKeySize(privateK) # Get key size if Verbose: print("Hashing in progress...") hm = hashF.sponge(M, size) # base64 to int hm = bm.bytes_to_int(bm.mult_to_bytes(hm)) if Verbose: print(f"hm = {hm}") print("Hashing done.\n") # raises it to the power of d (modulo n) # same thing as decrypting n, d = privateK sign = ut.square_and_multiply(hm, d, n) if saving: sign = it.writeKeytoFile(sign, "RSA_signature") return sign
def verifying(M: bytes, sign: tuple, publicKey: tuple = None): """ Verify given signature of message M with corresponding public key's. """ assert isinstance(M, (bytes, bytearray)) from ..hashbased import hashFunctions as hashF if not publicKey: publicKey = it.extractKeyFromFile("public_key") p, g, h = publicKey size = it.getKeySize(publicKey) hm = hashF.sponge(M, size) hm = bm.bytes_to_int(bm.mult_to_bytes(hm)) if not isinstance(sign, tuple): b64data = sign sign = it.getIntKey(b64data[1:], b64data[0]) s1, s2 = sign if (0 < s1 < p) and (0 < s2 < p - 1): test1 = (ut.square_and_multiply(h, s1, p) * ut.square_and_multiply(s1, s2, p)) % p test2 = ut.square_and_multiply(g, hm, p) if test1 == test2: return True return False raise ValueError
def signing(M: bytes, privateK: tuple = None, saving: bool = False, Verbose: bool = False): """ Signing a message M (bytes). """ from ..hashbased import hashFunctions as hashF # y choosed randomly between 1 and p-2 with condition than y coprime to p-1 if not privateK: privateK = it.extractKeyFromFile("private_key") p, g, x = privateK size = it.getKeySize(privateK) # M = bm.fileToBytes(M) # M = "Blablabla".encode() if Verbose: print("Hashing in progress...") hm = hashF.sponge(M, size) # #base64 to int hm = bm.bytes_to_int(bm.mult_to_bytes(hm)) if Verbose: print("Hashing done.\n") p1 = p - 1 k = rd.randrange(2, p - 2) while not ut.coprime(k, p1): k = rd.randrange(2, p - 2) if Verbose: print(f"Your secret integer is: {k}") s1 = ut.square_and_multiply(g, k, p) s2 = (multGroup.inv(k, p1) * (hm - x * s1)) % p1 # In the unlikely event that s2 = 0 start again with a different random k. if s2 == 0: if Verbose: print("Unlikely, s2 is equal to 0. Restart signing...") signing(M, privateK, saving, Verbose) else: sign = (s1, s2) if saving: sign = it.writeKeytoFile(sign, "elG_signature") return sign
def pad(N, r): iN = bm.bytes_to_int(N) lN = int.bit_length(iN) # Number of 0 to add b = (r - ((lN + 3) % r)) % r # Padding using the SHA-3 pattern 10*1: a 1 bit, followed by zero or more 0 bits (maximum r − 1) and a final 1 bit. op = ((iN | (1 << b + lN + 1)) << 1) ^ 1 return bm.mult_to_bytes(op)
def xorshiftperso(evenOrodd: int = 0, nBits: int = 512): """ Personal implementation of a random number generator using XORshift nBits: number of bits needed (e.g 2048 bits to output 2048 bits lenght's number). return even or odd number: - 0 odd - 1 even - both """ assert nBits > 23 bytesT = int(nBits / 8) # Unpredictable random seed state1, state2 = os.urandom(bytesT), os.urandom(bytesT) a, b = bm.bytes_to_int(state1), bm.bytes_to_int(state2) a ^= bm.bytes_to_int(bm.circularRotation(state1, 0, 23)) b ^= bm.bytes_to_int(bm.circularRotation(state2, 1, 17)) # Generate full bytes of 1 of the size of the array size = int("0x" + "".join(["FF" for _ in range(0, nBits)]), 16) # For n bits a &= size b &= size r = b + a if evenOrodd and r & 1: # Bitwsing and with "-2" (number with all bits set to one except lowest one) # always kills just the LSB of your number and forces it to be even r &= -2 elif not evenOrodd and not (r & 1): # Force the number to the next odd r |= 1 return r
def encrypt(M: bytes, publicKey, saving: bool = False): assert isinstance(M, (bytes, bytearray)) p, g, h = publicKey def process(m): y = rd.randrange(1, p - 1) # shared secret -> g^xy c1 = ut.square_and_multiply(g, y, p) s = ut.square_and_multiply(h, y, p) c2 = (m * s) % p return (c1, c2) Mint = bm.bytes_to_int(M) if Mint < p: # That's a short message m = Mint e = process(m) else: # M is a longer message, so it's divided into blocks # You need to choose a different y for each block to prevent # from Eve's attacks. size = (it.getKeySize(publicKey) // 8) - 1 e = [process(bm.bytes_to_int(elt)) for elt in bm.splitBytes(M, size)] if saving: e = it.writeKeytoFile(e, "encrypted", config.DIRECTORY_PROCESSING, ".kat") return e
def nullBits(H, nbZ): """ Chech if the given hash ends with nBz null bits H -> bytearray of the hash nbZ -> number of null bits """ H = bm.bytes_to_int(H) for i in range(0, nbZ): if ((H >> i) & 1) == 1: return False return True
def getIntKey(data: bytes, keyNumber: int = 1): """ Convert base64 key's into tuples of keyNumber integers. """ assert isinstance(data, (bytes, bytearray)) if isinstance(keyNumber, str): keyNumber = int(keyNumber) if keyNumber != 1: keys = () kL = [] for i in range(keyNumber): kL.append(int.from_bytes(data[i * 2:i * 2 + 2], "big")) padding = keyNumber * 2 for i, s in enumerate(kL): keys += (int.from_bytes(data[padding:padding + s], "big"), ) padding = padding + s else: keys = bm.bytes_to_int(data) return keys
def md5(block): """ Return md5 hash block: bytearray of data to hash """ import math # Done using the Wikipedia algorithm def iToB(i): return int.to_bytes(i, 4, "little") def p32(a, b): return (a + b) % (1 << 32) s = [ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, ] K = [] for i in range(64): K.append((math.floor(2 ** 32 * abs(math.sin(i + 1)))) % (1 << 32)) iN = bm.bytes_to_int(block) lN = len(block) * 8 # Number of 0 to add b = 512 - ((lN + 1) % 512) lN = int.from_bytes(lN.to_bytes(8, byteorder="little"), byteorder="big", signed=False) iN = (((iN << 1) | 1) << b) ^ lN block = bm.mult_to_bytes(iN) b512 = bm.splitBytes(block, 64) h1 = 0x67452301 h2 = 0xEFCDAB89 h3 = 0x98BADCFE h4 = 0x10325476 for b5 in b512: blocks = bm.splitBytes(b5, 4) A = h1 B = h2 C = h3 D = h4 for i in range(64): if i <= 15: F = (B & C) | (~B & D) g = i elif i <= 31: F = (D & B) | (~D & C) g = (5 * i + 1) % 16 elif i <= 47: F = B ^ C ^ D g = (3 * i + 5) % 16 else: # C xor (B or (not D)) F = C ^ (B | ~D) % (1 << 32) g = (7 * i) % 16 # F + A + K[i] + M[g] try: F = p32(p32(p32(F, A), K[i]), int.from_bytes(blocks[g], "little")) except IndexError: print(i, K, blocks[g]) raise Exception("Error") A = D D = C C = B # B + leftrotate(F, s[i]) B = p32(B, ((F << s[i]) | (F >> (32 - s[i])))) h1 = p32(A, h1) h2 = p32(B, h2) h3 = p32(C, h3) h4 = p32(D, h4) return bm.packSplittedBytes([iToB(h1), iToB(h2), iToB(h3), iToB(h4)])
def GCM(arr, encrypt=True, aad=""): """ GCM is CTR mode with authentification of additional data (AAD) authenticated with multiplication in a Galois Field arr: array of bytearray of 8 bytes of data to encrypt/decrypt encrypt: boolean, true to encypt aad: string of additional authenticated data """ if encrypt: iv = IV(arr) else: iv = IV_action(arr) # Integrity Check Balue icv = IV_action(arr) # Additional authenticated data (AAD), which is denoted as A A = [] if encrypt: if aad != "": aadc = aad.encode() if len(aadc) > 1 << 64: raise Exception("Too much AAD") A = bm.splitBytes(aadc, 8) A[-1] = bm.zfill_b(A[-1], 8) else: header = arr[0] epos = int.from_bytes(header, "big") A = arr[1:epos] arr = arr[epos:] # Encrypted message C = [] # 1 + α + α3 + α4 + α64 - 64 field polynomial p = int( "10000000000000000000000000000000000000000000000000000000000001111", 2) def lenb(i): return (len(i) * 8).to_bytes(8, "big") def GHASH64(H, A, C, X, i): n = len(C) m = len(A) if i <= m: # A1 = A[1-1] return gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, A[i - 1], "XOR")), H, p) if i <= m + n: return gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, C[i - m - 1], "XOR")), H, p) if i == m + n + 1: return gz2.poly_mult_mod_2( a=bm.bytes_to_int( bm.b_op( b1=gz2.poly_mult_mod_2( bm.bytes_to_int(bm.b_op(X, lenb(A), "XOR")), H, p).to_bytes(8, "big"), b2=lenb(C), ope="XOR", )), b=H, mod=p, ) H = bm.bytes_to_int(kasu.kasumi(b"\x00" * 8)) Y = GHASH64(H, b"", [iv], b"\x00", 1).to_bytes(8, "big") E0 = kasu.kasumi(Y) n = len(arr) m = len(A) # equivalent of CTR mode for i in range(n): config.WATCH_PERCENTAGE = (((n * 2 + m + 1) - ((n * 2 + m + 1) - i)) / (n * 2 + m + 1)) * 100 exTime = time.time() # treats the rightmost 32bits of its argument as a nonnegative integer with the least significant bit on the right, and increments this value modulo 2^32 Y = Y[:4] + ( (int.from_bytes(Y[-4:], "big") + 1) % 1 << 32).to_bytes(4, "big") E = kasu.kasumi(Y) C.append(bm.b_op(arr[i], E, "XOR")) config.WATCH_GLOBAL_CIPHER += time.time() - exTime config.WATCH_BLOC_CIPHER = config.WATCH_GLOBAL_CIPHER / (i + 1) config.WATCH_BLOC_KASUMI = config.WATCH_GLOBAL_KASUMI / (i + 1) res = C # plaintext is in C when we decrypt, me must replace it with the ciphertext if not encrypt: C = arr # first init of X = GHASH64(i=0) = b'\x00' X = b"\x00" for i in range(n + m + 1): config.WATCH_PERCENTAGE = (((n * 2 + m + 1) - ((n * 2 + m + 1) - (i + n))) / (n * 2 + m + 1)) * 100 exTime = time.time() X = GHASH64(H, A, C, X, i + 1).to_bytes(8, "big") config.WATCH_GLOBAL_CIPHER += time.time() - exTime config.WATCH_BLOC_CIPHER = config.WATCH_GLOBAL_CIPHER / (i + 1) icvc = bm.b_op(E0, X, "XOR") if not encrypt: if icv != icvc: print( "\nYELLOW: INTEGRITY CHECK CONTROL INCORRECT, AAD HAVE BEEN MODIFIED !!" ) if encrypt: IV_action(res, icvc, "store") # Adding the IV to the encrypted data IV_action(res, iv, "store") header = (1 + len(A)).to_bytes(8, "big") res = [header] + A + res return res