def test_initial_value_parameter(self): # Test with nonce parameter cipher1 = AES.new(self.key_128, AES.MODE_CTR, nonce=self.nonce_64, initial_value=0xFFFF) counter = Counter.new(64, prefix=self.nonce_64, initial_value=0xFFFF) cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) pt = get_tag_random("plaintext", 65536) self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) # Test without nonce parameter cipher1 = AES.new(self.key_128, AES.MODE_CTR, initial_value=0xFFFF) counter = Counter.new(64, prefix=cipher1.nonce, initial_value=0xFFFF) cipher2 = AES.new(self.key_128, AES.MODE_CTR, counter=counter) pt = get_tag_random("plaintext", 65536) self.assertEqual(cipher1.encrypt(pt), cipher2.encrypt(pt)) # Initial_value and Counter are mutually exclusive self.assertRaises(TypeError, AES.new, self.key_128, AES.MODE_CTR, counter=self.ctr_128, initial_value=0)
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_iv_with_matching_length(self): self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, counter=Counter.new(120)) self.assertRaises(ValueError, AES.new, self.key_128, AES.MODE_CTR, counter=Counter.new(136))
def write(self, path, data, offset, fh): if self.readonly: raise FuseOSError(errno.EPERM) # special check for special files if os.path.basename(path).startswith('.'): if windows: f = open(path, 'r+b', buffering=0) f.seek(offset) return f.write(data) else: with self.rwlock: os.lseek(fh, offset, 0) return os.write(fh, data) before = offset % 16 # after = (offset + size) % 16 counter = Counter.new(128, initial_value=self.path_to_iv(path) + (offset >> 4)) cipher = AES.new(self.key, AES.MODE_CTR, counter=counter) out_data = cipher.decrypt((b'\0' * before) + data)[before:] if windows: with open(path, 'r+b', buffering=0) as f: f.seek(offset - before) written = f.write(out_data) else: with self.rwlock: os.lseek(fh, offset, 0) written = os.write(fh, out_data) return written
def Enc_first(cheque): key = get_random_bytes(32) salt = get_random_bytes(16) ctr = Counter.new(128, initial_value=bytes_to_long(salt)) cipher = AES.new(key, AES.MODE_CTR, counter=ctr) cipher_text = cipher.encrypt(bytes(json.dumps(cheque), 'utf-8')) HMAC_FIRST = b"BBuXaXBdHg+wLPjRJpf3N/NmLq5kuvzGQx3II15/j8o=" mk = hmac.new(b64decode(HMAC_FIRST), salt + cipher_text, hashlib.sha512) dataDE = b64encode(mk.digest() + salt + cipher_text).decode('utf-8') recipient_key = RSA.import_key(open('1RSAcert.pem').read()) cipher_rsa = PKCS1_OAEP.new(recipient_key) dataAB = b64encode(cipher_rsa.encrypt(key)).decode('utf-8') # шифрованный запрос return { "ab": dataAB, "de": dataDE, "kassaid": "122772", "kassatoken": "5373167af64a6e02cae10c2bf5c6a7e4", "check_type": "standart", "test": "0" }
def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''): if init_value >= (1 << 96): raise InvalidInputException('IV should be 96-bit') if auth_tag >= (1 << 128): raise InvalidInputException('Tag should be 128-bit') if auth_tag != self.__ghash( auth_data, ciphertext) ^ bytes_to_long( self._aes_ecb.encrypt( long_to_bytes((init_value << 32) | 1, 16))): raise InvalidTagException len_ciphertext = len(ciphertext) if len_ciphertext > 0: counter = Counter.new( nbits=32, prefix=long_to_bytes(init_value, 12), initial_value=2, allow_wraparound=True) aes_ctr = AES.new(self._master_key, AES.MODE_CTR, counter=counter) if 0 != len_ciphertext % 16: padded_ciphertext = ciphertext + \ b'\x00' * (16 - len_ciphertext % 16) else: padded_ciphertext = ciphertext plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext] else: plaintext = b'' return plaintext
def create_ephIDs(SK): ''' Create the set of beacons for the day based on a new SK_t. This method created the set of ephemeral IDs given an SK for day / broadcast_key. Arguments: SK(b[]): given SK (b"rand" * 32) Returns: [b[]]: Set of ephemeral IDs (ephIDs) for a single day. ''' # Set up PRF with the SK and broadcast_key prf = hmac.new(SK, BROADCAST_KEY.encode(), hashlib.sha256).digest() # Start with a fresh counter each day and initialize AES in CTR mode prg = AES.new(prf, AES.MODE_CTR, counter=Counter.new(128, initial_value=0)) ephIDs = [] # Create the number of desired ephIDs by encrypting 0 bytes prg_data = prg.encrypt(b"\0" * 16 * NUM_EPOCHS_PER_DAY) for i in range(NUM_EPOCHS_PER_DAY): # split the prg data into individual ephIDs ephIDs.append(prg_data[i * 16:i * 16 + 16]) return ephIDs
def create_ctr_cipher(self, keyslot: Keyslot, ctr: int) -> 'Union[CtrMode, _TWLCryptoWrapper]': """ Create an AES-CTR cipher with the given keyslot. Normal and DSi crypto will be automatically chosen depending on keyslot. :param keyslot: :class:`Keyslot` to use. :param ctr: Counter to start with. :return: An AES-CTR cipher object from PyCryptodome, or a wrapper for DSi keyslots. :rtype: CtrMode | _TWLCryptoWrapper """ try: key = self.key_normal[keyslot] except KeyError: raise KeyslotMissingError( f'normal key for keyslot 0x{keyslot:02x} is not set up') cipher = AES.new(key, AES.MODE_CTR, counter=Counter.new(128, initial_value=ctr)) if keyslot < 0x04: return _TWLCryptoWrapper(cipher) else: return cipher
def decrypt(password, data): ''' Decrypt some data. Input must be bytes. @param password: The secret value used as the basis for a key. This should be as long as varied as possible. Try to avoid common words. @param data: The data to be decrypted, typically as bytes. @return: The decrypted data, as bytes. If the original message was a string you can re-create that using `result.decode('utf8')`. ''' _assert_not_unicode(data) _assert_header_prefix(data) version = _assert_header_version(data) _assert_decrypt_length(data, version) raw = data[HEADER_LEN:] salt = raw[:SALT_LEN[version] // 8] hmac_key, cipher_key = _expand_keys(password, salt, EXPANSION_COUNT[version]) hmac = raw[-HASH.digest_size:] hmac2 = _hmac(hmac_key, data[:-HASH.digest_size]) _assert_hmac(hmac_key, hmac, hmac2) counter = Counter.new(HALF_BLOCK, prefix=salt[:HALF_BLOCK // 8]) cipher = AES.new(cipher_key, AES.MODE_CTR, counter=counter) return cipher.decrypt(raw[SALT_LEN[version] // 8:-HASH.digest_size])
def decrypt(self, init_value, ciphertext, auth_tag, auth_data=b''): if init_value >= (1 << 96): raise InvalidInputException('IV should be 96-bit') if auth_tag >= (1 << 128): raise InvalidInputException('Tag should be 128-bit') if auth_tag != self.__ghash(auth_data, ciphertext) ^ bytes_to_long( self._aes_ecb.encrypt(long_to_bytes( (init_value << 32) | 1, 16))): raise InvalidTagException len_ciphertext = len(ciphertext) if len_ciphertext > 0: counter = Counter.new(nbits=32, prefix=long_to_bytes(init_value, 12), initial_value=2, allow_wraparound=True) aes_ctr = AES.new(self._master_key, AES.MODE_CTR, counter=counter) if 0 != len_ciphertext % 16: padded_ciphertext = ciphertext + \ b'\x00' * (16 - len_ciphertext % 16) else: padded_ciphertext = ciphertext plaintext = aes_ctr.decrypt(padded_ciphertext)[:len_ciphertext] else: plaintext = b'' return plaintext
def generate_ephids_for_day(current_day_key, shuffle=True): """Generates the list of EphIDs for the current day Args: key (byte array): A 32-byte key shuffle (bool, optional): Whether to shuffle the list of EphIDs. Default: True. Should only be set to False when testing or when generating test vectors Returns: list of byte arrays: The list of EphIDs for the day """ # Compute key for stream cipher based on current_day_key stream_key = hmac.new(current_day_key, BROADCAST_KEY, hashlib.sha256).digest() # Start with a fresh counter each day and initialize AES in CTR mode counter = Counter.new(128, initial_value=0) prg = AES.new(stream_key, AES.MODE_CTR, counter=counter) # Create the number of desired ephIDs by drawing from AES in CTR mode # operating a s a stream cipher. To get the raw output, we ask the library # to "encrypt" an all-zero message of sufficient length. prg_output_bytes = prg.encrypt(bytes(LENGTH_EPHID * NUM_EPOCHS_PER_DAY)) ephids = [ prg_output_bytes[idx:idx + LENGTH_EPHID] for idx in range(0, len(prg_output_bytes), LENGTH_EPHID) ] # Shuffle the resulting ephids if shuffle: secure_shuffle(ephids) return ephids
def read(self, path, size, offset, fh): # special check for special files if os.path.basename(path).startswith('.'): if windows: f = open(path, 'rb', buffering=0) f.seek(offset) return f.read(size) else: with self.rwlock: os.lseek(fh, offset, 0) return os.read(fh, size) before = offset % 16 after = (offset + size) % 16 # size_fix = size # if size % 16 != 0: # size_fix = size + 16 - size % 16 if windows: with open(path, 'rb', buffering=0) as f: f.seek(offset - before) data = f.read(size) else: with self.rwlock: os.lseek(fh, offset - before, 0) data = os.read(fh, size) counter = Counter.new(128, initial_value=self.path_to_iv(path) + (offset >> 4)) cipher = AES.new(self.key, AES.MODE_CTR, counter=counter) out_data = cipher.decrypt(data)[before:] return out_data
def get_DES(key, mode): # возвращает класс DES if mode == DES.MODE_CTR: print("Create counter") counter = Counter.new(nbits=64) return DES.new(key, mode, counter=counter) return DES.new(key, mode)
async def _encrypt_media( self, data: AsyncIterator[bytes], ) -> AsyncIterator[Union[bytes, EncryptedMediaInfo]]: aes256_key = Random.new().read(32) init_vector = Random.new().read(8) counter = Counter.new(64, prefix=init_vector, initial_value=0) cipher = AES.new(aes256_key, AES.MODE_CTR, counter=counter) sha256 = SHA256.new() async for chunk in data: encrypted = cipher.encrypt(chunk) sha256.update(encrypted) yield encrypted yield EncryptedMediaInfo( mxc=MXC("mxc://replace/me"), init_vector=encode_base64(init_vector + b"\x00" * 8), key=encode_base64(aes256_key, urlsafe=True), sha256=encode_base64(sha256.digest()), key_operations=["encrypt", "decrypt"], key_type="oct", key_algorithm="A256CTR", key_extractable=True, version="v2", )
def decrypt(key): if check_key(key): l = tkinter.Label( root, text="This is the correct key. \n" "Your files are being decrypted but it may take a while. Please wait..." ) l.pack() ctr = Counter.new(128) crypt = AES.new(key.encode(), AES.MODE_CTR, counter=ctr) startdirs = START_DIR for currentDir in startdirs: for file in discover.discoverFiles(currentDir): (name, ext) = os.path.splitext(file) if ext in '.Cryptsky': try: modify.modify_file_inplace(file, crypt.encrypt) os.rename(file, name) except IOError: print("Error") try: print() os.remove(r'C:\Windows\Temp\winUpdater.log') except FileNotFoundError: pass label = tkinter.Label( root, text="Congratulations. Your files are now decrypted") label.pack() else: return False
async def _decrypt_media( self, data: AsyncIterator[bytes], info: EncryptedMediaInfo, ) -> AsyncIterator[bytes]: try: wanted_sha256 = decode_base64(info.sha256) aes256_key = decode_base64(info.key) init_vector = decode_base64(info.init_vector) except BinAsciiError as e: raise err.MediaInvalidBase64(next(iter(e.args), "")) sha256 = SHA256.new() prefix = init_vector[:8] init_value = int.from_bytes(init_vector[8:], "big") counter = Counter.new(64, prefix=prefix, initial_value=init_value) try: cipher = AES.new(aes256_key, AES.MODE_CTR, counter=counter) except ValueError as e: raise err.MediaAESError(next(iter(e.args), "")) async for chunk in data: sha256.update(chunk) yield cipher.decrypt(chunk) got_sha256 = sha256.digest() if wanted_sha256 != got_sha256: raise err.MediaSHA256Mismatch(wanted_sha256, got_sha256)
def extract_files(wak, out_dir, extract=True): if extract: out_dir = out_dir + "/" print("[+] Extracting to {}".format(out_dir)) if not os.path.exists(out_dir): os.makedirs(out_dir) # multiprocessing this for f in wak.file_list: print("{:.50s}:[offs {:08X}][size {:08X}][pthl {:08X}]".format( f.path, f.offset, f.size, f.pathlen)) if extract: fdata = wak.datawak_contents[f.offset:f.offset + f.size] f_iv = wak.prng.badprng_get16(0x165EC8F + f.tblidx) c = Counter.new(128, initial_value=bytes_to_long(f_iv)) fdata_dec = AES.new(wak.prng.default_key, AES.MODE_CTR, counter=c).decrypt(fdata) out_filepath = os.path.abspath(out_dir + f.path) out_filedir = os.path.split(out_filepath)[0] if not os.path.exists(out_filedir): os.makedirs(out_filedir) with open(out_filepath, 'wb') as outfile: outfile.write(fdata_dec) print("[+] Complete, iterated {} files.".format(len(wak.file_list)))
def decrypt_ctr(key, cypherText): iv = cypherText[:block_size] ct1 = cypherText[block_size:] ctr = Counter.new(block_size * 8, initial_value=int(codecs.encode(iv, 'hex'), 16)) obj = AES.new(key, AES.MODE_CTR, counter=ctr) padded_str = obj.decrypt(ct1) return padded_str
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) # 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 get_each_aes(bytes_data, bytes_key, bytes_iv): results = [] # ECB res = AES.new(bytes_key, AES.MODE_ECB).encrypt(bytes_data) results.append(("ECB", res)) # no iv CTR res = AES.new(bytes_key, AES.MODE_CTR).encrypt(bytes_data) results.append(("CTR", res)) c = Counter.new(128, initial_value=bytes_to_long(bytes_iv)) res = AES.new(bytes_key, AES.MODE_CTR, counter=c).encrypt(bytes_data) results.append(("CTR-iv", res)) res = AES.new(bytes_key, AES.MODE_CBC, bytes_iv).encrypt(bytes_data) results.append(("CBC", res)) res = AES.new(bytes_key, AES.MODE_CFB, bytes_iv).encrypt(bytes_data) results.append(("CFB", res)) res = AES.new(bytes_key, AES.MODE_OFB, bytes_iv).encrypt(bytes_data) results.append(("OFB", res)) res = AES.new(bytes_key, AES.MODE_GCM, bytes_iv).encrypt(bytes_data) results.append(("GCM", res)) # SIV keylen is restricted. prob not SIV. # res = AES.new(bytes_key, AES.MODE_SIV, bytes_iv).encrypt(bytes_data) # results.append(("SIV", res)) # OCB takes a 15 byte nonce. don't think so. # res = AES.new(bytes_key, AES.MODE_OCB, bytes_iv).encrypt(bytes_data) # results.append(("OCB", res)) return results
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 mfa_decrypt_backup_tokens(backup_secret, file): """ Decrypts backed up MFA secrets from file, prints to stdout. """ with open(os.path.expanduser(file), 'r') as infile: data = infile.read() counter = Counter.new(128, initial_value=1337) cipher = AES.new(get_backup_key_digest(backup_secret), AES.MODE_CTR, counter=counter) return cipher.decrypt(base64.b64decode(data)).decode()
def __init__(self, key): self.key = key self.counter = Counter.new(128) if len(self.key) != 32: print("AES KEY ERROR, CIPHER INSECURE") print("REPORT THIS ERROR") print("YOU SHOULD ****NEVER**** SEE THIS") quit() self.aes_cipher = AES.new(self.key, AES.MODE_CTR, counter=self.counter)
def encrypt(n, key, data): sha256 = hashlib.sha256() sha256.update(key) digest = sha256.digest() key = digest[:16] print((n, [c for c in key], [c for c in data])) aes = AES.new(key, AES.MODE_CTR, counter=Counter.new(128, initial_value=n)) encrypted = aes.encrypt(data) return base64.b64encode(encrypted).decode()
def decrypt(key, inp, out): """ decrypt() @param key: AES-128 CENC key in bytes @param inp: Open input file @param out: Open output file """ with BufferedReader(inp) as reader: senc_boxes = deque() trun_boxes = deque() while reader.peek(1): box = Box.parse_stream(reader) fix_headers(box) for stsd_box in BoxUtil.find(box, b'stsz'): sample_size = stsd_box.sample_size if box.type == b'moof': senc_boxes.extend(BoxUtil.find(box, b'senc')) trun_boxes.extend(BoxUtil.find(box, b'trun')) elif box.type == b'mdat': senc_box = senc_boxes.popleft() trun_box = trun_boxes.popleft() clear_box = b'' with BytesIO(box.data) as box_bytes: for sample, sample_info in zip( senc_box.sample_encryption_info, trun_box.sample_info): counter = Counter.new(64, prefix=sample.iv, initial_value=0) cipher = AES.new(key, AES.MODE_CTR, counter=counter) if sample_size: cipher_bytes = box_bytes.read(sample_size) clear_box += cipher.decrypt(cipher_bytes) elif not sample.subsample_encryption_info: cipher_bytes = box_bytes.read( sample_info.sample_size) clear_box += cipher.decrypt(cipher_bytes) else: for subsample in sample.subsample_encryption_info: clear_box += box_bytes.read( subsample.clear_bytes) cipher_bytes = box_bytes.read( subsample.cipher_bytes) clear_box += cipher.decrypt(cipher_bytes) box.data = clear_box out.write(Box.build(box)) return
def test_wrap_around(self): counter = Counter.new(8, prefix=bchr(9) * 15) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) cipher.encrypt(bchr(9) * 16 * 255) self.assertRaises(OverflowError, cipher.encrypt, bchr(9) * 16) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) cipher.decrypt(bchr(9) * 16 * 255) self.assertRaises(OverflowError, cipher.decrypt, bchr(9) * 16)
def decryption(key, file_name): counter = Counter.new(128) d = AES.new(key, AES.MODE_CTR, counter=counter) with open(file_name, 'r+b') as f: block_size = 16 plaintext = f.read(block_size) while plaintext: f.seek(-len(plaintext), 1) f.write(d.decrypt(plaintext)) plaintext = f.read(block_size) os.rename(file_name, file_name.strip(".R"))
def prepare_decoder(self, offset): initial_value = self.initial_value + int(offset / 16) try: from Cryptodome.Cipher import AES from Cryptodome.Util import Counter self.decryptor = AES.new(self._file._client.a32_to_str(self.k), AES.MODE_CTR, counter=Counter.new( 128, initial_value=initial_value)) except: from Crypto.Cipher import AES from Crypto.Util import Counter self.decryptor = AES.new(self._file._client.a32_to_str(self.k), AES.MODE_CTR, counter=Counter.new( 128, initial_value=initial_value)) rest = offset - int(offset / 16) * 16 if rest: self.decode(str(0) * rest)
def rclone_encrypt_password(password): key = bytes([0x9c, 0x93, 0x5b, 0x48, 0x73, 0x0a, 0x55, 0x4d, 0x6b, 0xfd, 0x7c, 0x63, 0xc8, 0x86, 0xa9, 0x2b, 0xd3, 0x90, 0x19, 0x8e, 0xb8, 0x12, 0x8a, 0xfb, 0xf4, 0xde, 0x16, 0x2b, 0x8b, 0x95, 0xf6, 0x38]) iv = Random.new().read(AES.block_size) counter = Counter.new(128, initial_value=int(codecs.encode(iv, "hex"), 16)) cipher = AES.new(key, AES.MODE_CTR, counter=counter) encrypted = iv + cipher.encrypt(password.encode("utf-8")) return base64.urlsafe_b64encode(encrypted).decode("ascii").rstrip("=")
def decrypt(self, text: bytes): """ 解密函数 :param text: :return: """ text = base64.b64decode(text) countf = Counter.new(64, text[:8]) encrypto = AES.new(self.key, AES.MODE_CTR, counter=countf) plain_text = encrypto.decrypt(text[8:]) return plain_text.decode().rstrip('\0')
def test_nonce_attribute(self): # Nonce attribute is the prefix passed to Counter (DES3) cipher = DES3.new(self.key_192, DES3.MODE_CTR, counter=self.ctr_64) self.assertEqual(cipher.nonce, self.nonce_32) # Nonce attribute is the prefix passed to Counter (AES) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=self.ctr_128) self.assertEqual(cipher.nonce, self.nonce_64) # Nonce attribute is not defined if suffix is used in Counter counter = Counter.new(64, prefix=self.nonce_32, suffix=self.nonce_32) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) self.failIf(hasattr(cipher, "nonce"))
def encrypt(data): data = data.encode('utf8') def pad(x): return x + (PWENC_BLOCK_SIZE - len(x) % PWENC_BLOCK_SIZE) * PWENC_PADDING nonce = os.urandom(8) cipher = AES.new(PWEncService.get_secret(), AES.MODE_CTR, counter=Counter.new(64, prefix=nonce)) encoded = base64.b64encode(nonce + cipher.encrypt(pad(data))) return encoded.decode()
def test_wrap_around(self): # Counter is only 8 bits, so we can only encrypt/decrypt 256 blocks (=4096 bytes) counter = Counter.new(8, prefix=bchr(9) * 15) max_bytes = 4096 cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) cipher.encrypt(b'9' * max_bytes) self.assertRaises(OverflowError, cipher.encrypt, b'9') cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) self.assertRaises(OverflowError, cipher.encrypt, b'9' * (max_bytes + 1)) cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) cipher.decrypt(b'9' * max_bytes) self.assertRaises(OverflowError, cipher.decrypt, b'9') cipher = AES.new(self.key_128, AES.MODE_CTR, counter=counter) self.assertRaises(OverflowError, cipher.decrypt, b'9' * (max_bytes + 1))
def test_aes_256(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = '601ec313775789a5b7a7f504bbf3d228' +\ 'f443e3ca4d62b59aca84e990cacaf5c5' +\ '2b0930daa23de94ce87017ba2d84988d' +\ 'dfc9c58db67aada613c2dd08457941a6' key = '603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4' counter = Counter.new(nbits=16, prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), initial_value=0xfeff) key = unhexlify(key) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.decrypt(ciphertext), plaintext)
def test_aes_192(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = '1abc932417521ca24f2b0459fe7e6e0b' +\ '090339ec0aa6faefd5ccc2c6f4ce8e94' +\ '1e36b26bd1ebc670d1bd1d665620abf7' +\ '4f78a7f6d29809585a97daec58c6b050' key = '8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b' counter = Counter.new(nbits=16, prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), initial_value=0xfeff) key = unhexlify(key) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.decrypt(ciphertext), plaintext)
def test_aes_128(self): plaintext = '6bc1bee22e409f96e93d7e117393172a' +\ 'ae2d8a571e03ac9c9eb76fac45af8e51' +\ '30c81c46a35ce411e5fbc1191a0a52ef' +\ 'f69f2445df4f9b17ad2b417be66c3710' ciphertext = '874d6191b620e3261bef6864990db6ce' +\ '9806f66b7970fdff8617187bb9fffdff' +\ '5ae4df3edbd5d35e5b4f09020db03eab' +\ '1e031dda2fbe03d1792170a0f3009cee' key = '2b7e151628aed2a6abf7158809cf4f3c' counter = Counter.new(nbits=16, prefix=unhexlify('f0f1f2f3f4f5f6f7f8f9fafbfcfd'), initial_value=0xfeff) key = unhexlify(key) plaintext = unhexlify(plaintext) ciphertext = unhexlify(ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.encrypt(plaintext), ciphertext) cipher = AES.new(key, AES.MODE_CTR, counter=counter) self.assertEqual(cipher.decrypt(ciphertext), plaintext)
def encrypt(self, init_value, plaintext, auth_data=b''): if init_value >= (1 << 96): raise InvalidInputException('IV should be 96-bit') # a naive checking for IV reuse if init_value == self.prev_init_value: raise InvalidInputException('IV must not be reused!') self.prev_init_value = init_value len_plaintext = len(plaintext) # len_auth_data = len(auth_data) if len_plaintext > 0: counter = Counter.new( nbits=32, prefix=long_to_bytes(init_value, 12), initial_value=2, # notice this allow_wraparound=True) aes_ctr = AES.new(self._master_key, AES.MODE_CTR, counter=counter) if 0 != len_plaintext % 16: padded_plaintext = plaintext + \ b'\x00' * (16 - len_plaintext % 16) else: padded_plaintext = plaintext ciphertext = aes_ctr.encrypt(padded_plaintext)[:len_plaintext] else: ciphertext = b'' auth_tag = self.__ghash(auth_data, ciphertext) # print 'GHASH\t', hex(auth_tag) auth_tag ^= bytes_to_long(self._aes_ecb.encrypt( long_to_bytes((init_value << 32) | 1, 16))) # assert len(ciphertext) == len(plaintext) assert auth_tag < (1 << 128) return ciphertext, auth_tag
def test_iv(self): c = Counter.new(128, initial_value=2)
def generate_counter(cls, block_size, iv): ctr = Counter.new(block_size * 8, initial_value=int(binascii.hexlify(iv), 16)) return ctr
def decrypt_and_verify_challenge(self, challenge_url, action): """ Decrypts the data packed in the challenge url, verifies its content, returns the parsed data as a dictionary, calculates and returns the signature. The calling method must then send the signature back to the server. (The reason for this control flow is that the challenge data must be checked in different scenarios, e.g. when we have a pairing the data must be checked by the method that simulates the pairing) :param challenge_url: the challenge url as sent by the server :param action: a string identifier for the verification action (at the moment 'ACCEPT' or 'DENY') :returns: (challenge, signature) challenge has the keys * content_type - one of the three values CONTENT_TYPE_SIGNREQ, CONTENT_TYPE_PAIRING or CONTENT_TYPE_LOGIN) (all defined in this module) * transaction_id - used to identify the challenge on the server * callback_url (optional) - the url to which the challenge response should be set * user_token_id - used to identify the token in the user database for which this challenge was created depending on the content type additional keys are present * for CONTENT_TYPE_PAIRING: serial * for CONTENT_TYPE_SIGNREQ: message * for CONTENT_TYPE_LOGIN: login, host signature is the generated user signature used to respond to the challenge """ challenge_data_encoded = challenge_url[len(self.uri + '://chal/'):] challenge_data = decode_base64_urlsafe(challenge_data_encoded) # ------------------------------------------------------------------ -- # parse and verify header information in the # encrypted challenge data header = challenge_data[0:5] version, user_token_id = struct.unpack('<bI', header) self.assertEqual(version, CHALLENGE_URL_VERSION) # ------------------------------------------------------------------ -- # get token from client token database token = self.tokens[user_token_id] server_public_key = token['server_public_key'] # ------------------------------------------------------------------ -- # prepare decryption by seperating R from # ciphertext and server signature R = challenge_data[5:5 + 32] ciphertext = challenge_data[5 + 32:-64] server_signature = challenge_data[-64:] # check signature data = challenge_data[0:-64] crypto_sign_verify_detached(server_signature, data, server_public_key) # ------------------------------------------------------------------ -- # key derivation secret_key_dh = dsa_to_dh_secret(self.secret_key) ss = calc_dh(secret_key_dh, R) U = SHA256.new(ss).digest() sk = U[0:16] nonce = U[16:32] # ------------------------------------------------------------------ -- # decrypt and verify challenge nonce_as_int = int_from_bytes(nonce, byteorder='big') ctr = Counter.new(128, initial_value=nonce_as_int) cipher = AES.new(sk, AES.MODE_CTR, counter=ctr) plaintext = cipher.decrypt(ciphertext) # ------------------------------------------------------------------ -- # parse/check plaintext header # 1 - for content type # 8 - for transaction id # 8 - for time stamp offset = 1 + 8 + 8 pt_header = plaintext[0:offset] (content_type, transaction_id, _time_stamp) = struct.unpack('<bQQ', pt_header) transaction_id = u64_to_transaction_id(transaction_id) # ------------------------------------------------------------------ -- # prepare the parsed challenge data challenge = {} challenge['content_type'] = content_type # ------------------------------------------------------------------ -- # retrieve plaintext data depending on content_type if content_type == CONTENT_TYPE_PAIRING: serial, callback_url, __ = plaintext[offset:].split('\x00') challenge['serial'] = serial elif content_type == CONTENT_TYPE_SIGNREQ: message, callback_url, __ = plaintext[offset:].split('\x00') challenge['message'] = message elif content_type == CONTENT_TYPE_LOGIN: login, host, callback_url, __ = plaintext[offset:].split('\x00') challenge['login'] = login challenge['host'] = host # ------------------------------------------------------------------ -- # prepare the parsed challenge data challenge['callback_url'] = callback_url challenge['transaction_id'] = transaction_id challenge['user_token_id'] = user_token_id # calculate signature sig_base = ( struct.pack('<b', CHALLENGE_URL_VERSION) + b'%s\0' % action + server_signature + plaintext) sig = crypto_sign_detached(sig_base, self.secret_key) encoded_sig = encode_base64_urlsafe(sig) return challenge, encoded_sig
def runTest(self): for pt, ct, key, prefix in self.bindata: counter = Counter.new(32, prefix=prefix) cipher = AES.new(key, AES.MODE_CTR, counter=counter) result = cipher.encrypt(pt) self.assertEqual(hexlify(ct), hexlify(result))
def test_BE(self): """Big endian""" c = Counter.new(128) c = Counter.new(128, little_endian=False)
def test_LE(self): """Little endian""" c = Counter.new(128, little_endian=True)
def create_challenge_url(self, transaction_id, content_type, callback_url='', message=None, login=None, host=None): """ creates a challenge url (looking like lseqr://push/<base64string>), returns the url and the unencrypted challenge data :param transaction_id: The transaction id generated by LinOTP :param content_type: One of the types CONTENT_TYPE_SIGNREQ, CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN :param callback_url: callback url (optional), default is empty string :param message: the transaction message, that should be signed by the client. Only for content type CONTENT_TYPE_SIGNREQ :param login: the login name of the user. Only for content type CONTENT_TYPE_LOGIN :param host: hostname of the user. Only for content type CONTENT_TYPE_LOGIN :returns: tuple (challenge_url, sig_base), with challenge_url being the push url and sig_base the message, that is used for the client signature """ serial = self.getSerial() # ------------------------------------------------------------------- -- # sanity/format checks if content_type not in [CONTENT_TYPE_SIGNREQ, CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN]: raise InvalidFunctionParameter('content_type', 'content_type must ' 'be CONTENT_TYPE_SIGNREQ, ' 'CONTENT_TYPE_PAIRING or ' 'CONTENT_TYPE_LOGIN.') # ------------------------------------------------------------------- -- # after the lseqr://push/ prefix the following data is encoded # in urlsafe base64: # --------------------------------------------------- # fields | version | user token id | R | ciphertext | sign | # --------------------------------------------------- # | header | body | # --------------------------------------------------- # size | 1 | 4 | 32 | ? | 64 | # --------------------------------------------------- # # create header user_token_id = self.getFromTokenInfo('user_token_id') data_header = struct.pack('<bI', CHALLENGE_URL_VERSION, user_token_id) # ------------------------------------------------------------------- -- # create body r = urandom(32) R = calc_dh_base(r) b64_user_dsa_public_key = self.getFromTokenInfo('user_dsa_public_key') user_dsa_public_key = b64decode(b64_user_dsa_public_key) user_dh_public_key = dsa_to_dh_public(user_dsa_public_key) ss = calc_dh(r, user_dh_public_key) U = SHA256.new(ss).digest() zerome(ss) sk = U[0:16] nonce = U[16:32] zerome(U) # ------------------------------------------------------------------- -- # create plaintext section # ------------------------------------------------------------------- -- # generate plaintext header # ------------------------------------------------ # fields | content_type | transaction_id | timestamp | .. # ------------------------------------------------ # size | 1 | 8 | 8 | ? # ------------------------------------------------- transaction_id = transaction_id_to_u64(transaction_id) plaintext = struct.pack('<bQQ', content_type, transaction_id, int(time.time())) # ------------------------------------------------------------------- -- 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') # ------------------------------------------------------------------- -- # create data package depending on content type # ------------------------------------------------------------------- -- if content_type == CONTENT_TYPE_PAIRING: # ----------------------------------------- # fields | header | serial | NUL | callback | NUL | # ----------------------------------------- # size | 9 | ? | 1 | ? | 1 | # ----------------------------------------- utf8_serial = serial.encode('utf8') if len(utf8_serial) > 63: raise ValueError('serial (encoded as utf8) can only be 63 ' 'characters long') plaintext += utf8_serial + b'\00' + utf8_callback_url + b'\00' # ------------------------------------------------------------------- -- if content_type == CONTENT_TYPE_SIGNREQ: if message is None: raise InvalidFunctionParameter('message', 'message must be ' 'supplied for content type ' 'SIGNREQ') # ------------------------------------------ # fields | header | message | NUL | callback | NUL | # ------------------------------------------ # size | 9 | ? | 1 | ? | 1 | # ------------------------------------------ utf8_message = message.encode('utf8') # enforce max sizes specified by protocol if len(utf8_message) > 511: raise InvalidFunctionParameter('message', 'max string ' 'length (encoded as utf8) is ' '511') plaintext += utf8_message + b'\00' + utf8_callback_url + b'\00' # ------------------------------------------------------------------- -- if content_type == CONTENT_TYPE_LOGIN: if login is None: raise InvalidFunctionParameter('login', 'login must be ' 'supplied for content type ' 'LOGIN') if host is None: raise InvalidFunctionParameter('host', 'host must be ' 'supplied for content type ' 'LOGIN') # ----------------------------------------------------- # fields | header | login | NUL | host | NUL | callback | NUL | # ----------------------------------------------------- # size | 9 | ? | 1 | ? | 1 | ? | 1 | # ----------------------------------------------------- utf8_login = login.encode('utf8') utf8_host = host.encode('utf8') # enforce max sizes specified by protocol if len(utf8_login) > 127: raise InvalidFunctionParameter('login', 'max string ' 'length (encoded as utf8) is ' '127') if len(utf8_host) > 255: raise InvalidFunctionParameter('host', 'max string ' 'length (encoded as utf8) is ' '255') plaintext += utf8_login + b'\00' plaintext += utf8_host + b'\00' plaintext += utf8_callback_url + b'\00' # ------------------------------------------------------------------- -- # encrypt inner layer nonce_as_int = int_from_bytes(nonce, byteorder='big') ctr = Counter.new(128, initial_value=nonce_as_int) cipher = AES.new(sk, AES.MODE_CTR, counter=ctr) ciphertext = cipher.encrypt(plaintext) unsigned_raw_data = data_header + R + ciphertext # ------------------------------------------------------------------- -- # create signature partition = self.getFromTokenInfo('partition') secret_key = get_secret_key(partition) signature = crypto_sign_detached(unsigned_raw_data, secret_key) raw_data = unsigned_raw_data + signature protocol_id = config.get('mobile_app_protocol_id', 'lseqr') url = protocol_id + '://push/' + encode_base64_urlsafe(raw_data) return url, (signature + plaintext)
def test_suffix(self): c = Counter.new(128, suffix=b("xx"))
def test_nbits(self): c = Counter.new(nbits=128) self.assertRaises(ValueError, Counter.new, 129)
def test_prefix(self): c = Counter.new(128, prefix=b("xx"))