def test_memoryview(self): data = b"1" * 16 data_mv = memoryview(bytearray(data)) # Encrypt key_mv = memoryview(bytearray(self.key_128)) iv_mv = memoryview(bytearray(self.iv_128)) cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128) ref1 = cipher1.encrypt(data) cipher2 = AES.new(key_mv, self.aes_mode, iv_mv) key_mv[:3] = b'\xFF\xFF\xFF' iv_mv[:3] = b'\xFF\xFF\xFF' ref2 = cipher2.encrypt(data_mv) self.assertEqual(ref1, ref2) self.assertEqual(cipher1.iv, cipher2.iv) # Decrypt key_mv = memoryview(bytearray(self.key_128)) iv_mv = memoryview(bytearray(self.iv_128)) cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128) ref3 = cipher3.decrypt(data) cipher4 = AES.new(key_mv, self.aes_mode, iv_mv) key_mv[:3] = b'\xFF\xFF\xFF' iv_mv[:3] = b'\xFF\xFF\xFF' ref4 = cipher4.decrypt(data_mv) self.assertEqual(ref3, ref4)
def test_output_param(self): pt = b'5' * 16 cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) ct = cipher.encrypt(pt) tag = cipher.digest() output = bytearray(16) cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) res = cipher.encrypt(pt, output=output) self.assertEqual(ct, output) self.assertEqual(res, None) cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) res = cipher.decrypt(ct, output=output) self.assertEqual(pt, output) self.assertEqual(res, None) cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) res, tag_out = cipher.encrypt_and_digest(pt, output=output) self.assertEqual(ct, output) self.assertEqual(res, None) self.assertEqual(tag, tag_out) cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) res = cipher.decrypt_and_verify(ct, tag, output=output) self.assertEqual(pt, output) self.assertEqual(res, None)
def test_invalid_multiple_decrypt_and_verify(self): cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) ct, tag = cipher.encrypt_and_digest(self.data_128) cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) cipher.decrypt_and_verify(ct, tag) self.assertRaises(TypeError, cipher.decrypt_and_verify, ct, tag)
def test_shorter_assoc_data_than_expected(self): # With plaintext cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, assoc_len=17) cipher.update(self.data_128) self.assertRaises(ValueError, cipher.encrypt, self.data_128) # With empty plaintext cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, assoc_len=17) cipher.update(self.data_128) self.assertRaises(ValueError, cipher.digest) # With ciphertext cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, assoc_len=17) cipher.update(self.data_128) self.assertRaises(ValueError, cipher.decrypt, self.data_128) # With empty ciphertext cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96) cipher.update(self.data_128) mac = cipher.digest() cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=self.nonce_96, assoc_len=17) cipher.update(self.data_128) self.assertRaises(ValueError, cipher.verify, mac)
def test_data_must_be_bytes(self): cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') cipher = AES.new(self.key_256, AES.MODE_SIV, nonce=self.nonce_96) self.assertRaises(TypeError, cipher.decrypt_and_verify, u'test1234567890-*', b("xxxx"))
def test_bytearray(self): data = b"1" * 16 iv = b"\x00" * 6 + b"\xFF\xFF" # Encrypt cipher1 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64, initial_value=iv) ref1 = cipher1.encrypt(data) cipher2 = AES.new(self.key_128, AES.MODE_CTR, nonce=bytearray(self.nonce_64), initial_value=bytearray(iv)) ref2 = cipher2.encrypt(bytearray(data)) self.assertEqual(ref1, ref2) self.assertEqual(cipher1.nonce, cipher2.nonce) # Decrypt cipher3 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64, initial_value=iv) ref3 = cipher3.decrypt(data) cipher4 = AES.new(self.key_128, AES.MODE_CTR, nonce=bytearray(self.nonce_64), initial_value=bytearray(iv)) ref4 = cipher4.decrypt(bytearray(data)) self.assertEqual(ref3, ref4)
def _aes_CTR_cipher(key, nonce, block_index=0, little_endian=False): if little_endian: counter = Cryptodome.Util.Counter.new( nbits=64, prefix=nonce, initial_value=block_index, little_endian=True) return AES.new(key, AES.MODE_CTR, counter=counter) else: return AES.new(key, AES.MODE_CTR, nonce=nonce, initial_value=block_index)
def test_aes_256(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = 'dc7e84bfda79164b7ecd8486985d3860' +\ '4febdc6740d20b3ac88f6ad82a4fb08d' +\ '71ab47a086e86eedf39d1c5bba97c408' +\ '0126141d67f37be8538f5a8be740e484' key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' iv = '000102030405060708090a0b0c0d0e0f' key = unhexlify(key) iv = unhexlify(iv) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext), plaintext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
def test_aes_192(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = 'cdc80d6fddf18cab34c25909c99a4174' +\ 'fcc28b8d4c63837c09e81700c1100401' +\ '8d9a9aeac0f6596f559c6d4daf59a5f2' +\ '6d9f200857ca6c3e9cac524bd9acc92a' key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' iv = '000102030405060708090a0b0c0d0e0f' key = unhexlify(key) iv = unhexlify(iv) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext), plaintext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
def test_valid_init_verify(self): # Verify path INIT->VERIFY cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) mac = cipher.digest() cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.verify(mac)
def test_hex_mac(self): cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) mac_hex = cipher.hexdigest() self.assertEqual(cipher.digest(), unhexlify(mac_hex)) cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.hexverify(mac_hex)
def test3(self): for keylen, taglen, result in self.tv3: key = bchr(0) * (keylen // 8 - 1) + bchr(taglen) C = b("") for i in range(128): S = bchr(0) * i N = long_to_bytes(3 * i + 1, 12) cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) cipher.update(S) C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest() N = long_to_bytes(3 * i + 2, 12) cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) C += cipher.encrypt(S) + cipher.encrypt() + cipher.digest() N = long_to_bytes(3 * i + 3, 12) cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) cipher.update(S) C += cipher.encrypt() + cipher.digest() N = long_to_bytes(385, 12) cipher = AES.new(key, AES.MODE_OCB, nonce=N, mac_len=taglen // 8) cipher.update(C) result2 = cipher.encrypt() + cipher.digest() self.assertEquals(unhexlify(b(result)), result2)
def test_unaligned_data_128(self): plaintexts = [ b"7777777" ] * 100 cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) ciphertexts = [ cipher.encrypt(x) for x in plaintexts ] cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) self.assertEqual(b"".join(ciphertexts), cipher.encrypt(b"".join(plaintexts)))
def test_IV_iv_attributes(self): cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) eiv = cipher.encrypt(b"") self.assertEqual(cipher.iv, self.iv_128) cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) self.assertEqual(cipher.iv, self.iv_128)
def test_bytearray(self): data = b"1" * 16 data_ba = bytearray(data) # Encrypt key_ba = bytearray(self.key_128) iv_ba = bytearray(self.iv_128) cipher1 = AES.new(self.key_128, self.aes_mode, self.iv_128) ref1 = cipher1.encrypt(data) cipher2 = AES.new(key_ba, self.aes_mode, iv_ba) key_ba[:3] = b'\xFF\xFF\xFF' iv_ba[:3] = b'\xFF\xFF\xFF' ref2 = cipher2.encrypt(data_ba) self.assertEqual(ref1, ref2) self.assertEqual(cipher1.iv, cipher2.iv) # Decrypt key_ba = bytearray(self.key_128) iv_ba = bytearray(self.iv_128) cipher3 = AES.new(self.key_128, self.aes_mode, self.iv_128) ref3 = cipher3.decrypt(data) cipher4 = AES.new(key_ba, self.aes_mode, iv_ba) key_ba[:3] = b'\xFF\xFF\xFF' iv_ba[:3] = b'\xFF\xFF\xFF' ref4 = cipher4.decrypt(data_ba) self.assertEqual(ref3, ref4)
def test_aes_128(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = '3b3fd92eb72dad20333449f8e83cfb4a' +\ '7789508d16918f03f53c52dac54ed825' +\ '9740051e9c5fecf64344f7a82260edcc' +\ '304c6528f659c77866a510d9c1d6ae5e' key = '2b7e151628aed2a6abf7158809cf4f3c' iv = '000102030405060708090a0b0c0d0e0f' key = unhexlify(key) iv = unhexlify(iv) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext), plaintext) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.encrypt(plaintext[:-8]), ciphertext[:-8]) cipher = AES.new(key, AES.MODE_OFB, iv) self.assertEqual(cipher.decrypt(ciphertext[:-8]), plaintext[:-8])
def test_nonce_parameter(self): # Nonce parameter becomes nonce attribute cipher1 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64) self.assertEqual(cipher1.nonce, self.nonce_64) counter = Counter.new(64, prefix=self.nonce_64, initial_value=0) cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) self.assertEqual(cipher1.nonce, cipher2.nonce) pt = get_tag_random("plaintext", 65536) self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) # Nonce is implicitly created (for AES) when no parameters are passed nonce1 = AES.new(self.key_128, AES.MODE_CTR).nonce nonce2 = AES.new(self.key_128, AES.MODE_CTR).nonce self.assertNotEqual(nonce1, nonce2) self.assertEqual(len(nonce1), 8) # Nonce can be zero-length cipher = AES.new(self.key_128, AES.MODE_CTR, nonce=b"") self.assertEqual(b"", cipher.nonce) cipher.encrypt(b'0'*300) # Nonce and Counter are mutually exclusive self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, counter=self.ctr_128, nonce=self.nonce_64)
def test_unknown_parameters(self): self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, self.iv_128, 7) self.assertRaises(TypeError, AES.new, self.key_128, self.aes_mode, iv=self.iv_128, unknown=7) # But some are only known by the base cipher (e.g. use_aesni consumed by the AES module) AES.new(self.key_128, self.aes_mode, iv=self.iv_128, use_aesni=False)
def test_unknown_parameters(self): self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, 7, counter=self.ctr_128) self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, counter=self.ctr_128, unknown=7) # But some are only known by the base cipher (e.g. use_aesni consumed by the AES module) AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128, use_aesni=False)
def decrypt(secretkey, params): iv = b64decode(params['iv']) salt = b64decode(params['salt']) #~ keylen = params.get('ks', 128) // 8 # FIXME use somewhere? taglen = params.get('ts', 64) // 8 iterations = params.get('iter', 1000) data = b64decode(params['ct']) ciphertext = data[:-taglen] tag = data[-taglen:] if params.get('adata'): raise NotImplementedError('authenticated data support is not implemented') iv = trunc_iv(iv, ciphertext, taglen) hash_func = lambda k, s: HMAC.new(k, s, SHA256).digest() key = PBKDF2(secretkey, salt=salt, count=iterations, prf=hash_func) mode_str = params.get('mode', 'ccm') mode = dict(ccm=AES.MODE_CCM)[mode_str] if mode_str == 'ccm': cipher = AES.new(key, mode=AES.MODE_CCM, nonce=iv, mac_len=taglen) else: cipher = AES.new(key, mode=mode, iv=iv) decrypted = cipher.decrypt_and_verify(ciphertext, tag) return decrypted
def test_either_encrypt_or_decrypt(self): cipher = AES.new(self.key_128, AES.MODE_OPENPGP, self.iv_128) eiv = cipher.encrypt(b"") self.assertRaises(TypeError, cipher.decrypt, b"") cipher = AES.new(self.key_128, AES.MODE_OPENPGP, eiv) cipher.decrypt(b"") self.assertRaises(TypeError, cipher.encrypt, b"")
def test_loopback_128(self): cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) pt = get_tag_random("plaintext", 16 * 100) ct = cipher.encrypt(pt) cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) pt2 = cipher.decrypt(ct) self.assertEqual(pt, pt2)
def test_loopback_128(self): cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) pt = get_tag_random("plaintext", 16 * 100) ct, mac = cipher.encrypt_and_digest(pt) cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) pt2 = cipher.decrypt_and_verify(ct, mac) self.assertEqual(pt, pt2)
def test_loopback_128(self): cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) pt = get_tag_random("plaintext", 16 * 100) ct = cipher.encrypt(pt) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) pt2 = cipher.decrypt(ct) self.assertEqual(pt, pt2)
def test_either_encrypt_or_decrypt(self): cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.encrypt(b("xyz")) self.assertRaises(TypeError, cipher.decrypt, b("xyz")) cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.decrypt(b("xyz")) self.assertRaises(TypeError, cipher.encrypt, b("xyz"))
def test_either_encrypt_or_decrypt(self): cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) cipher.encrypt(b("")) self.assertRaises(TypeError, cipher.decrypt, b("")) cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) cipher.decrypt(b("")) self.assertRaises(TypeError, cipher.encrypt, b(""))
def initialize_cipher(self): """Creates the cipher-related objects needed for AES-CTR encryption and decryption. """ self.ctr_e = Counter.new(self.AES_block_size, initial_value=self.iv) self.ctr_d = Counter.new(self.AES_block_size, initial_value=self.iv) self.encryptor = AES.new(self.key, AES.MODE_CTR, counter=self.ctr_e) self.decryptor = AES.new(self.key, AES.MODE_CTR, counter=self.ctr_d)
def test_loopback_128(self): cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) pt = get_tag_random("plaintext", 16 * 100) ct = cipher.encrypt(pt) cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) pt2 = cipher.decrypt(ct) self.assertEqual(pt, pt2)
def test_unaligned_data_128(self): cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) for wrong_length in xrange(1,16): self.assertRaises(ValueError, cipher.encrypt, b("5") * wrong_length) cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) for wrong_length in xrange(1,16): self.assertRaises(ValueError, cipher.decrypt, b("5") * wrong_length)
def test_either_encrypt_or_decrypt(self): cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) cipher.encrypt(b"") self.assertRaises(TypeError, cipher.decrypt, b"") cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) cipher.decrypt(b"") self.assertRaises(TypeError, cipher.encrypt, b"")
def decrypt_pairing_response(enc_pairing_response): """ Parses and decrypts a pairing response into a named tuple PairingResponse consisting of * user_public_key - the user's public key * user_token_id - an id for the client to uniquely identify the token. this id is necessary, because the client could communicate with more than one linotp, so serials could overlap. * serial - the serial identifying the token in linotp * user_login - the user login name It is possible that either user_login or serial is None. Both being None is a valid response according to this function but will be considered an error in the calling method. The following parameters are needed: :param enc_pairing_response: The urlsafe-base64 encoded string received from the client The following exceptions can be raised: :raises ParameterError: If the pairing response has an invalid format :raises ValueError: If the pairing response has a different version than this implementation (currently hardcoded) :raises ValueError: If the pairing response indicates a different token type than QRToken (also hardcoded) :raises ValueError: If the pairing response field "partition" is not identical to the field "token_type" ("partition" is currently used for the token type id. It is reserved for multiple key usage in a future implementation.) :raises ValueError: If the MAC of the response didn't match :return: Parsed/decrypted PairingReponse """ data = decode_base64_urlsafe(enc_pairing_response) # ---------------------------------------------------------------------- -- # ------------------------------------------- -- # fields | version | partition | R | ciphertext | MAC | # ------------------------------------------- -- # size | 1 | 4 | 32 | ? | 16 | # ------------------------------------------- -- if len(data) < 1 + 4 + 32 + 16: raise ParameterError('Malformed pairing response') # ---------------------------------------------------------------------- -- # parse header header = data[0:5] version, partition = struct.unpack('<bI', header) if version != PAIR_RESPONSE_VERSION: raise ValueError('Unexpected pair-response version, ' 'expected: %d, got: %d' % (PAIR_RESPONSE_VERSION, version)) # ---------------------------------------------------------------------- -- R = data[5:32+5] ciphertext = data[32+5:-16] mac = data[-16:] # ---------------------------------------------------------------------- -- # calculate the shared secret # - -- secret_key = get_dh_secret_key(partition) ss = calc_dh(secret_key, R) # derive encryption key and nonce from the shared secret # zero the values from memory when they are not longer needed U = SHA256.new(ss).digest() zerome(ss) encryption_key = U[0:16] nonce = U[16:32] zerome(U) # decrypt response cipher = AES.new(encryption_key, AES.MODE_EAX, nonce) cipher.update(header) plaintext = cipher.decrypt_and_verify(ciphertext, mac) zerome(encryption_key) # ---------------------------------------------------------------------- -- # check format boundaries for type peaking # (token type specific length boundaries are checked # in the appropriate functions) plaintext_min_length = 1 if len(data) < plaintext_min_length: raise ParameterError('Malformed pairing response') # ---------------------------------------------------------------------- -- # get token type and parse decrypted response # -------------------- -- # fields | token type | ... | # -------------------- -- # size | 1 | ? | # -------------------- -- token_type = struct.unpack('<b', plaintext[0])[0] if token_type not in SUPPORTED_TOKEN_TYPES: raise ValueError('unsupported token type %d, supported types ' 'are %s' % (token_type, SUPPORTED_TOKEN_TYPES)) # ---------------------------------------------------------------------- -- # delegate the data parsing of the plaintext # to the appropriate function and return the result data_parser = get_pairing_data_parser(token_type) pairing_data = data_parser(plaintext) zerome(plaintext) # get the appropriate high level type try: token_type_as_str = INV_TOKEN_TYPES[token_type] except KeyError: raise ProgrammingError('token_type %d is in SUPPORTED_TOKEN_TYPES', 'however an appropriate mapping entry in ' 'TOKEN_TYPES is missing' % token_type) return PairingResponse(token_type_as_str, pairing_data)
def test_null_encryption_decryption(self): for func in "encrypt", "decrypt": cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) result = getattr(cipher, func)(b("")) self.assertEqual(result, b(""))
def test_data_must_be_bytes(self): cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
def test_null_encryption_decryption(self): for func in "encrypt", "decrypt": cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) result = getattr(cipher, func)(b("")) self.assertEqual(result, b(""))
def getDBfromFile(kInfoFile): names = [\ b'kindle.account.tokens',\ b'kindle.cookie.item',\ b'eulaVersionAccepted',\ b'login_date',\ b'kindle.token.item',\ b'login',\ b'kindle.key.item',\ b'kindle.name.info',\ b'kindle.device.info',\ b'MazamaRandomNumber',\ b'max_date',\ b'SIGVERIF',\ b'build_version',\ b'SerialNumber',\ b'UsernameHash',\ b'kindle.directedid.info',\ b'DSN',\ b'kindle.accounttype.info',\ b'krx.flashcardsplugin.data.encryption_key',\ b'krx.notebookexportplugin.data.encryption_key',\ b'proxy.http.password',\ b'proxy.http.username' ] with open(kInfoFile, 'rb') as infoReader: filedata = infoReader.read() data = filedata[:-1] items = data.split(b'/') IDStrings = GetIDStrings() print("trying username ", GetUserName(), " on file ", kInfoFile) for IDString in IDStrings: print("trying IDString:", IDString) try: DB = {} items = data.split(b'/') # the headerblob is the encrypted information needed to build the entropy string headerblob = items.pop(0) #print ("headerblob: ",headerblob) encryptedValue = decode(headerblob, charMap1) #print ("encryptedvalue: ",encryptedValue) cleartext = UnprotectHeaderData(encryptedValue) #print ("cleartext: ",cleartext) # now extract the pieces in the same way pattern = re.compile( br'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE) for m in re.finditer(pattern, cleartext): version = int(m.group(1)) build = m.group(2) guid = m.group(4) #print ("version",version) #print ("build",build) #print ("guid",guid,"\n") if version == 5: # .kinf2011: identical to K4PC, except the build number gets multiplied entropy = str(0x2df * int(build)).encode('utf-8') + guid cud = CryptUnprotectData(entropy, IDString) #print ("entropy",entropy) #print ("cud",cud) elif version == 6: # .kinf2018: identical to K4PC salt = str(0x6d8 * int(build)).encode('utf-8') + guid sp = GetUserName() + b'+@#$%+' + IDString passwd = encode(SHA256(sp), charMap5) key = PBKDF2(passwd, salt, count=10000, dkLen=0x400)[:32] #print ("salt",salt) #print ("sp",sp) #print ("passwd",passwd) #print ("key",key) # loop through the item records until all are processed while len(items) > 0: # get the first item record item = items.pop(0) # the first 32 chars of the first record of a group # is the MD5 hash of the key name encoded by charMap5 keyhash = item[0:32] keyname = b'unknown' # unlike K4PC the keyhash is not used in generating entropy # entropy = SHA1(keyhash) + added_entropy # entropy = added_entropy # the remainder of the first record when decoded with charMap5 # has the ':' split char followed by the string representation # of the number of records that follow # and make up the contents srcnt = decode(item[34:], charMap5) rcnt = int(srcnt) # read and store in rcnt records of data # that make up the contents value edlst = [] for i in range(rcnt): item = items.pop(0) edlst.append(item) keyname = b'unknown' for name in names: if encodeHash(name, testMap8) == keyhash: keyname = name break if keyname == b'unknown': keyname = keyhash # the testMap8 encoded contents data has had a length # of chars (always odd) cut off of the front and moved # to the end to prevent decoding using testMap8 from # working properly, and thereby preventing the ensuing # CryptUnprotectData call from succeeding. # The offset into the testMap8 encoded contents seems to be: # len(contents) - largest prime number less than or equal to int(len(content)/3) # (in other words split 'about' 2/3rds of the way through) # move first offsets chars to end to align for decode by testMap8 encdata = b''.join(edlst) contlen = len(encdata) # now properly split and recombine # by moving noffset chars from the start of the # string to the end of the string noffset = contlen - primes(int(contlen / 3))[-1] pfx = encdata[0:noffset] encdata = encdata[noffset:] encdata = encdata + pfx if version == 5: # decode using testMap8 to get the CryptProtect Data encryptedValue = decode(encdata, testMap8) cleartext = cud.decrypt(encryptedValue) elif version == 6: # decode using new testMap8 to get IV + ciphertext iv_ciphertext = decode(encdata, testMap8) # pad IV so that we can substitute AES-CTR for GCM iv = iv_ciphertext[:12] + b'\x00\x00\x00\x02' ciphertext = iv_ciphertext[12:] # convert IV to int for use with pycrypto iv_ints = unpack('>QQ', iv) iv = iv_ints[0] << 64 | iv_ints[1] # set up AES-CTR ctr = Counter.new(128, initial_value=iv) cipher = AES.new(key, AES.MODE_CTR, counter=ctr) # decrypt and decode cleartext = decode(cipher.decrypt(ciphertext), charMap5) # print keyname # print cleartext if len(cleartext) > 0: DB[keyname] = cleartext if len(DB) > 6: break except Exception: print(traceback.format_exc()) pass if len(DB) > 6: # store values used in decryption print( "Decrypted key file using IDString '{0:s}' and UserName '{1:s}'" .format(IDString.decode('utf-8'), GetUserName().decode('utf-8'))) DB[b'IDString'] = IDString DB[b'UserName'] = GetUserName() else: print("Couldn't decrypt file.") DB = {} return DB
def generateCipher(AESkey, iv): return AES.new(AESkey, AES.MODE_GCM, iv)
def test_invalid_decrypt_after_final(self): cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.update(self.data_128) cipher.decrypt(self.data_128) cipher.decrypt() self.assertRaises(TypeError, cipher.decrypt, self.data_128)
def parse_xiaomi(self, data, xiaomi_index, is_ext_packet): # parse BLE message in Xiaomi MiBeacon format firmware = "Xiaomi (MiBeacon)" # check for no BR/EDR + LE General discoverable mode flags advert_start = 29 if is_ext_packet else 14 adv_index = data.find(b"\x02\x01\x06", advert_start, 3 + advert_start) adv_index2 = data.find(b"\x15\x16\x95", advert_start, 3 + advert_start) if adv_index == -1 and adv_index2 == -1: raise NoValidError("Invalid index") if adv_index2 != -1: adv_index = adv_index2 # check for BTLE msg size msg_length = data[2] + 3 if msg_length != len(data): raise NoValidError("Invalid msg size") # extract device type device_type = data[xiaomi_index + 5:xiaomi_index + 7] # extract frame control bits framectrl_data = data[xiaomi_index + 3:xiaomi_index + 5] framectrl, = struct.unpack('>H', framectrl_data) # flag advertisements without mac address in service data if device_type == b'\xF6\x07' and framectrl_data == b'\x48\x59': # MJYD02YL does not have a MAC address in the service data of some advertisements mac_in_service_data = False elif device_type == b'\xDD\x03' and framectrl_data == b'\x40\x30': # MUE4094RT does not have a MAC address in the service data mac_in_service_data = False else: mac_in_service_data = True # check for MAC presence in message and in service data mac_index = adv_index - 14 if is_ext_packet else adv_index if mac_in_service_data is True: xiaomi_mac_reversed = data[xiaomi_index + 8:xiaomi_index + 14] source_mac_reversed = data[mac_index - 7:mac_index - 1] if xiaomi_mac_reversed != source_mac_reversed: raise NoValidError("Invalid MAC address") else: # for sensors without mac in service data, use the first mac in advertisment xiaomi_mac_reversed = data[mac_index - 7:mac_index - 1] # check for MAC presence in whitelist, if needed if self.discovery is False and xiaomi_mac_reversed not in self.whitelist: return None, None, None packet_id = data[xiaomi_index + 7] try: prev_packet = self.lpacket_ids[xiaomi_mac_reversed] except KeyError: # start with empty first packet prev_packet = None, None, None if prev_packet == packet_id: # only process new messages return None, None, None self.lpacket_ids[xiaomi_mac_reversed] = packet_id # extract RSSI byte rssi_index = 18 if is_ext_packet else msg_length - 1 (rssi,) = struct.unpack("<b", data[rssi_index:rssi_index + 1]) # strange positive RSSI workaround if rssi > 0: rssi = -rssi try: sensor_type, binary_data = XIAOMI_TYPE_DICT[device_type] except KeyError: if self.report_unknown: _LOGGER.info( "BLE ADV from UNKNOWN: RSSI: %s, MAC: %s, ADV: %s", rssi, ''.join('{:02X}'.format(x) for x in xiaomi_mac_reversed[::-1]), data.hex() ) raise NoValidError("Device unkown") # check data is present if not (framectrl & 0x4000): return { "rssi": rssi, "mac": ''.join('{:02X}'.format(x) for x in xiaomi_mac_reversed[::-1]), "type": sensor_type, "packet": packet_id, "firmware": firmware, "data": False, }, None, None xdata_length = 0 xdata_point = 0 # check capability byte present if framectrl & 0x2000: xdata_length = -1 xdata_point = 1 # check for messages without mac address in service data if mac_in_service_data is False: xdata_length = +6 xdata_point = -6 # parse_xiaomi data length = message length # -all bytes before XiaomiUUID # -3 bytes Xiaomi UUID + ADtype # -1 byte rssi # -3+1 bytes sensor type # -1 byte packet_id # -6 bytes MAC (if present) # -capability byte offset xdata_length += msg_length - xiaomi_index - 15 if xdata_length < 3: raise NoValidError("Xdata length invalid") xdata_point += xiaomi_index + 14 # check if parse_xiaomi data start and length is valid if xdata_length != len(data[xdata_point:-1]): raise NoValidError("Invalid data length") # check encrypted data flags if framectrl & 0x0800: # try to find encryption key for current device try: key = self.aeskeys[xiaomi_mac_reversed] except KeyError: # no encryption key found raise NoValidError("No encryption key found") nonce = b"".join( [ xiaomi_mac_reversed, device_type, data[xiaomi_index + 7:xiaomi_index + 8] ] ) endoffset = msg_length - int(not is_ext_packet) encrypted_payload = data[xdata_point:endoffset] aad = b"\x11" token = encrypted_payload[-4:] payload_counter = encrypted_payload[-7:-4] nonce = b"".join([nonce, payload_counter]) cipherpayload = encrypted_payload[:-7] cipher = AES.new(key, AES.MODE_CCM, nonce=nonce, mac_len=4) cipher.update(aad) try: decrypted_payload = cipher.decrypt_and_verify(cipherpayload, token) except ValueError as error: _LOGGER.error("Decryption failed: %s", error) _LOGGER.error("token: %s", token.hex()) _LOGGER.error("nonce: %s", nonce.hex()) _LOGGER.error("encrypted_payload: %s", encrypted_payload.hex()) _LOGGER.error("cipherpayload: %s", cipherpayload.hex()) raise NoValidError("Error decrypting with arguments") if decrypted_payload is None: _LOGGER.error( "Decryption failed for %s, decrypted payload is None", "".join("{:02X}".format(x) for x in xiaomi_mac_reversed[::-1]), ) raise NoValidError("Decryption failed") # replace cipher with decrypted data msg_length -= len(encrypted_payload) if is_ext_packet: data = b"".join((data[:xdata_point], decrypted_payload)) else: data = b"".join((data[:xdata_point], decrypted_payload, data[-1:])) msg_length += len(decrypted_payload) result = { "rssi": rssi, "mac": ''.join('{:02X}'.format(x) for x in xiaomi_mac_reversed[::-1]), "type": sensor_type, "packet": packet_id, "firmware": firmware, "data": True, } binary = False measuring = False # loop through parse_xiaomi payload # assume that the data may have several values of different types, # although I did not notice this behavior with my LYWSDCGQ sensors while True: xvalue_typecode = data[xdata_point:xdata_point + 2] try: xvalue_length = data[xdata_point + 2] except ValueError as error: _LOGGER.error("xvalue_length conv. error: %s", error) _LOGGER.error("xdata_point: %s", xdata_point) _LOGGER.error("data: %s", data.hex()) result = {} break except IndexError as error: _LOGGER.error("Wrong xdata_point: %s", error) _LOGGER.error("xdata_point: %s", xdata_point) _LOGGER.error("data: %s", data.hex()) result = {} break xnext_point = xdata_point + 3 + xvalue_length xvalue = data[xdata_point + 3:xnext_point] resfunc, tbinary, tmeasuring = self._dataobject_dict.get(xvalue_typecode, (None, None, None)) if resfunc: binary = binary or tbinary measuring = measuring or tmeasuring result.update(resfunc(xvalue)) else: if self.report_unknown: _LOGGER.info( "UNKNOWN dataobject from DEVICE: %s, MAC: %s, ADV: %s", sensor_type, ''.join('{:02X}'.format(x) for x in xiaomi_mac_reversed[::-1]), data.hex() ) if xnext_point > msg_length - 3: break xdata_point = xnext_point binary = binary and binary_data return result, binary, measuring
def test_block_size_128(self): cipher = AES.new(self.key_128, self.aes_mode, self.iv_128) self.assertEqual(cipher.block_size, AES.block_size)
def test_block_size_128(self): cipher = AES.new(self.key_128, AES.MODE_EAX, nonce=self.nonce_96) self.assertEqual(cipher.block_size, AES.block_size)
def encrypt(self, message, key, key_size=256): message = self.pad(message) iv = Random.new().read(AES.block_size) cipher = AES.new(key, AES.MODE_CBC, iv) return iv + cipher.encrypt(message)
def decrypt(self, ciphertext, key): iv = ciphertext[:AES.block_size] cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext[AES.block_size:]) return plaintext.rstrip(b"\0")
def getDBfromFile(kInfoFile): names = [\ b'kindle.account.tokens',\ b'kindle.cookie.item',\ b'eulaVersionAccepted',\ b'login_date',\ b'kindle.token.item',\ b'login',\ b'kindle.key.item',\ b'kindle.name.info',\ b'kindle.device.info',\ b'MazamaRandomNumber',\ b'max_date',\ b'SIGVERIF',\ b'build_version',\ b'SerialNumber',\ b'UsernameHash',\ b'kindle.directedid.info',\ b'DSN',\ b'kindle.accounttype.info',\ b'krx.flashcardsplugin.data.encryption_key',\ b'krx.notebookexportplugin.data.encryption_key',\ b'proxy.http.password',\ b'proxy.http.username' ] namehashmap = {encodeHash(n, testMap8): n for n in names} # print(namehashmap) DB = {} with open(kInfoFile, 'rb') as infoReader: data = infoReader.read() # assume .kinf2011 or .kinf2018 style .kinf file # the .kinf file uses "/" to separate it into records # so remove the trailing "/" to make it easy to use split data = data[:-1] items = data.split(b'/') # starts with an encoded and encrypted header blob headerblob = items.pop(0) encryptedValue = decode(headerblob, testMap1) cleartext = UnprotectHeaderData(encryptedValue) #print "header cleartext:",cleartext # now extract the pieces that form the added entropy pattern = re.compile( br'''\[Version:(\d+)\]\[Build:(\d+)\]\[Cksum:([^\]]+)\]\[Guid:([\{\}a-z0-9\-]+)\]''', re.IGNORECASE) for m in re.finditer(pattern, cleartext): version = int(m.group(1)) build = m.group(2) guid = m.group(4) if version == 5: # .kinf2011 added_entropy = build + guid elif version == 6: # .kinf2018 salt = str(0x6d8 * int(build)).encode('utf-8') + guid sp = GetUserName() + b'+@#$%+' + GetIDString().encode('utf-8') passwd = encode(SHA256(sp), charMap5) key = PBKDF2(passwd, salt, count=10000, dkLen=0x400)[:32] # this is very slow # loop through the item records until all are processed while len(items) > 0: # get the first item record item = items.pop(0) # the first 32 chars of the first record of a group # is the MD5 hash of the key name encoded by charMap5 keyhash = item[0:32] # the remainder of the first record when decoded with charMap5 # has the ':' split char followed by the string representation # of the number of records that follow # and make up the contents srcnt = decode(item[34:], charMap5) rcnt = int(srcnt) # read and store in rcnt records of data # that make up the contents value edlst = [] for i in range(rcnt): item = items.pop(0) edlst.append(item) # key names now use the new testMap8 encoding if keyhash in namehashmap: keyname = namehashmap[keyhash] #print "keyname found from hash:",keyname else: keyname = keyhash #print "keyname not found, hash is:",keyname # the testMap8 encoded contents data has had a length # of chars (always odd) cut off of the front and moved # to the end to prevent decoding using testMap8 from # working properly, and thereby preventing the ensuing # CryptUnprotectData call from succeeding. # The offset into the testMap8 encoded contents seems to be: # len(contents)-largest prime number <= int(len(content)/3) # (in other words split "about" 2/3rds of the way through) # move first offsets chars to end to align for decode by testMap8 # by moving noffset chars from the start of the # string to the end of the string encdata = b"".join(edlst) #print "encrypted data:",encdata contlen = len(encdata) noffset = contlen - primes(int(contlen / 3))[-1] pfx = encdata[0:noffset] encdata = encdata[noffset:] encdata = encdata + pfx #print "rearranged data:",encdata if version == 5: # decode using new testMap8 to get the original CryptProtect Data encryptedValue = decode(encdata, testMap8) #print "decoded data:",encryptedValue.encode('hex') entropy = SHA1(keyhash) + added_entropy cleartext = CryptUnprotectData(encryptedValue, entropy, 1) elif version == 6: # decode using new testMap8 to get IV + ciphertext iv_ciphertext = decode(encdata, testMap8) # pad IV so that we can substitute AES-CTR for GCM iv = iv_ciphertext[:12] + b'\x00\x00\x00\x02' ciphertext = iv_ciphertext[12:] # convert IV to int for use with pycrypto iv_ints = unpack('>QQ', iv) iv = iv_ints[0] << 64 | iv_ints[1] # set up AES-CTR ctr = Counter.new(128, initial_value=iv) cipher = AES.new(key, AES.MODE_CTR, counter=ctr) # decrypt and decode cleartext = decode(cipher.decrypt(ciphertext), charMap5) if len(cleartext) > 0: #print "cleartext data:",cleartext,":end data" DB[keyname] = cleartext #print keyname, cleartext if len(DB) > 6: # store values used in decryption DB[b'IDString'] = GetIDString().encode('utf-8') DB[b'UserName'] = GetUserName() print( "Decrypted key file using IDString '{0:s}' and UserName '{1:s}'" .format(GetIDString(), GetUserName().decode('utf-8'))) else: print("Couldn't decrypt file.") DB = {} return DB
def decrypt_file(metadata, encryption_material, in_filename, chunk_size=block_size * 4 * 1024, tmp_dir=None): """ Decrypts a file and stores the output in the temporary directory :param metadata: metadata input :param encryption_material: encryption material :param in_filename: input file name :param chunk_size: read chunk size :param tmp_dir: temporary directory, optional :return: a decrypted file name """ logger = getLogger(__name__) use_openssl_only = os.getenv('SF_USE_OPENSSL_ONLY', 'False') == 'True' key_base64 = metadata.key iv_base64 = metadata.iv decoded_key = base64.standard_b64decode( encryption_material.query_stage_master_key) key_bytes = base64.standard_b64decode(key_base64) iv_bytes = base64.standard_b64decode(iv_base64) if not use_openssl_only: key_cipher = AES.new(key=decoded_key, mode=AES.MODE_ECB) file_key = PKCS5_UNPAD(key_cipher.decrypt(key_bytes)) data_cipher = AES.new(key=file_key, mode=AES.MODE_CBC, IV=iv_bytes) else: backend = default_backend() cipher = Cipher(algorithms.AES(decoded_key), modes.ECB(), backend=backend) decryptor = cipher.decryptor() file_key = PKCS5_UNPAD( decryptor.update(key_bytes) + decryptor.finalize()) cipher = Cipher(algorithms.AES(file_key), modes.CBC(iv_bytes), backend=backend) decryptor = cipher.decryptor() temp_output_fd, temp_output_file = tempfile.mkstemp( text=False, dir=tmp_dir, prefix=os.path.basename(in_filename) + "#") total_file_size = 0 prev_chunk = None logger.debug(u'encrypted file: %s, tmp file: %s', in_filename, temp_output_file) with open(in_filename, u'rb') as infile: with os.fdopen(temp_output_fd, u'wb') as outfile: while True: chunk = infile.read(chunk_size) if len(chunk) == 0: break total_file_size += len(chunk) if not use_openssl_only: d = data_cipher.decrypt(chunk) else: d = decryptor.update(chunk) outfile.write(d) prev_chunk = d if prev_chunk is not None: total_file_size -= PKCS5_OFFSET(prev_chunk) if use_openssl_only: outfile.write(decryptor.finalize()) outfile.truncate(total_file_size) return temp_output_file
def __init__(self, km200_host, gateway_password, private_password): self.km200_host = km200_host self.cipher = AES.new( self.create_decryption_key(gateway_password, private_password), AES.MODE_ECB) self.pool_manager = urllib3.PoolManager()
def decrypt_key(p_encrypted_key): cipher = AES.new(K_PRIM, AES.MODE_ECB) print(">>Key was decrypted") return cipher.decrypt(p_encrypted_key)
def q3_break_cbc_mac(): """Question 3: Break CBC-MAC if used as a hash function In this question, You will show that CBC-MAC will fail catastrophically if used as a hash function rather than as a MAC. To explain the underlying issue, I reproduce a quote from the blog of Prof. Matt Green at Johns Hopkins: "Cryptographic hash functions are public functions (i.e., no secret key) that have the property of collision-resistance (it's hard to find two messages with the same hash). MACs are keyed functions that (typically) provide message unforgeability -- a very different property. Moreover, they guarantee this only when the key is secret." Let's make a (broken) hash function using CBC-MAC with a hardcoded key 'very secret key!' K = 'very secret key!' # key is fixed and public def hash_from_cbcmac(M): return cbcmac(K, M) # run CBC-MAC using this key Your Task: To solve this question, you probably need to revisit your CBC-MAC implementation from the previous lab and modify it to use AES (ECB-mode) as the block cipher (instead of `TOY`). Then, show that the hash function is broken by finding two colliding messages. Hmm, wait, that's too easy, and this is the final question of the lab. So, let me make the question harder in a couple of ways. First, rather than letting you choose the two messages, I'm going to fix one of them for you. Ergo, you'll break the **second preimage resistance** game rather than the **collision resistance** game. Specifically: I'm going to choose the following message: `msg = 'print("CBC-MAC is a very strong hash function!")'` Second, I want you to produce a collision that actually has some semantic value. Observe that the original message is a valid Python3 program that can be executed via the command `exec(msg)`. I want your collision to do the same. Your output: A string `collision` that simultaneously satisfies the following 5 properties. 1. The `collision` string is exactly 3 blocks (aka 48 bytes) long. 2. Its hash equals the following: `hash_from_cbcmac(msg) == hash_from_cbcmac(collision)`. 3. The `collision` string contains only printable ASCII characters (i.e., characters in `string.printable`). 4. The `collision` string is valid Python3 syntax. 5. When executed via the command `exec(collision)`, the following text is printed to the terminal: "CBC-MAC not a hash" If you cannot find a string that simultaneously satisfies all 5 properties, then simply go as far down the list of properties as you can for partial credit. Because there are multiple valid answers to this question, I cannot provide any test vectors to you. Note that you only need to find **one** valid answer. """ key = b'very secret key!' message = 'print("CBC-MAC is a very strong hash function!")' blocksToEncrypt = [message[i:i + 16] for i in range(0, len(message), 16)] cipherTexts = [] intermediate_states = [] cobj = AES.new(key, AES.MODE_ECB) x = cobj.encrypt(blocksToEncrypt[0].encode()) x = x.hex() cipherTexts.append(x) for byte in blocksToEncrypt[1:]: y = strxor(unhexlify(x), byte.encode('ascii')).hex() intermediate_states.append(y) x = cobj.encrypt(unhexlify(y)) x = x.hex() cipherTexts.append(x) X = bytes.fromhex(intermediate_states[1]) while (True): randomMessage = message_generator(4) collisionMessage = "print('CBC-MAC not a hash')#" collisionMessage += randomMessage collision_blocks = [ collisionMessage[i:i + 16] for i in range(0, len(collisionMessage), 16) ] col_x = cobj.encrypt(collision_blocks[0].encode()) col_y = strxor(col_x, collision_blocks[1].encode('ascii')) col_x = cobj.encrypt(col_y) Y = col_x message3 = strxor(X, Y) if isPrintable(message3): #print (randomMessage) #print (message3.decode('ascii')) message = collisionMessage + message3.decode('ascii') if is_valid_python(message): return message
def test_valid_init_digest(self): # Verify path INIT->DIGEST cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) cipher.digest()
def encrypt(k, s): """ Implement an Enc function in the paper. k: the key list generated by key_gen s: the original texts return: ciphertext tuple (d, c, l) """ # build a tree upon the string s tree = SuffixTree(s) kd = k[0] kc = k[1] kl = k[2] k1 = k[3] k2 = k[4] k3 = k[5] k4 = k[6] d = {} nodes = [] tree.get_nodes(tree.root, nodes) nodes.sort(key=operator.attrgetter('edge_label')) for node in nodes: children = [] g2 = [] for child in node.children: children.append(child) h2 = hmac.new(key=k2, digestmod=hashlib.sha256) h2.update(child.get_initial_path().encode('utf-8')) g2.append(h2.digest()) for i in range(len(children), 128): h = hmac.new(key=secrets.token_bytes(LAMBDA), digestmod=hashlib.sha256) g2.append(h.digest()) piu = [i for i in range(0, 128)] random.Random( hashlib.blake2b(key=secrets.token_bytes(LAMBDA), digest_size=LAMBDA)).shuffle(piu) f2 = [i for i in range(0, 128)] for i in range(0, 128): f2[i] = g2[piu[i]] h1 = hmac.new(key=k1, digestmod=hashlib.sha256) h1.update(node.get_initial_path().encode('utf-8')) f1 = h1.digest() xu = str( node.get_ind()) + '$' + str(tree.get_leafpos(node)) + '$' + str( tree.get_num(node)) + '$' + str( node.get_len()) + '$' + f1.hex() for i in range(0, 128): xu += '$' + f2[i].hex() iv = Random.new().read(AES.block_size) cipher = AES.new(kd, AES.MODE_CFB, iv) wu = iv.hex() + '$' + cipher.encrypt(xu.encode("utf8")).hex() vu = '' for i in range(0, 128): vu += f2[i].hex() + '$' vu += wu d[f1] = vu for i in range(0, 2 * len(s) - len(nodes)): dummy_string = [] for j in range(0, 129): h = hashlib.blake2b(key=secrets.token_bytes(LAMBDA), digest_size=LAMBDA) dummy_string.append(h.digest()) iv = Random.new().read(AES.block_size) cipher = AES.new(kd, AES.MODE_CFB, iv) enc0 = iv.hex() + '$' + cipher.encrypt(str(0).encode("utf8")).hex() dummy = '' for i in range(0, 128): dummy += dummy_string[i].hex() + '$' dummy += enc0 d[dummy_string[128]] = dummy c = [i for i in range(0, len(s))] p = [i for i in range(0, len(s))] random.seed(k3) random.shuffle(p) for i in range(0, len(s)): iv = Random.new().read(AES.block_size) cipher = AES.new(kc, AES.MODE_CFB, iv) c[p[i]] = iv.hex() + '$' + cipher.encrypt( (s[i] + '$' + str(i)).encode("utf8")).hex() l = [i for i in range(0, len(s))] p = [i for i in range(0, len(s))] random.seed(k4) random.shuffle(p) for i in range(0, len(s)): iv = Random.new().read(AES.block_size) cipher = AES.new(kl, AES.MODE_CFB, iv) l[p[i]] = iv.hex() + '$' + cipher.encrypt( (str(tree.leaves[i].get_ind()) + '$' + str(i)).encode("utf8")).hex() return (d, c, l)
def activation_finalyze(self, random_bytes=None): R = self.get_r() params = { "action": "ActionFinalize", "mode": self.mode, "id": self.data.iwid, "lastsync": self.data.iwTsync, "version": "Generator-1.0/0.2.11", "lang": "fr", "ack": "", "macid": self.macid } if self.mode == Otp.OTP_MODE: params.update({"keytype": '0', "sid": self.data.iwsecid}) elif self.mode == Otp.ACTIVATE_MODE: kma_crypt = self.cipher.encrypt( bytes.fromhex(self.generate_kma(self.codepin))).hex() pin_crypt = self.cipher.encrypt(self.codepin.encode("utf-8")).hex() params.update({ "serial": self.get_serial(), "code": self.smsCode, "Kma": kma_crypt, "pin": pin_crypt, "name": "Android SDK built for x86_64 / UNKNOWN", }) params.update(R) xml = self.request(params) if xml["err"] != "OK": logger.error("Error during activation: %s", xml) return Otp.NOK self.data.synchro(xml, self.generate_kma(self.codepin)) if self.mode == Otp.OTP_MODE: try: self.defi = str(xml["defi"]) except KeyError: raise ConfigException if "J" in xml: logger.debug("Need another otp request") return Otp.OTP_TWICE return Otp.OK if "ms_n" not in xml or xml["ms_n"] == 0: logger.debug("no ms_n request needed") return Otp.OK if int(xml["ms_n"]) > 1: raise NotImplementedError ms_n = "0" self.challenge = xml["challenge"] self.action = "synchro" res = self.decode_oaep(xml["ms_key"], self.Kfact) temp_key = RSA.construct((int(res, 16), self.exponent)) temp_cipher = oaep.new(temp_key, hash_algo=Hash.SHA256) if random_bytes is None: random_bytes = token_bytes(16) kpub_encode = temp_cipher.encrypt(random_bytes) aes_cipher = AES.new(bytes.fromhex(self.generate_kma(self.codepin)), AES.MODE_ECB) encode_aes_from_hex = aes_cipher.encrypt(random_bytes).hex() self.data.iwsecval = encode_aes_from_hex self.data.iwsecid = xml["s_id"] self.data.iwsecn = 1 req_param = { "action": "ActionFinalize", "mode": Otp.MS_MODE, "ms_id" + ms_n: xml["ms_id"], "ms_val" + ms_n: kpub_encode.hex(), "macid": self.macid } req_param.update({ "id": self.data.iwid, "lastsync": self.data.iwTsync, "ms_n": 1 }) req_param.update(self.get_r()) xml = self.request(req_param) self.data.synchro(xml, self.generate_kma(self.codepin)) return Otp.OK
# AES案例 from Cryptodome.Cipher import AES # 16个字节的密码 key = b'1234567890123456' # 创建加密对象 cipher = AES.new(key, mode=AES.MODE_EAX) data = '我是心蓝'.encode() # encrytion msg = cipher.encrypt(data) print(msg) # 解密 过程 # 创建一个新的密码对象 cipher2 = AES.new(key, AES.MODE_EAX, nonce=cipher.nonce) res = cipher2.decrypt(msg) print(res.decode('utf-8')) ''' 文档参考:<https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html> '''
def test_data_must_be_bytes(self): cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) self.assertRaises(TypeError, cipher.encrypt, u'test1234567890-*') cipher = AES.new(self.key_128, AES.MODE_OCB, nonce=self.nonce_96) self.assertRaises(TypeError, cipher.decrypt, u'test1234567890-*')
def create_decryptor(self, key, sequence): if key.method != "AES-128": raise StreamError("Unable to decrypt cipher {0}", key.method) if not key.uri: raise StreamError("Missing URI to decryption key") if self.key_uri != key.uri: zoom_key = self.reader.stream.session.options.get("zoom-key") zuom_key = self.reader.stream.session.options.get("zuom-key") livecam_key = self.reader.stream.session.options.get("livecam-key") saw_key = self.reader.stream.session.options.get("saw-key") your_key = self.reader.stream.session.options.get("your-key") mama_key = self.reader.stream.session.options.get("mama-key") custom_uri = self.reader.stream.session.options.get("custom-uri") if zoom_key: uri = 'http://www.zoomtv.me/k.php?q=' + base64.urlsafe_b64encode( zoom_key + base64.urlsafe_b64encode(key.uri)) elif zuom_key: uri = 'http://www.zuom.xyz/k.php?q=' + base64.urlsafe_b64encode( zuom_key + base64.urlsafe_b64encode(key.uri)) elif livecam_key: h = urlparse.urlparse(urllib.unquote(livecam_key)).netloc q = urlparse.urlparse(urllib.unquote(livecam_key)).query uri = 'http://%s/kaes?q=' % h + base64.urlsafe_b64encode( q + base64.b64encode(key.uri)) elif saw_key: if 'foxsportsgo' in key.uri: _tmp = key.uri.split('/') uri = urljoin(saw_key, '/m/fream?p=' + _tmp[-4] + '&k=' + _tmp[-1]) elif 'nlsk.neulion' in key.uri: _tmp = key.uri.split('?') uri = urljoin(saw_key, '/m/stream?' + _tmp[-1]) elif 'nlsk' in key.uri: _tmp = key.uri.split('?') uri = 'http://bile.level303.club/m/stream?' + _tmp[-1] elif 'nhl.com' in key.uri: _tmp = key.uri.split('/') uri = urljoin(saw_key, '/m/streams?ci=' + _tmp[-3] + '&k=' + _tmp[-1]) else: uri = key.uri elif mama_key: if 'nlsk' in key.uri: _tmp = key.uri.split('&url=') uri = 'http://mamahd.in/nba?url=' + _tmp[-1] elif your_key: if 'mlb.com' in key.uri: _tmp = key.uri.split('?') uri = urljoin(your_key, '/mlb/get_key/' + _tmp[-1]) elif 'espn3/auth' in key.uri: _tmp = key.uri.split('?') uri = urljoin(your_key, '/ncaa/get_key/' + _tmp[-1]) elif 'nhl.com' in key.uri: _tmp = key.uri.split('nhl.com/') uri = urljoin(your_key, '/nhl/get_key/' + _tmp[-1]) else: uri = key.uri elif custom_uri: uri = custom_uri else: uri = key.uri #xbmc.log('[StreamLink_Proxy] using key uri %s'%str(uri)) res = self.session.http.get(uri, exception=StreamError, retries=self.retries, **self.reader.request_params) self.key_data = res.content self.key_uri = key.uri iv = key.iv or num_to_iv(sequence) # Pad IV if needed iv = b"\x00" * (16 - len(iv)) + iv if _android_ssl: return enc(self.key_data, iv) elif _oscrypto: return AES(self.key_data, iv) else: return AES.new(self.key_data, AES.MODE_CBC, iv)
import socket from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import pad, unpad key = '!!12345678900987654321!!12345678' iv = '1234567890123456' bufferSize = 1024 #TCP로 통신하도록 socket설정 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('localhost', 12345)) while True: #새로운 thread 생성할 때마다 AES의 값을 초기화하는 작업 필요(안그러면 충돌발생하여 추가 thread생성 시 오류 발생) cipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode()) decipher = AES.new(key.encode(), AES.MODE_CBC, iv.encode()) #화면에 출력하여 안내하는 interface input_data = input("SEND( type :q! to Quit) :") print("send : ", input_data) ###################################################### #암호화하여 전달(client -> server) #암호화1(pad처리) padded_data = pad(input_data.encode(), AES.block_size) #암호화2(pad된 데이터를 encrypt하여 암호화) encrypted_data = cipher.encrypt(padded_data)
def ecb_encode(text, key): cipher = AES.new(key, AES.MODE_ECB) return (cipher.encrypt(text))
def decrypt(enc, password): private_key = hashlib.sha256(password.encode("utf-8")).digest() enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new(private_key, AES.MODE_CBC, iv) return unpad(cipher.decrypt(enc[16:]))
def create_challenge_url(self, transaction_id, content_type, message, callback_url, callback_sms_number, use_compression=False, reset_url=False): """ creates a challenge url (looking like lseqr://chal/<base64string>) from a challenge dictionary as provided by Challanges.create_challenge in lib.challenge the version identifier of the challenge url is currently hardcoded to 1. """ serial = self.getSerial() if content_type is None: content_type = CONTENT_TYPE_FREE # ------------------------------------------------------------------- -- # sanity/format checks if content_type not in [ CONTENT_TYPE_PAIRING, CONTENT_TYPE_AUTH, CONTENT_TYPE_FREE ]: raise InvalidFunctionParameter( 'content_type', 'content_type must ' 'be CONTENT_TYPE_PAIRING, ' 'CONTENT_TYPE_AUTH or ' 'CONTENT_TYPE_FREE.') if content_type == CONTENT_TYPE_PAIRING and \ message != serial: raise InvalidFunctionParameter( 'message', 'message must be equal ' 'to serial in pairing mode') if content_type == CONTENT_TYPE_AUTH: if '@' not in message: raise InvalidFunctionParameter( 'message', 'For content type ' 'auth, message must have format ' '<login>@<server>') # ------------------------------------------------------------------- -- # after the lseqr://chal/ prefix the following data is encoded # in urlsafe base64: # --------------------------------------------------- # fields | version | user token id | R | ciphertext | MAC | # --------------------------------------------------- # | header | | EAX enc data | # --------------------------------------------------- # size | 1 | 4 | 32 | ? | 16 | # --------------------------------------------------- # r = urandom(32) R = calc_dh_base(r) user_token_id = self.getFromTokenInfo('user_token_id') data_header = struct.pack('<bI', QRTOKEN_VERSION, user_token_id) # the user public key is saved as base64 in # the token info since the byte format is # incompatible with the json backend. b64_user_public_key = self.getFromTokenInfo('user_public_key') user_public_key = b64decode(b64_user_public_key) ss = calc_dh(r, user_public_key) U1 = sha256(ss).digest() U2 = sha256(U1).digest() zerome(ss) skA = U1[0:16] skB = U2[0:16] nonce = U2[16:32] zerome(U1) zerome(U2) # ------------------------------------------------------------------- -- # create plaintext section # ------------------------------------------------------------------- -- # create the bitmap for flags flags = 0 if use_compression: flags |= CHALLENGE_HAS_COMPRESSION # FIXME: sizecheck for message, callback url, sms number # wiki specs are utf-8 byte length (without \0) if callback_url is not None: flags |= CHALLENGE_HAS_URL if callback_sms_number is not None: flags |= CHALLENGE_HAS_SMS_NUMBER if (content_type == CONTENT_TYPE_PAIRING): flags |= CHALLENGE_HAS_SIGNATURE if reset_url: flags |= CHALLENGE_SHOULD_RESET_URL flags |= CHALLENGE_HAS_SIGNATURE # ------------------------------------------------------------------- -- # generate plaintext header # ---------------------------------------------- # fields | content_type | flags | transaction_id | ... | # ---------------------------------------------- # size | 1 | 1 | 8 | ? | # ---------------------------------------------- transaction_id = transaction_id_to_u64(transaction_id) pt_header = struct.pack('<bbQ', content_type, flags, transaction_id) plaintext = pt_header # ------------------------------------------------------------------- -- # create data package # ------------------------------- # fields | header | message | NUL | ... | # ------------------------------- # size | 10 | ? | 1 | ? | # ------------------------------- data_package = b'' utf8_message = message.encode('utf8') # enforce max sizes specified by protocol if content_type == CONTENT_TYPE_FREE and len(utf8_message) > 511: raise ParameterError('message (encoded as utf8) can only be 511 ' 'characters long') elif content_type == CONTENT_TYPE_PAIRING and len(utf8_message) > 63: raise InvalidFunctionParameter( 'message', 'max string length ' '(encoded as utf8) is 511 for ' 'content type PAIRING') elif content_type == CONTENT_TYPE_AUTH and len(utf8_message) > 511: raise InvalidFunctionParameter( 'message', 'max string length ' '(encoded as utf8) is 511 for ' 'content type AUTH') data_package += utf8_message + b'\x00' # ------------------------------------------------------------------- -- # depending on function parameters add callback url # and/or callback sms number # ----------------------------------------------------- # fields | ... | callback url | NUL | callback sms | NUL | ... | # ----------------------------------------------------- # size | ? | ? | 1 | ? | 1 | ? | # ----------------------------------------------------- # ------------------------------------------------------------------- -- if callback_url is not None: utf8_callback_url = callback_url.encode('utf8') # enforce max url length as specified in protocol if len(utf8_callback_url) > 511: raise InvalidFunctionParameter( 'callback_url', 'max string ' 'length (encoded as utf8) is ' '511') data_package += utf8_callback_url + b'\x00' # ------------------------------------------------------------------- -- if callback_sms_number is not None: utf8_callback_sms_number = callback_sms_number.encode('utf8') if len(utf8_callback_sms_number) > 31: raise InvalidFunctionParameter( 'callback_sms_number', 'max string length (encoded ' 'as utf8) is 31') data_package += utf8_callback_sms_number + b'\x00' # ------------------------------------------------------------------- -- if use_compression: maybe_compressed_data_package = zlib.compress(data_package, 9) else: maybe_compressed_data_package = data_package # ------------------------------------------------------------------- -- # when content type is pairing the protocol specifies that # the server must send a hmac based signature with the # response sig = '' sec_obj = self._get_secret_object() if flags & CHALLENGE_HAS_SIGNATURE: hmac_message = nonce + pt_header + maybe_compressed_data_package sig = sec_obj.hmac_digest(data_input=hmac_message, bkey=self.server_hmac_secret, hash_algo=sha256) plaintext += sig # ------------------------------------------------------------------- -- plaintext += maybe_compressed_data_package # ------------------------------------------------------------------- -- user_message = nonce + pt_header + sig + data_package user_sig = sec_obj.hmac_digest(data_input=user_message, bkey=skB, hash_algo=sha256) # the user sig will be given as urlsafe base64 in the # challenge response. for this reasons (and because we # need to serialize it into json) we convert the user_sig # into this format. user_sig = encode_base64_urlsafe(user_sig) # ------------------------------------------------------------------- -- cipher = AES.new(skA, AES.MODE_EAX, nonce) cipher.update(data_header) ciphertext, tag = cipher.encrypt_and_digest(plaintext) raw_data = data_header + R + ciphertext + tag protocol_id = config.get('mobile_app_protocol_id', 'lseqr') url = protocol_id + '://chal/' + encode_base64_urlsafe(raw_data) return url, user_sig
def encrypt(raw, password): private_key = hashlib.sha256(password.encode("utf-8")).digest() raw = pad(raw) iv = Random.new().read(AES.block_size) cipher = AES.new(private_key, AES.MODE_CBC, iv) return base64.b64encode(iv + cipher.encrypt(raw))
def UnprotectHeaderData(encryptedData): passwdData = b'header_key_data' salt = b'HEADER.2011' key_iv = PBKDF2(passwdData, salt, dkLen=256, count=128) return AES.new(key_iv[0:32], AES.MODE_CBC, key_iv[32:48]).decrypt(encryptedData)
from Cryptodome.Cipher import AES from Cryptodome.Random import get_random_bytes """ https://www.pycryptodome.org/en/latest/src/examples.html """ ''' Encrypt data with AES The following code generates a new AES128 key and encrypts a piece of data into a file. We use the EAX mode because it allows the receiver to detect any unauthorized modification (similarly, we could have used other authenticated encryption modes like GCM, CCM or SIV). ''' data = b"hello" key = get_random_bytes(16) cipher = AES.new(key, AES.MODE_EAX) ciphertext, tag = cipher.encrypt_and_digest(data) print(key, cipher, ciphertext) file_out = open("encrypted.bin", "wb") [file_out.write(x) for x in (cipher.nonce, tag, ciphertext)] print(cipher.nonce, tag, ciphertext) file_out.close() ''' At the other end, the receiver can securely load the piece of data back (if they know the key!). Note that the code generates a ValueError exception when tampering is detected. ''' file_in = open("encrypted.bin", "rb") nonce, tag, ciphertext = [file_in.read(x) for x in (16, 16, -1)]