def digest(self): """Return the **binary** (non-printable) MAC tag of the message that has been authenticated so far. :return: The MAC tag, computed over the data processed so far. Binary form. :rtype: byte string """ if self._mac_tag is not None: return self._mac_tag[:self._mac_len] if self._data_size > self._max_size: raise ValueError("MAC is unsafe for this message") if len(self._cache) == 0 and self._before_last_ct is not None: # Last block was full pt = strxor(strxor(self._before_last_ct, self._k1), self._last_pt) else: # Last block is partial (or message length is zero) ext = self._cache + b'\x80' +\ b'\x00' * (self.digest_size - len(self._cache) - 1) pt = strxor(strxor(self._last_ct, self._k2), ext) cipher = self._factory.new(self._key, self._factory.MODE_ECB, **self._cipher_params) self._mac_tag = cipher.encrypt(pt) return self._mac_tag[:self._mac_len]
def digest(self): """Return the **binary** (non-printable) MAC of the message that has been authenticated so far. This method does not change the state of the MAC object. You can continue updating the object after calling this function. :Return: A byte string of `digest_size` bytes. It may contain non-ASCII characters, including null bytes. """ if self._mac_tag is not None: return self._mac_tag if self._data_size > self._max_size: raise ValueError("MAC is unsafe for this message") if len(self._cache) == 0 and self._before_last_ct is not None: ## Last block was full pt = strxor(strxor(self._before_last_ct, self._k1), self._last_pt) else: ## Last block is partial (or message length is zero) ext = self._cache + bchr(0x80) +\ bchr(0) * (self.digest_size - len(self._cache) - 1) pt = strxor(strxor(self._last_ct, self._k2), ext) cipher = self._factory.new(self._key, self._factory.MODE_ECB, **self._cipher_params) self._mac_tag = cipher.encrypt(pt) return self._mac_tag
def digest(self): """Return the **binary** (non-printable) MAC tag of the message that has been authenticated so far. :return: The MAC tag, computed over the data processed so far. Binary form. :rtype: byte string """ bs = self._block_size if self._mac_tag is not None and not self._update_after_digest: return self._mac_tag if self._data_size > self._max_size: raise ValueError("MAC is unsafe for this message") if self._cache_n == 0 and self._data_size > 0: # Last block was full pt = strxor(self._last_pt, self._k1) else: # Last block is partial (or message length is zero) partial = self._cache[:] partial[self._cache_n:] = b'\x80' + b'\x00' * (bs - self._cache_n - 1) pt = strxor(strxor(self._last_ct, partial), self._k2) self._mac_tag = self._ecb.encrypt(pt)[:self.digest_size] return self._mac_tag
def q1_dec_cbc_mode(key, ciphertext, iv, cipher=Sample_Cipher): """Question 1 (part 2): Implement CBC Mode **decryption** (with PKCS#7 padding, using the provided block cipher `cipher`) Your Task: The problem description is similar to the one in the previous problem, just note the different inputs and expected outputs Args: key (str): hex-encoded string (cipher.BLOCK_SIZE-bytes long) ciphertext (str): hex-encoded ciphertext (multiple cipher.BLOCK_SIZE-bytes long) iv (str): hex-encoded string of an IV that should be used for the CBC decryption (cipher.BLOCK_SIZE-bytes long) cipher (Cipher): Cipher class (check `sample_cipher.py`) that provides access to a sample block cipher. + cipher.decipher(key, ciphertext) Output: ret (str): ascii output string with an arbitrary length (with the padding removed) Test vectors: You can use the same test vectors from `q1_enc_cbc_mode` in the reverse order to double check your solution """ message = [] blocksToDecrypt = [ ciphertext[i:i + cipher.BLOCK_SIZE * 2] for i in range(0, len(ciphertext), cipher.BLOCK_SIZE * 2) ] prev_cipher = blocksToDecrypt[0] temp_value = cipher.decipher(key, prev_cipher) m1 = strxor(bytes.fromhex(iv), bytes.fromhex(temp_value)) message.append(m1.decode()) for byte in blocksToDecrypt[1:]: temp_value = cipher.decipher(key, byte) m1 = strxor(bytes.fromhex(temp_value), bytes.fromhex(prev_cipher)) message.append(m1.decode()) prev_cipher = byte message[len(message) - 1] = unpadd(message[len(message) - 1]) return ''.join(message)
def set_keys(self, keys): """ Initialisations des clés pour le chiffrement/déchiffrement, une clée doit être une suite de bytes de longeur 256, 512 ou 1024 bits :param keys: la clé pour le chiffrement :type keys: bytes""" # keys doit être du type bytes if isinstance(keys, bytes): # vérifie et sauvegarde la taille de keys self.set_block_size(len(keys) * 8) # on évalue N N = self.get_block_size() // 64 # ici notre N vaut 4, 8 ou 16 # on affecte les valeurs à K K = [] for i in range(N): # découpage de keys en N mots K.append(keys[(i * (64 // 8)):((i + 1) * (64 // 8))]) # on ajoute le dernier mot C = b'\x1b\xd1\x1b\xda\xa9\xfc\x1a\x22' K.append(C) for i in range(N): # on apllique le xor sur tout les mots K[N] = strxor(K[N], K[i]) # On calcul les tweaks tweak = self.get_tweak() t = [tweak[0:8], tweak[8:16], strxor(tweak[0:8], tweak[8:16])] # On détemine les sous-clés k k = [] for s in range(20): # s in [0;19] k.append([]) for i in range(N): # i in [0:N-1] step_key = K[(i + s) % (N + 1)] if i <= N - 4: # on prend directment une sous-clé sans modification ki = step_key elif i == N - 3: # on additionne avec un tweak int_tweak = int.from_bytes(t[s % 3], sys.byteorder) int_Key = int.from_bytes(step_key, sys.byteorder) ki = (int_Key + int_tweak) % pow(2, 64) # convert back to bytes ki = ki.to_bytes(64 // 8, sys.byteorder) elif i == N - 2: # même chose mais avec un autre tweak int_tweak = int.from_bytes(t[(s + 1) % 3], sys.byteorder) int_Key = int.from_bytes(step_key, sys.byteorder) ki = (int_Key + int_tweak) % pow(2, 64) # convert back to bytes ki = ki.to_bytes(64 // 8, sys.byteorder) elif i == N - 1: # on additionne directement avec s int_Key = int.from_bytes(step_key, sys.byteorder) ki = (int_Key + s) % pow(2, 64) # convert back to bytes ki = ki.to_bytes(64 // 8, sys.byteorder) else: raise OverflowError('i gets > N') k[s].append(ki) else: raise TypeError("keys must be bytes type") self.__keys = k self.__initial_key = keys
def __TOY_inv(self, ciphertext, key): assert(len(key) == self.BLOCK_SIZE) assert(len(ciphertext) % self.BLOCK_SIZE == 0) post_xor = strxor(ciphertext, key) sub_word = b''.join(self.inv_sub_word(word) for word in slice_into_block(post_xor, self.BLOCK_SIZE)) pre_xor = strxor(sub_word, key) return pre_xor
def __TOY(self, message, key): assert(len(key) == self.BLOCK_SIZE) assert(len(message) % self.BLOCK_SIZE == 0) pre_xor = strxor(message, key) sub_word = b''.join(self.sub_word(word) for word in slice_into_block(pre_xor, self.BLOCK_SIZE)) post_xor = strxor(sub_word, key) return post_xor
def encrypt(self, message): """Produce the PKCS#1 OAEP encryption of a message. This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in section 7.1.1 of RFC3447. :Parameters: message : byte string The message to encrypt, also known as plaintext. It can be of variable length, but not longer than the RSA modulus (in bytes) minus 2, minus twice the hash output size. :Return: A byte string, the ciphertext in which the message is encrypted. It is as long as the RSA modulus (in bytes). :Raise ValueError: If the RSA key length is not sufficiently long to deal with the given message. """ # TODO: Verify the key is RSA # See 7.1.1 in RFC3447 modBits = Cryptodome.Util.number.size(self._key.n) k = ceil_div(modBits,8) # Convert from bits to bytes hLen = self._hashObj.digest_size mLen = len(message) # Step 1b ps_len = k-mLen-2*hLen-2 if ps_len<0: raise ValueError("Plaintext is too long.") # Step 2a lHash = self._hashObj.new(self._label).digest() # Step 2b ps = bchr(0x00)*ps_len # Step 2c db = lHash + ps + bchr(0x01) + message # Step 2d ros = self._randfunc(hLen) # Step 2e dbMask = self._mgf(ros, k-hLen-1) # Step 2f maskedDB = strxor(db, dbMask) # Step 2g seedMask = self._mgf(maskedDB, hLen) # Step 2h maskedSeed = strxor(ros, seedMask) # Step 2i em = bchr(0x00) + maskedSeed + maskedDB # Step 3a (OS2IP) em_int = bytes_to_long(em) # Step 3b (RSAEP) m_int = self._key._encrypt(em_int) # Step 3c (I2OSP) c = long_to_bytes(m_int, k) return c
def encrypt(self, message): """Produce the PKCS#1 OAEP encryption of a message. This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in section 7.1.1 of RFC3447. :Parameters: message : byte string The message to encrypt, also known as plaintext. It can be of variable length, but not longer than the RSA modulus (in bytes) minus 2, minus twice the hash output size. :Return: A byte string, the ciphertext in which the message is encrypted. It is as long as the RSA modulus (in bytes). :Raise ValueError: If the RSA key length is not sufficiently long to deal with the given message. """ # TODO: Verify the key is RSA # See 7.1.1 in RFC3447 modBits = Cryptodome.Util.number.size(self._key.n) k = ceil_div(modBits, 8) # Convert from bits to bytes hLen = self._hashObj.digest_size mLen = len(message) # Step 1b ps_len = k - mLen - 2 * hLen - 2 if ps_len < 0: raise ValueError("Plaintext is too long.") # Step 2a lHash = self._hashObj.new(self._label).digest() # Step 2b ps = bchr(0x00) * ps_len # Step 2c db = lHash + ps + bchr(0x01) + message # Step 2d ros = self._randfunc(hLen) # Step 2e dbMask = self._mgf(ros, k - hLen - 1) # Step 2f maskedDB = strxor(db, dbMask) # Step 2g seedMask = self._mgf(maskedDB, hLen) # Step 2h maskedSeed = strxor(ros, seedMask) # Step 2i em = bchr(0x00) + maskedSeed + maskedDB # Step 3a (OS2IP) em_int = bytes_to_long(em) # Step 3b (RSAEP) m_int = self._key._encrypt(em_int) # Step 3c (I2OSP) c = long_to_bytes(m_int, k) return c
def encrypt(self, message): """Encrypt a message with PKCS#1 OAEP. :param message: The message to encrypt, also known as plaintext. It can be of variable length, but not longer than the RSA modulus (in bytes) minus 2, minus twice the hash output size. For instance, if you use RSA 2048 and SHA-256, the longest message you can encrypt is 190 byte long. :type message: byte string :returns: The ciphertext, as large as the RSA modulus. :rtype: byte string :raises ValueError: if the message is too long. """ # TODO: Verify the key is RSA # See 7.1.1 in RFC3447 modBits = Cryptodome.Util.number.size(self._key.n) k = ceil_div(modBits,8) # Convert from bits to bytes hLen = self._hashObj.digest_size mLen = len(message) # Step 1b ps_len = k-mLen-2*hLen-2 if ps_len<0: raise ValueError("Plaintext is too long.") # Step 2a lHash = self._hashObj.new(self._label).digest() # Step 2b ps = bchr(0x00)*ps_len # Step 2c db = lHash + ps + bchr(0x01) + message # Step 2d ros = self._randfunc(hLen) # Step 2e dbMask = self._mgf(ros, k-hLen-1) # Step 2f maskedDB = strxor(db, dbMask) # Step 2g seedMask = self._mgf(maskedDB, hLen) # Step 2h maskedSeed = strxor(ros, seedMask) # Step 2i em = bchr(0x00) + maskedSeed + maskedDB # Step 3a (OS2IP) em_int = bytes_to_long(em) # Step 3b (RSAEP) m_int = self._key._encrypt(em_int) # Step 3c (I2OSP) c = long_to_bytes(m_int, k) return c
def __init__(self, key, msg=b(""), digestmod=None): """Create a new HMAC object. :Parameters: key : byte string secret key for the MAC object. It must be long enough to match the expected security level of the MAC. However, there is no benefit in using keys longer than the `digest_size` of the underlying hash algorithm. msg : byte string The very first chunk of the message to authenticate. It is equivalent to an early call to `update()`. Optional. :Parameter digestmod: The hash algorithm the HMAC is based on. Default is `Cryptodome.Hash.MD5`. :Type digestmod: A hash module or object instantiated from `Cryptodome.Hash` """ if digestmod is None: digestmod = MD5 if msg is None: msg = b("") #: Size of the MAC tag self.digest_size = digestmod.digest_size self._digestmod = digestmod try: if len(key) <= digestmod.block_size: # Step 1 or 2 key_0 = key + bchr(0) * (digestmod.block_size - len(key)) else: # Step 3 hash_k = digestmod.new(key).digest() key_0 = hash_k + bchr(0) * (digestmod.block_size - len(hash_k)) except AttributeError: # Not all hash types have "block_size" raise ValueError("Hash type incompatible to HMAC") # Step 4 key_0_ipad = strxor(key_0, bchr(0x36) * len(key_0)) # Start step 5 and 6 self._inner = digestmod.new(key_0_ipad) self._inner.update(msg) # Step 7 key_0_opad = strxor(key_0, bchr(0x5c) * len(key_0)) # Start step 8 and 9 self._outer = digestmod.new(key_0_opad)
def q3_block_cipher_timing_attack( leaky_encipher=lab6_helper.leaky_encipher_example): """Question 3: Collision timing attack on AES""" trials = [] At = 14 A1t = 0 for _ in range(15): dic = {} for _ in range(200): Rand = urandom(At) Rand1 = urandom(A1t) mintrial = 17 byte1 = "01" secondtrial = "" for i in range(0, 16**2): byte2 = "{:02x}".format(i) pt = binascii.unhexlify(byte1) + Rand1 + binascii.unhexlify( byte2) + Rand trial, ct = leaky_encipher(pt) if trial <= mintrial: mintrial = trial secondtrial = [byte1, byte2] mainTrial = strxor(binascii.unhexlify(secondtrial[0]), binascii.unhexlify(secondtrial[1])).hex() if mainTrial not in dic: dic[mainTrial] = 1 else: dic[mainTrial] += 1 maxor = max(dic.items(), key=operator.itemgetter(1))[0] trials.append(maxor) At -= 1 A1t += 1 plaintext = "this is a adals!" tri, leaky = leaky_encipher(plaintext.encode()) for j in range(0, 16**2): key = "{:02x}".format(j) poss1rd = key for t in trials: posskey = strxor(binascii.unhexlify(key), binascii.unhexlify(t)).hex() poss1rd = posskey + poss1rd possCi = aes_(poss1rd, plaintext.encode()) if possCi == leaky: return poss1rd return "NOPE"
def crypt(self, block): """ Crypt le block""" # le vecteur doit être initilisé si on ne s'en ai jamais servi if self.get_vector() is None: self.init_vector(len(block)) # premièrement on xor avec le vecteur crypted_block = strxor(self.get_vector(), block) # Puis on chiffre le tout crypted_block = self.get_encrypter().crypt(crypted_block) # enfin on sauvegarde le résultat dans le vecteur self.set_vector(strxor(crypted_block, block)) # on retourne le résulat return crypted_block
def uncrypt(self, block): """ Uncrypt le block""" # le vecteur doit être initilisé si on ne s'en ai jamais servi if self.get_vector() is None: self.init_vector(len(block)) # Premièrement on déchiffre le block uncrypted_block = self.get_encrypter().uncrypt(block) # on xor le résultat avec notre vecteur uncrypted_block = strxor(self.get_vector(), uncrypted_block) # notre vecteur prend la valeur du block self.set_vector(strxor(block, uncrypted_block)) # On retourne le texte claire return uncrypted_block
def _cipher(data): xorKey = io.BytesIO() digest = constants.msKey[:20] while xorKey.tell() < len(data): digest = SHA.new(digest + constants.msKey[20:40]).digest() xorKey.write(digest) return strxor(data, xorKey.getvalue()[:len(data)])
def update(self, item): """Pass the next component of the vector. The maximum number of components you can pass is equal to the block length of the cipher (in bits) minus 1. :Parameters: item : byte string The next component of the vector. :Raise TypeError: when the limit on the number of components has been reached. :Raise ValueError: when the component is empty """ if not item: raise ValueError("A component cannot be empty") if self._n_updates==0: raise TypeError("Too many components passed to S2V") self._n_updates -= 1 mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod, cipher_params=self._cipher_params) self._cache = strxor(self._double(self._cache), mac.digest()) self._last_string = item
def test_bytearray(self): term1 = unhexlify(b"ff339a83e5cd4cdf5649") term1_ba = bytearray(term1) term2 = unhexlify(b"383d4ba020573314395b") result = unhexlify(b"c70ed123c59a7fcb6f12") self.assertEqual(strxor(term1_ba, term2), result)
def test_memoryview(self): term1 = unhexlify(b"ff339a83e5cd4cdf5649") term1_mv = memoryview(term1) term2 = unhexlify(b"383d4ba020573314395b") result = unhexlify(b"c70ed123c59a7fcb6f12") self.assertEqual(strxor(term1_mv, term2), result)
def decrypt(self, ct: bytes, iv: bytes) -> bytes: c = [iv] + iteration.blocks(ct, AES.block_size) p = [ strxor.strxor(x, self.ecb.decrypt(y)) for x, y in iteration.pairs(c) ] return self._unpad(b"".join(p))
def leaky_encipher(key, A): # Let me denote the state in AES as follows: # A = input to AES # Z = enciphering of A (aka, result after applying all 10 rounds) # Y = value just before the final AddRoundKey # X = value at the beginning of the 10th round, before ShiftRows and SubBytes as well (remember: the final round lacks MixColumns) # So the final round of AES looks like this: # X -> [SubBytes] -> [ShiftRows] -> Y -> [AddRoundKey, aka strxor with lastroundkey] -> Z # First, perform all 10 rounds of AES assert(len(A) == 16) permutation = AES.new(key, AES.MODE_ECB) Z = permutation.encrypt(A) # This is the desired output! Now we just have to simulate the cache lines... # Now go back one round of AES, from the end back to the beginning. We begin with AddRoundKey, which is its own inverse. lastroundkey = aes128_lastroundkey(key) # This is the key used in the final xor step of AES. You need the aeskeyexp helper file for this. Y = strxor(Z, lastroundkey) # xor is its own inverse #print("key",lastroundkey) Yvec = [ord(i) for i in Y] # Transform the string into a vector of bytes #print("Yvec",Yvec) sinv_yvec = [Sinv[y] for y in Yvec] # ShiftRows is irrelevant here because it merely permutes the order of bytes, and we won't care about that when we output a set. # So it remains to perform an inverse SubBytes operation. #X = frozenset(map(Sinv, Yvec)) # Apply Sinv to each byte of the state, and form the *set* of resulting values X = frozenset(sinv_yvec) # Return both the ciphertext and the set of bytes at the start of the 10th round return [Z, X]
def guess_block(block: bytes) -> bytes: deblock = bytearray([0] * BS) # AES decrypted block for i in range(BS - 1, -1, -1): # guess deblock[i] knowing deblock[i+1:] pad_siz = BS - i # choosing the iv, we will force block[i+1:] # to be decrypted as the following pad -1 byte padding = bytes([pad_siz] * (pad_siz - 1)) iv_suff = strxor(deblock[i + 1:], padding) # block[i] will have to be decrypted as pad_size # for the padding to be valid # except for the last byte which will require # a second request to confirm the pad '1' for b in range(256): # try iv[i] = b iv = bytes(i) + bytes([b]) + iv_suff if oracle(iv, block): if i == BS - 1: # last byte # check that it is not a false positive: # make sure that the valid pad is '1' and # not '22', '333', '4444', ... # by changing the previous byte in the IV iv = bytes(i - 1) + bytes([1, b]) if oracle(iv, block): # confirmed break else: # block[i] was decrypted to pad_size break deblock[i] = b ^ pad_siz return bytes(deblock)
def q2_cbc_mac(key, message, cipher=Sample_Cipher): """Question 2 Args: key (str): ascii input string (1-byte long) message (str): ascii input string with an arbitrary length cipher (Cipher): Cipher class (check `sample_cipher.py`) that provides access to the TOY cipher + cipher.TOY(message, key) Your task: Implement CBC-MAC mode on top of the TOY cipher (from question 1). Using the TOY cipher will result in a tag of length 1-byte, such a short tag limits the security guarantees, but we won't focus on that in this question. Your Output: Compute and return the 1-byte message tag as a hex-encoded string Test vectors: q2_cbc_mac("k", "This is a test message!", Sample_Cipher()) == "1f" q2_cbc_mac("a", "print(\"CBC-MAC is a very strong hash function!\")", Sample_Cipher()) == "a6" q2_cbc_mac("m", "short_msg", Sample_Cipher()) == "12" """ """convert the string to hexadecimal representaion and store in array and just call the TOY for as many bytes in the array the first one is done outside the loop and CBC mode is done inside the loop """ enc_key = key.encode('ascii').hex() array = [] for char in message: temp = ord(char) val = hex(temp) final_val = val[2:] array.append(final_val) final_tag = cipher.TOY(array[0],enc_key) for byte in array[1:]: intermediate_tag = strxor(bytes.fromhex(final_tag),bytes.fromhex(byte)).hex() final_tag = cipher.TOY(intermediate_tag,enc_key) return final_tag
def verify(self, received_mac_tag): """Validate the *binary* MAC tag. The caller invokes this function at the very end. This method checks if the decrypted message is indeed valid (that is, if the key is correct) and it has not been tampered with while in transit. :Parameters: received_mac_tag : bytes/bytearray/memoryview This is the *binary* MAC, as received from the sender. :Raises MacMismatchError: if the MAC does not match. The message has been tampered with or the key is incorrect. """ if self.verify not in self._next: raise TypeError("verify() cannot be called" " when encrypting a message") self._next = [self.verify] if not self._mac_tag: tag = b'\x00' * self.block_size for i in xrange(3): tag = strxor(tag, self._omac[i].digest()) self._mac_tag = tag[:self._mac_len] secret = get_random_bytes(16) mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) if mac1.digest() != mac2.digest(): raise ValueError("MAC check failed")
def derive(self): """"Derive a secret from the vector of components. :Return: a byte string, as long as the block length of the cipher. """ if len(self._last_string)>=16: final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache) else: padded = (self._last_string + bchr(0x80)+ bchr(0)*15)[:16] final = strxor(padded, self._double(self._cache)) mac = CMAC.new(self._key, msg=final, ciphermod=self._ciphermod, cipher_params=self._cipher_params) return mac.digest()
def token_key_pair(self, mac: str, ssid: str, pw: str): local_key = self.local_key(mac, ssid, pw) rand = get_random_bytes(32) key = strxor(rand, local_key) token = self.aes_cbc_encrypt(key, local_key) sign = sha256(key).digest() return (token + sign, key)
def q1_dec_cbc_mode(key, ciphertext, iv, cipher=Sample_Cipher): """Implement CBC Mode **decryption**""" pad = lambda s: s + (cipher.BLOCK_SIZE - len(s) % cipher.BLOCK_SIZE) * chr(cipher.BLOCK_SIZE - len(s) % cipher.BLOCK_SIZE) unpad = lambda s : s[0:-ord(s[-1])] cipher1=slice_into_block(ciphertext, cipher.BLOCK_SIZE*2) msg=cipher.decipher(key,cipher1[0]) msgs="" first=binascii.hexlify(strxor(binascii.unhexlify(iv),binascii.unhexlify(msg))).decode() msgs+=first for i in range(1, len(cipher1)): msg=cipher.decipher(key,cipher1[i]) last=binascii.hexlify(strxor(binascii.unhexlify(cipher1[i-1]),binascii.unhexlify(msg))).decode() msgs+=last return unpad(binascii.unhexlify(msgs).decode())
def cbcmac(key, message, iv): BLOCK_SIZE = 16 def Encipher(key, X): assert(len(key) == BLOCK_SIZE*2) assert(len(X) == BLOCK_SIZE*2) return bytes.fromhex(aes_encipher(key,X)) def slice_into_blocks(message, block_size): len_message = len(message) assert(len_message >= block_size) return [message[i: i + block_size] for i in range(0 , len_message, block_size)] len_message = len(message) num_rounds = len_message//BLOCK_SIZE msg_blocks = slice_into_blocks(message, BLOCK_SIZE) assert(len(msg_blocks) == num_rounds) if not iv: iv = bytes([0 for _ in range(BLOCK_SIZE)]) # All zeros IV to start with for msg_block in msg_blocks: block_input = strxor(msg_block.encode('ascii'), iv) block_output = Encipher(key.encode('ascii').hex(), block_input.hex()) iv = block_output return block_output.hex()
def leaky_encipher_example(plaintext): """performs an AES encipher on the input 16-bytes input `plaintext` Args: plaintext (bytes) : 16-bytes input to be passed to AES for enciphering Output: ret (int, bytes): number of unique values in the intermidiate state after the first round of substitutions, and the ciphertext produced by running AES on the input. """ key = TEST_KEY # NOTE: Just as an example, a random key would be used to test your code # First, perform all 10 rounds of AES assert (len(plaintext) == 16) permutation = AES.new(bytes.fromhex(key), AES.MODE_ECB) Z = permutation.encrypt(plaintext) # This is the desired output! mid_state = strxor(plaintext, bytes.fromhex(key)) X = frozenset(map(Sbox, mid_state)) # Apply Sbox to each byte of the state, # and form the *set* of resulting values return (len(X), Z)
def _digest(self): if self._mac_tag: return self._mac_tag if self._assoc_len is None: assert(isinstance(self._cache, list)) self._assoc_len = sum([len(x) for x in self._cache]) if self._msg_len is not None: self._start_mac() else: if self._cumul_assoc_len < self._assoc_len: raise ValueError("Associated data is too short") if self._msg_len is None: self._msg_len = 0 self._start_mac() if self._cumul_msg_len != self._msg_len: raise ValueError("Message is too short") # Both associated data and payload are concatenated with the least # number of zero bytes (possibly none) that align it to the # 16 byte boundary (A.2.2 and A.2.3) self._pad_cache_and_update() # Step 8 in 6.1 (T xor MSB_Tlen(S_0)) self._mac_tag = strxor(self._t, self._s_0)[:self._mac_len] return self._mac_tag
def decrypt_data(self, given_key=None, file_name=None): # data parsing if file_name is not None: self.file_name = file_name with open(self.file_name, 'rb') as file: iv = file.read(AES.block_size) encrypted_data = file.read() # key setting raw_key = given_key if raw_key is None: raw_key = self.raw_key set_key = self.set_key(raw_key) encoded_key = set_key.encode() key = strxor.strxor(encoded_key, iv) # decrypt data cipher = AES.new(key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(encrypted_data) unpadded_data = Padding.unpad(decrypted_data, AES.block_size) plain_data = unpadded_data.decode() parsed_data = plain_data.split(';') self.login_id = parsed_data[0] self.login_password = parsed_data[1] self.account_password = parsed_data[2] self.certificate_password = parsed_data[3] return parsed_data
def encrypt_data(self, login_id, login_password, account_password, certificate_password, given_key=None, file_name=None): plain_data = ';'.join((login_id, login_password, account_password, certificate_password)) if file_name is not None: self.file_name = file_name # key setting random = Random.new() raw_key = given_key if raw_key is None: raw_key = self.raw_key set_key = self.set_key(raw_key) iv = random.read(AES.block_size) encoded_key = set_key.encode() key = strxor.strxor(encoded_key, iv) # Encryption encoded_data = plain_data.encode() padded_data = Padding.pad(encoded_data, AES.block_size) cipher = AES.new(key, AES.MODE_CBC, iv) encrypted_data = cipher.encrypt(padded_data) # File writing with open(self.file_name, 'wb') as file: file.write(iv) file.write(encrypted_data)
def _digest(self): if self._mac_tag: return self._mac_tag if self._assoc_len is None: assert (isinstance(self._cache, list)) self._assoc_len = sum([len(x) for x in self._cache]) if self._msg_len is not None: self._start_mac() else: if self._cumul_assoc_len < self._assoc_len: raise ValueError("Associated data is too short") if self._msg_len is None: self._msg_len = 0 self._start_mac() if self._cumul_msg_len != self._msg_len: raise ValueError("Message is too short") # Both associated data and payload are concatenated with the least # number of zero bytes (possibly none) that align it to the # 16 byte boundary (A.2.2 and A.2.3) self._pad_cache_and_update() # Step 8 in 6.1 (T xor MSB_Tlen(S_0)) self._mac_tag = strxor(self._t, self._s_0)[:self._mac_len] return self._mac_tag
def verify(self, received_mac_tag): """Validate the *binary* MAC tag. The caller invokes this function at the very end. This method checks if the decrypted message is indeed valid (that is, if the key is correct) and it has not been tampered with while in transit. :Parameters: received_mac_tag : byte string/array This is the *binary* MAC, as received from the sender. :Raises MacMismatchError: if the MAC does not match. The message has been tampered with or the key is incorrect. """ if self.verify not in self._next: raise TypeError("verify() cannot be called" " when encrypting a message") self._next = [self.verify] if not self._mac_tag: tag = bchr(0) * self.block_size for i in xrange(3): tag = strxor(tag, self._omac[i].digest()) self._mac_tag = tag[:self._mac_len] secret = get_random_bytes(16) mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag) mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag) if mac1.digest() != mac2.digest(): raise ValueError("MAC check failed")
def update(self, item): """Pass the next component of the vector. The maximum number of components you can pass is equal to the block length of the cipher (in bits) minus 1. :Parameters: item : byte string The next component of the vector. :Raise TypeError: when the limit on the number of components has been reached. :Raise ValueError: when the component is empty """ if not item: raise ValueError("A component cannot be empty") if self._n_updates == 0: raise TypeError("Too many components passed to S2V") self._n_updates -= 1 mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod, cipher_params=self._cipher_params) self._cache = strxor(self._double(self._cache), mac.digest()) self._last_string = item
def decrypt(cls, data, key): validate_key_size(key, cls.key_size, "Twofish") ctr = BlockCipher.generate_decrypt_counter(data, cls.block_size) tfish = twofish.Twofish(key) return strxor(data[cls.block_size:], cls._gen_keystream(len(data[cls.block_size:]), tfish, ctr))
def test_corrupt_decrypt(self, tv): self._id = "Wycheproof Corrupt Decrypt EAX Test #" + str(tv.id) if len(tv.iv) == 0 or len(tv.ct) < 1: return cipher = AES.new(tv.key, AES.MODE_EAX, tv.iv, mac_len=tv.tag_size) cipher.update(tv.aad) ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
def encrypt(cls, data, key, iv_data): validate_key_size(key, cls.key_size, "Twofish") iv, ctr = iv_data tfish = twofish.Twofish(key) ciphertext = strxor(data, cls._gen_keystream(len(data), tfish, ctr)) return iv + ciphertext
def test_corrupt_decrypt(self, tv): self._id = "Wycheproof Corrupt Decrypt ChaCha20-Poly1305 Test #" + str(tv.id) if len(tv.iv) == 0 or len(tv.ct) < 1: return cipher = ChaCha20_Poly1305.new(key=tv.key, nonce=tv.iv) cipher.update(tv.aad) ct_corrupt = strxor(tv.ct, b"\x00" * (len(tv.ct) - 1) + b"\x01") self.assertRaises(ValueError, cipher.decrypt_and_verify, ct_corrupt, tv.tag)
def runTest(self): key = RSA.generate(1024) hashed = SHA1.new(b("Test")) good_signature = PKCS1_PSS.new(key).sign(hashed) verifier = PKCS1_PSS.new(key.publickey()) self.assertEqual(verifier.verify(hashed, good_signature), True) # Flip a few bits in the signature bad_signature = strxor(good_signature, bchr(1) * len(good_signature)) self.assertEqual(verifier.verify(hashed, bad_signature), False)
def runTest(self): key = RSA.importKey(PKCS1_15_NoParams.rsakey) hashed = SHA1.new(b("Test")) good_signature = PKCS1_v1_5.new(key).sign(hashed) verifier = PKCS1_v1_5.new(key.publickey()) self.assertEqual(verifier.verify(hashed, good_signature), True) # Flip a few bits in the signature bad_signature = strxor(good_signature, bchr(1) * len(good_signature)) self.assertEqual(verifier.verify(hashed, bad_signature), False)
def __init__(self, key, msg=b"", digestmod=None): if digestmod is None: digestmod = MD5 if msg is None: msg = b"" # Size of the MAC tag self.digest_size = digestmod.digest_size self._digestmod = digestmod if isinstance(key, _memoryview): key = key.tobytes() try: if len(key) <= digestmod.block_size: # Step 1 or 2 key_0 = key + b"\x00" * (digestmod.block_size - len(key)) else: # Step 3 hash_k = digestmod.new(key).digest() key_0 = hash_k + b"\x00" * (digestmod.block_size - len(hash_k)) except AttributeError: # Not all hash types have "block_size" raise ValueError("Hash type incompatible to HMAC") # Step 4 key_0_ipad = strxor(key_0, b"\x36" * len(key_0)) # Start step 5 and 6 self._inner = digestmod.new(key_0_ipad) self._inner.update(msg) # Step 7 key_0_opad = strxor(key_0, b"\x5c" * len(key_0)) # Start step 8 and 9 self._outer = digestmod.new(key_0_opad)
def test_output_overlapping_memoryview(self): """Verify result can be stored in overlapping memory""" term1 = memoryview(bytearray(unhexlify(b"ff339a83e5cd4cdf5649"))) term2 = unhexlify(b"383d4ba020573314395b") original_term2 = term2[:] expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") result = strxor(term1, term2, output=term1) self.assertEqual(result, None) self.assertEqual(term1, expected_xor) self.assertEqual(term2, original_term2)
def _scryptROMix(blocks, n): """Sequential memory-hard function for scrypt.""" x = [blocks[i:i + 64] for i in xrange(0, len(blocks), 64)] len_x = len(x) v = [None]*n load_le_uint32 = _raw_salsa20_lib.load_le_uint32 for i in xrange(n): v[i] = x x = _scryptBlockMix(x, len_x) for i in xrange(n): j = load_le_uint32(x[-1]) & (n - 1) t = [strxor(x[idx], v[j][idx]) for idx in xrange(len_x)] x = _scryptBlockMix(t, len_x) return b("").join([get_raw_buffer(y) for y in x])
def test_output_memoryview(self): """Verify result can be stored in pre-allocated memory""" term1 = unhexlify(b"ff339a83e5cd4cdf5649") term2 = unhexlify(b"383d4ba020573314395b") original_term1 = term1[:] original_term2 = term2[:] expected_xor = unhexlify(b"c70ed123c59a7fcb6f12") output = memoryview(bytearray(len(term1))) result = strxor(term1, term2, output=output) self.assertEqual(result, None) self.assertEqual(output, expected_xor) self.assertEqual(term1, original_term1) self.assertEqual(term2, original_term2)
def _update(self, data_block): """Update a block aligned to the block boundary""" bs = self._block_size assert len(data_block) % bs == 0 if len(data_block) == 0: return ct = self._cbc.encrypt(data_block) if len(data_block) == bs: second_last = self._last_ct else: second_last = ct[-bs*2:-bs] self._last_ct = ct[-bs:] self._last_pt = strxor(second_last, data_block[-bs:])
def digest(self): """Compute the *binary* MAC tag. The caller invokes this function at the very end. This method returns the MAC that shall be sent to the receiver, together with the ciphertext. :Return: the MAC, as a byte string. """ if self.digest not in self._next: raise TypeError("digest() cannot be called when decrypting" " or validating a message") self._next = [self.digest] if not self._mac_tag: tag = bchr(0) * self.block_size for i in xrange(3): tag = strxor(tag, self._omac[i].digest()) self._mac_tag = tag[: self._mac_len] return self._mac_tag
def _EMSA_PSS_ENCODE(mhash, emBits, randFunc, mgf, sLen): """ Implement the ``EMSA-PSS-ENCODE`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.1.1). The original ``EMSA-PSS-ENCODE`` actually accepts the message ``M`` as input, and hash it internally. Here, we expect that the message has already been hashed instead. :Parameters: mhash : hash object The hash object that holds the digest of the message being signed. emBits : int Maximum length of the final encoding, in bits. randFunc : callable An RNG function that accepts as only parameter an int, and returns a string of random bytes, to be used as salt. mgf : callable A mask generation function that accepts two parameters: a string to use as seed, and the lenth of the mask to generate, in bytes. sLen : int Length of the salt, in bytes. :Return: An ``emLen`` byte long string that encodes the hash (with ``emLen = \ceil(emBits/8)``). :Raise ValueError: When digest or salt length are too big. """ emLen = ceil_div(emBits, 8) # Bitmask of digits that fill up lmask = 0 for i in iter_range(8*emLen-emBits): lmask = lmask >> 1 | 0x80 # Step 1 and 2 have been already done # Step 3 if emLen < mhash.digest_size+sLen+2: raise ValueError("Digest or salt length are too long" " for given key size.") # Step 4 salt = randFunc(sLen) # Step 5 m_prime = bchr(0)*8 + mhash.digest() + salt # Step 6 h = mhash.new() h.update(m_prime) # Step 7 ps = bchr(0)*(emLen-sLen-mhash.digest_size-2) # Step 8 db = ps + bchr(1) + salt # Step 9 dbMask = mgf(h.digest(), emLen-mhash.digest_size-1) # Step 10 maskedDB = strxor(db, dbMask) # Step 11 maskedDB = bchr(bord(maskedDB[0]) & ~lmask) + maskedDB[1:] # Step 12 em = maskedDB + h.digest() + bchr(0xBC) return em
def decrypt(self, ct): """Decrypt a PKCS#1 OAEP ciphertext. This function is named ``RSAES-OAEP-DECRYPT``, and is specified in section 7.1.2 of RFC3447. :Parameters: ct : byte string The ciphertext that contains the message to recover. :Return: A byte string, the original message. :Raise ValueError: If the ciphertext length is incorrect, or if the decryption does not succeed. :Raise TypeError: If the RSA key has no private half. """ # See 7.1.2 in RFC3447 modBits = Cryptodome.Util.number.size(self._key.n) k = ceil_div(modBits,8) # Convert from bits to bytes hLen = self._hashObj.digest_size # Step 1b and 1c if len(ct) != k or k<hLen+2: raise ValueError("Ciphertext with incorrect length.") # Step 2a (O2SIP) ct_int = bytes_to_long(ct) # Step 2b (RSADP) m_int = self._key._decrypt(ct_int) # Complete step 2c (I2OSP) em = long_to_bytes(m_int, k) # Step 3a lHash = self._hashObj.new(self._label).digest() # Step 3b y = em[0] # y must be 0, but we MUST NOT check it here in order not to # allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143) maskedSeed = em[1:hLen+1] maskedDB = em[hLen+1:] # Step 3c seedMask = self._mgf(maskedDB, hLen) # Step 3d seed = strxor(maskedSeed, seedMask) # Step 3e dbMask = self._mgf(seed, k-hLen-1) # Step 3f db = strxor(maskedDB, dbMask) # Step 3g valid = 1 one = db[hLen:].find(bchr(0x01)) lHash1 = db[:hLen] if lHash1!=lHash: valid = 0 if one<0: valid = 0 if bord(y)!=0: valid = 0 if not valid: raise ValueError("Incorrect decryption.") # Step 4 return db[hLen+one+1:]
def __init__(self, factory, nonce, mac_len, cipher_params): if factory.block_size != 16: raise ValueError("OCB mode is only available for ciphers" " that operate on 128 bits blocks") self.block_size = 16 """The block size of the underlying cipher, in bytes.""" self.nonce = nonce """Nonce used for this session.""" if len(nonce) not in range(1, 16): raise ValueError("Nonce must be at most 15 bytes long") self._mac_len = mac_len if not 8 <= mac_len <= 16: raise ValueError("MAC tag must be between 8 and 16 bytes long") # Cache for MAC tag self._mac_tag = None # Cache for unaligned associated data self._cache_A = b("") # Cache for unaligned ciphertext/plaintext self._cache_P = b("") # Allowed transitions after initialization self._next = [self.update, self.encrypt, self.decrypt, self.digest, self.verify] # Compute Offset_0 params_without_key = dict(cipher_params) key = params_without_key.pop("key") nonce = (bchr(self._mac_len << 4 & 0xFF) + bchr(0) * (14 - len(self.nonce)) + bchr(1) + self.nonce) bottom = bord(nonce[15]) & 0x3F # 6 bits, 0..63 ktop = factory.new(key, factory.MODE_ECB, **params_without_key)\ .encrypt(nonce[:15] + bchr(bord(nonce[15]) & 0xC0)) stretch = ktop + strxor(ktop[:8], ktop[1:9]) # 192 bits offset_0 = long_to_bytes(bytes_to_long(stretch) >> (64 - bottom), 24)[8:] # Create low-level cipher instance raw_cipher = factory._create_base_cipher(cipher_params) if cipher_params: raise TypeError("Unknown keywords: " + str(cipher_params)) self._state = VoidPointer() result = _raw_ocb_lib.OCB_start_operation(raw_cipher.get(), offset_0, c_size_t(len(offset_0)), self._state.address_of()) if result: raise ValueError("Error %d while instantiating the OCB mode" % result) # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the cipher mode self._state = SmartPointer(self._state.get(), _raw_ocb_lib.OCB_stop_operation) # Memory allocated for the underlying block cipher is now owed # by the cipher mode raw_cipher.release()
def test3(self): term1 = unhexlify(b("ff339a83e5cd4cdf5649")) all_zeros = bchr(0) * len(term1) self.assertEqual(strxor(term1, term1), all_zeros)
def test2(self): es = b("") self.assertEqual(strxor(es, es), es)
def test1(self): term1 = unhexlify(b("ff339a83e5cd4cdf5649")) term2 = unhexlify(b("383d4ba020573314395b")) result = unhexlify(b("c70ed123c59a7fcb6f12")) self.assertEqual(strxor(term1, term2), result) self.assertEqual(strxor(term2, term1), result)
def _EMSA_PSS_VERIFY(mhash, em, emBits, mgf, sLen): """ Implement the ``EMSA-PSS-VERIFY`` function, as defined in PKCS#1 v2.1 (RFC3447, 9.1.2). ``EMSA-PSS-VERIFY`` actually accepts the message ``M`` as input, and hash it internally. Here, we expect that the message has already been hashed instead. :Parameters: mhash : hash object The hash object that holds the digest of the message to be verified. em : string The signature to verify, therefore proving that the sender really signed the message that was received. emBits : int Length of the final encoding (em), in bits. mgf : callable A mask generation function that accepts two parameters: a string to use as seed, and the lenth of the mask to generate, in bytes. sLen : int Length of the salt, in bytes. :Raise ValueError: When the encoding is inconsistent, or the digest or salt lengths are too big. """ emLen = ceil_div(emBits, 8) # Bitmask of digits that fill up lmask = 0 for i in iter_range(8*emLen-emBits): lmask = lmask >> 1 | 0x80 # Step 1 and 2 have been already done # Step 3 if emLen < mhash.digest_size+sLen+2: return False # Step 4 if ord(em[-1:]) != 0xBC: raise ValueError("Incorrect signature") # Step 5 maskedDB = em[:emLen-mhash.digest_size-1] h = em[emLen-mhash.digest_size-1:-1] # Step 6 if lmask & bord(em[0]): raise ValueError("Incorrect signature") # Step 7 dbMask = mgf(h, emLen-mhash.digest_size-1) # Step 8 db = strxor(maskedDB, dbMask) # Step 9 db = bchr(bord(db[0]) & ~lmask) + db[1:] # Step 10 if not db.startswith(bchr(0)*(emLen-mhash.digest_size-sLen-2) + bchr(1)): raise ValueError("Incorrect signature") # Step 11 if sLen > 0: salt = db[-sLen:] else: salt = b"" # Step 12 m_prime = bchr(0)*8 + mhash.digest() + salt # Step 13 hobj = mhash.new() hobj.update(m_prime) hp = hobj.digest() # Step 14 if h != hp: raise ValueError("Incorrect signature")