def _update(self, assoc_data_pt=b("")): """Update the MAC with associated data or plaintext (without FSM checks)""" if self._mac_status == MacStatus.NOT_STARTED: self._cache.append(bstr(assoc_data_pt)) return assert(len(self._cache) < self.block_size) if len(self._cache) > 0: filler = min(self.block_size - len(self._cache), len(assoc_data_pt)) self._cache += assoc_data_pt[:filler] assoc_data_pt = assoc_data_pt[filler:] if len(self._cache) < self.block_size: return # The cache is exactly one block self._t = self._mac.encrypt(self._cache) self._cache = b("") update_len = len(assoc_data_pt) // self.block_size * self.block_size self._cache = assoc_data_pt[update_len:] if update_len > 0: self._t = self._mac.encrypt(assoc_data_pt[:update_len])[-16:]
def __init__(self, key, nonce): """Initialize a Salsa20 cipher object See also `new()` at the module level.""" if len(key) not in key_size: raise ValueError("Incorrect key length for Salsa20 (%d bytes)" % len(key)) if len(nonce) != 8: raise ValueError("Incorrect nonce length for Salsa20 (%d bytes)" % len(nonce)) self.nonce = bstr(nonce) self._state = VoidPointer() result = _raw_salsa20_lib.Salsa20_stream_init(c_uint8_ptr(key), c_size_t(len(key)), c_uint8_ptr(nonce), c_size_t(len(nonce)), self._state.address_of()) if result: raise ValueError("Error %d instantiating a Salsa20 cipher") self._state = SmartPointer(self._state.get(), _raw_salsa20_lib.Salsa20_stream_destroy) self.block_size = 1 self.key_size = len(key)
def _start_mac(self): assert(self._mac_status == MacStatus.NOT_STARTED) assert(None not in (self._assoc_len, self._msg_len)) assert(isinstance(self._cache, list)) # Formatting control information and nonce (A.2.1) q = 15 - len(self.nonce) # length of Q, the encoded message length flags = (64 * (self._assoc_len > 0) + 8 * ((self._mac_len - 2) // 2) + (q - 1)) b_0 = bchr(flags) + bstr(self.nonce) + long_to_bytes(self._msg_len, q) # Formatting associated data (A.2.2) # Encoded 'a' is concatenated with the associated data 'A' assoc_len_encoded = b('') if self._assoc_len > 0: if self._assoc_len < (2 ** 16 - 2 ** 8): enc_size = 2 elif self._assoc_len < (2L ** 32): assoc_len_encoded = b('\xFF\xFE') enc_size = 4 else: assoc_len_encoded = b('\xFF\xFF') enc_size = 8 assoc_len_encoded += long_to_bytes(self._assoc_len, enc_size)
def _start_mac(self): assert(self._mac_status == MacStatus.NOT_STARTED) assert(None not in (self._assoc_len, self._msg_len)) assert(isinstance(self._cache, list)) # Formatting control information and nonce (A.2.1) q = 15 - len(self.nonce) # length of Q, the encoded message length flags = (64 * (self._assoc_len > 0) + 8 * ((self._mac_len - 2) // 2) + (q - 1)) b_0 = bchr(flags) + bstr(self.nonce) + long_to_bytes(self._msg_len, q) # Formatting associated data (A.2.2) # Encoded 'a' is concatenated with the associated data 'A' assoc_len_encoded = b('') if self._assoc_len > 0: if self._assoc_len < (2 ** 16 - 2 ** 8): enc_size = 2 elif self._assoc_len < (2 ** 32): assoc_len_encoded = b('\xFF\xFE') enc_size = 4 else: assoc_len_encoded = b('\xFF\xFF') enc_size = 8 assoc_len_encoded += long_to_bytes(self._assoc_len, enc_size) # b_0 and assoc_len_encoded must be processed first self._cache.insert(0, b_0) self._cache.insert(1, assoc_len_encoded) # Process all the data cached so far first_data_to_mac = b("").join(self._cache) self._cache = b("") self._mac_status = MacStatus.PROCESSING_AUTH_DATA self._update(first_data_to_mac)
def __init__(self, block_cipher, initial_counter_block, prefix_len, counter_len, little_endian): """Create a new block cipher, configured in CTR mode. :Parameters: block_cipher : C pointer A smart pointer to the low-level block cipher instance. initial_counter_block : byte string/array The initial plaintext to use to generate the key stream. It is as large as the cipher block, and it embeds the initial value of the counter. This value must not be reused. It shall contain a nonce or a random component. Reusing the *initial counter block* for encryptions performed with the same key compromises confidentiality. prefix_len : integer The amount of bytes at the beginning of the counter block that never change. counter_len : integer The length in bytes of the counter embedded in the counter block. little_endian : boolean True if the counter in the counter block is an integer encoded in little endian mode. If False, it is big endian. """ if len(initial_counter_block) == prefix_len + counter_len: self.nonce = bstr(initial_counter_block[:prefix_len]) """Nonce; not available if there is a fixed suffix""" self._state = VoidPointer() result = raw_ctr_lib.CTR_start_operation( block_cipher.get(), c_uint8_ptr(initial_counter_block), c_size_t(len(initial_counter_block)), c_size_t(prefix_len), counter_len, little_endian, self._state.address_of()) if result: raise ValueError("Error %X while instatiating the CTR mode" % result) # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the cipher mode self._state = SmartPointer(self._state.get(), raw_ctr_lib.CTR_stop_operation) # Memory allocated for the underlying block cipher is now owed # by the cipher mode block_cipher.release() self.block_size = len(initial_counter_block) """The block size of the underlying cipher, in bytes.""" self._next = [self.encrypt, self.decrypt]
def update(self, assoc_data): """Process the associated data. If there is any associated data, the caller has to invoke this method one or more times, before using ``decrypt`` or ``encrypt``. By *associated data* it is meant any data (e.g. packet headers) that will not be encrypted and will be transmitted in the clear. However, the receiver shall still able to detect modifications. If there is no associated data, this method must not be called. The caller may split associated data in segments of any size, and invoke this method multiple times, each time with the next segment. :Parameters: assoc_data : byte string/array A piece of associated data. """ if self.update not in self._next: raise TypeError("update() can only be called" " immediately after initialization") self._next = [ self.encrypt, self.decrypt, self.digest, self.verify, self.update ] if len(self._cache_A) > 0: filler = min(16 - len(self._cache_A), len(assoc_data)) self._cache_A += bstr(assoc_data[:filler]) assoc_data = assoc_data[filler:] if len(self._cache_A) < 16: return self # Clear the cache, and proceeding with any other aligned data self._cache_A, seg = b(""), self._cache_A self.update(seg) update_len = len(assoc_data) // 16 * 16 self._cache_A = bstr(assoc_data[update_len:]) self._update(assoc_data, update_len) return self
def bcrypt(password, cost, salt=None): """Hash a password into a key, using the OpenBSD bcrypt protocol. Args: password (byte string or string): The secret password or pass phrase. It must be at most 72 bytes long. It must not contain the zero byte. Unicode strings will be encoded as UTF-8. cost (integer): The exponential factor that makes it slower to compute the hash. It must be in the range 4 to 31. salt (byte string): Optional. Random byte string to thwarts dictionary and rainbow table attacks. It must be 16 bytes long. If not passed, a random value is generated. Return (byte string): The bcrypt hash Raises: ValueError: if password is longer than 72 bytes or if it contains the zero byte Note: If you want to hash passwords with no restrictions on their length, it is common practice to apply a cryptographic hash and then BASE64-encode the result. For instance:: from base64 import b64encode from Crypto.Hash import SHA256 from Crypto.Protocol.KDF import bcrypt password = b"test" b64pwd = b64encode(SHA256.new(password).digest()) bcrypt_hash = bcrypt(b64pwd, 10) """ password = tobytes(password, "utf-8") if password.find(bchr(0)[0]) != -1: raise ValueError("The password contains the zero byte") if len(password) < 72: password += b"\x00" if salt is None: salt = get_random_bytes(16) if len(salt) != 16: raise ValueError("bcrypt salt must be 16 bytes long") ctext = _bcrypt_hash(password, cost, salt, b"OrpheanBeholderScryDoubt", True) cost_enc = b"$" + bstr(str(cost).zfill(2)) salt_enc = b"$" + _bcrypt_encode(salt) hash_enc = _bcrypt_encode(ctext[:-1]) # only use 23 bytes, not 24 return b"$2a" + cost_enc + salt_enc + hash_enc
def _create_ctr_cipher(self, mac_tag): """Create a new CTR cipher from the MAC in SIV mode""" tag_int = bytes_to_long(bstr(mac_tag)) return self._factory.new(self._subkey_cipher, self._factory.MODE_CTR, initial_value=tag_int ^ (tag_int & 0x8000000080000000), nonce=b(""), **self._cipher_params)
def _transcrypt(self, in_data, trans_func, trans_desc): # Last piece to encrypt/decrypt if in_data is None: out_data = self._transcrypt_aligned(self._cache_P, len(self._cache_P), trans_func, trans_desc) self._cache_P = b("") return out_data # Try to fill up the cache, if it already contains something prefix = b("") if len(self._cache_P) > 0: filler = min(16 - len(self._cache_P), len(in_data)) self._cache_P += bstr(in_data[:filler]) in_data = in_data[filler:] if len(self._cache_P) < 16: # We could not manage to fill the cache, so there is certainly # no output yet. return b("") # Clear the cache, and proceeding with any other aligned data prefix = self._transcrypt_aligned(self._cache_P, len(self._cache_P), trans_func, trans_desc) self._cache_P = b("") # Process data in multiples of the block size trans_len = len(in_data) // 16 * 16 result = self._transcrypt_aligned(c_uint8_ptr(in_data), trans_len, trans_func, trans_desc) if prefix: result = prefix + result # Left-over self._cache_P = bstr(in_data[trans_len:]) return result
def __init__(self, block_cipher, iv, segment_size): """Create a new block cipher, configured in CFB mode. :Parameters: block_cipher : C pointer A smart pointer to the low-level block cipher instance. iv : byte string/array The initialization vector to use for encryption or decryption. It is as long as the cipher block. **The IV must be unpredictable**. Ideally it is picked randomly. Reusing the *IV* for encryptions performed with the same key compromises confidentiality. segment_size : integer The number of bytes the plaintext and ciphertext are segmented in. """ self._state = VoidPointer() result = raw_cfb_lib.CFB_start_operation(block_cipher.get(), c_uint8_ptr(iv), c_size_t(len(iv)), c_size_t(segment_size), self._state.address_of()) if result: raise ValueError("Error %d while instatiating the CFB mode" % result) # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the cipher mode self._state = SmartPointer(self._state.get(), raw_cfb_lib.CFB_stop_operation) # Memory allocated for the underlying block cipher is now owed # by the cipher mode block_cipher.release() self.block_size = len(iv) """The block size of the underlying cipher, in bytes.""" self.iv = bstr(iv) """The Initialization Vector originally used to create the object. The value does not change.""" self.IV = self.iv """Alias for `iv`""" self._next = [ self.encrypt, self.decrypt ]
def bcrypt(password, cost, salt=None): """Hash a password into a key, using the OpenBSD bcrypt protocol. Args: password (byte string or string): The secret password or pass phrase. It must be at most 72 bytes long. It must not contain the zero byte. Unicode strings will be encoded as UTF-8. cost (integer): The exponential factor that makes it slower to compute the hash. It must be in the range 4 to 31. A value of at least 12 is recommended. salt (byte string): Optional. Random byte string to thwarts dictionary and rainbow table attacks. It must be 16 bytes long. If not passed, a random value is generated. Return (byte string): The bcrypt hash Raises: ValueError: if password is longer than 72 bytes or if it contains the zero byte """ password = tobytes(password, "utf-8") if password.find(bchr(0)[0]) != -1: raise ValueError("The password contains the zero byte") if len(password) < 72: password += b"\x00" if salt is None: salt = get_random_bytes(16) if len(salt) != 16: raise ValueError("bcrypt salt must be 16 bytes long") ctext = _bcrypt_hash(password, cost, salt, b"OrpheanBeholderScryDoubt", True) cost_enc = b"$" + bstr(str(cost).zfill(2)) salt_enc = b"$" + _bcrypt_encode(salt) hash_enc = _bcrypt_encode(ctext[:-1]) # only use 23 bytes, not 24 return b"$2a" + cost_enc + salt_enc + hash_enc
def __init__(self, factory, key, nonce, mac_len, cipher_params): """EAX cipher mode""" self.block_size = factory.block_size """The block size of the underlying cipher, in bytes.""" self.nonce = bstr(nonce) """The nonce originally used to create the object.""" self._mac_len = mac_len self._mac_tag = None # Cache for MAC tag # Allowed transitions after initialization self._next = [ self.update, self.encrypt, self.decrypt, self.digest, self.verify ] # MAC tag length if not (4 <= self._mac_len <= self.block_size): raise ValueError("Parameter 'mac_len' must not be larger than %d" % self.block_size) # Nonce cannot be empty and must be a byte string if len(self.nonce) == 0: raise ValueError("Nonce cannot be empty in EAX mode") if isinstance(nonce, unicode): raise TypeError("nonce must be a byte string") self._omac = [ CMAC.new(key, bchr(0) * (self.block_size - 1) + bchr(i), ciphermod=factory, cipher_params=cipher_params) for i in xrange(0, 3) ] # Compute MAC of nonce self._omac[0].update(self.nonce) self._signer = self._omac[1] # MAC of the nonce is also the initial counter for CTR encryption counter_int = bytes_to_long(self._omac[0].digest()) self._cipher = factory.new(key, factory.MODE_CTR, initial_value=counter_int, nonce=b(""), **cipher_params)
def __init__(self, key, nonce): """Initialize a ChaCha20 cipher object See also `new()` at the module level.""" self.nonce = bstr(nonce) self._next = ( self.encrypt, self.decrypt ) self._state = VoidPointer() result = _raw_chacha20_lib.chacha20_init( self._state.address_of(), c_uint8_ptr(key), c_size_t(len(key)), self.nonce, c_size_t(len(nonce))) if result: raise ValueError("Error %d instantiating a ChaCha20 cipher") self._state = SmartPointer(self._state.get(), _raw_chacha20_lib.chacha20_destroy)
def _create_base_cipher(dict_parameters): """This method instantiates and returns a handle to a low-level base cipher. It will absorb named parameters in the process.""" try: key_in = dict_parameters.pop("key") except KeyError: raise TypeError("Missing 'key' parameter") key = adjust_key_parity(bstr(key_in)) start_operation = _raw_des3_lib.DES3_start_operation stop_operation = _raw_des3_lib.DES3_stop_operation cipher = VoidPointer() result = start_operation(key, c_size_t(len(key)), cipher.address_of()) if result: raise ValueError("Error %X while instantiating the TDES cipher" % result) return SmartPointer(cipher.get(), stop_operation)
def __init__(self, factory, key, nonce, kwargs): self.block_size = factory.block_size """The block size of the underlying cipher, in bytes.""" self._factory = factory self._nonce = nonce self._cipher_params = kwargs if len(key) not in (32, 48, 64): raise ValueError("Incorrect key length (%d bytes)" % len(key)) if nonce is not None: if isinstance(nonce, str): raise TypeError( "When provided, the nonce must be a byte string") if len(nonce) == 0: raise ValueError("When provided, the nonce must be non-empty") self.nonce = bstr(nonce) """Public attribute is only available in case of non-deterministic encryption.""" subkey_size = len(key) // 2 self._mac_tag = None # Cache for MAC tag self._kdf = _S2V(key[:subkey_size], ciphermod=factory, cipher_params=self._cipher_params) self._subkey_cipher = key[subkey_size:] # Purely for the purpose of verifying that cipher_params are OK factory.new(key[:subkey_size], factory.MODE_ECB, **kwargs) # Allowed transitions after initialization self._next = [ self.update, self.encrypt, self.decrypt, self.digest, self.verify ]
def _bcrypt_encode(data): s = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" bits = [] for c in data: bits_c = bin(bord(c))[2:].zfill(8) bits.append(bstr(bits_c)) bits = b"".join(bits) bits6 = [bits[idx:idx + 6] for idx in range(0, len(bits), 6)] result = [] for g in bits6[:-1]: idx = int(g, 2) result.append(s[idx]) g = bits6[-1] idx = int(g, 2) << (6 - len(g)) result.append(s[idx]) result = "".join(result) return tobytes(result)
def __init__(self, factory, key, iv, cipher_params): #: The block size of the underlying cipher, in bytes. self.block_size = factory.block_size self._done_first_block = False # True after the first encryption # Instantiate a temporary cipher to process the IV IV_cipher = factory.new(key, factory.MODE_CFB, IV=bchr(0) * self.block_size, segment_size=self.block_size * 8, **cipher_params) # The cipher will be used for... if len(iv) == self.block_size: # ... encryption self._encrypted_IV = IV_cipher.encrypt(iv + iv[-2:]) elif len(iv) == self.block_size + 2: # ... decryption self._encrypted_IV = iv # Last two bytes are for a deprecated "quick check" feature that # should not be used. (https://eprint.iacr.org/2005/033) iv = IV_cipher.decrypt(iv)[:-2] else: raise ValueError("Length of IV must be %d or %d bytes" " for MODE_OPENPGP" % (self.block_size, self.block_size + 2)) self.iv = self.IV = bstr(iv) # Instantiate the cipher for the real PGP data self._cipher = factory.new(key, factory.MODE_CFB, IV=self._encrypted_IV[-self.block_size:], segment_size=self.block_size * 8, **cipher_params)
def __init__(self, factory, nonce, mac_len, cipher_params): if factory.block_size != 16: raise ValueError("OCB mode is only available for ciphers" " that operate on 128 bits blocks") self.block_size = 16 """The block size of the underlying cipher, in bytes.""" self.nonce = bstr(nonce) """Nonce used for this session.""" if len(nonce) not in list(range(1, 16)): raise ValueError("Nonce must be at most 15 bytes long") if isinstance(nonce, str): raise TypeError("Nonce must be a byte string") self._mac_len = mac_len if not 8 <= mac_len <= 16: raise ValueError("MAC tag must be between 8 and 16 bytes long") # Cache for MAC tag self._mac_tag = None # Cache for unaligned associated data self._cache_A = b("") # Cache for unaligned ciphertext/plaintext self._cache_P = b("") # Allowed transitions after initialization self._next = [ self.update, self.encrypt, self.decrypt, self.digest, self.verify ] # Compute Offset_0 params_without_key = dict(cipher_params) key = params_without_key.pop("key") nonce = (bchr(self._mac_len << 4 & 0xFF) + bchr(0) * (14 - len(self.nonce)) + bchr(1) + self.nonce) bottom = bord(nonce[15]) & 0x3F # 6 bits, 0..63 ktop = factory.new(key, factory.MODE_ECB, **params_without_key)\ .encrypt(nonce[:15] + bchr(bord(nonce[15]) & 0xC0)) stretch = ktop + strxor(ktop[:8], ktop[1:9]) # 192 bits offset_0 = long_to_bytes(bytes_to_long(stretch) >> (64 - bottom), 24)[8:] # Create low-level cipher instance raw_cipher = factory._create_base_cipher(cipher_params) if cipher_params: raise TypeError("Unknown keywords: " + str(cipher_params)) self._state = VoidPointer() result = _raw_ocb_lib.OCB_start_operation(raw_cipher.get(), offset_0, c_size_t(len(offset_0)), self._state.address_of()) if result: raise ValueError("Error %d while instantiating the OCB mode" % result) # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the cipher mode self._state = SmartPointer(self._state.get(), _raw_ocb_lib.OCB_stop_operation) # Memory allocated for the underlying block cipher is now owed # by the cipher mode raw_cipher.release()
def __init__(self, factory, key, nonce, mac_len, cipher_params): self.block_size = factory.block_size if self.block_size != 16: raise ValueError("GCM mode is only available for ciphers" " that operate on 128 bits blocks") if len(nonce) == 0: raise ValueError("Nonce cannot be empty") if isinstance(nonce, unicode): raise TypeError("Nonce must be a byte string") self.nonce = bstr(nonce) """Nonce""" self._factory = factory self._key = bstr(key) self._tag = None # Cache for MAC tag self._mac_len = mac_len if not (4 <= mac_len <= 16): raise ValueError("Parameter 'mac_len' must be in the range 4..16") # Allowed transitions after initialization self._next = [ self.update, self.encrypt, self.decrypt, self.digest, self.verify ] self._no_more_assoc_data = False # Length of associated data self._auth_len = 0 # Length of the ciphertext or plaintext self._msg_len = 0 # Step 1 in SP800-38D, Algorithm 4 (encryption) - Compute H # See also Algorithm 5 (decryption) hash_subkey = factory.new(key, self._factory.MODE_ECB, **cipher_params).encrypt(bchr(0) * 16) # Step 2 - Compute J0 (integer, not byte string!) if len(self.nonce) == 12: self._j0 = bytes_to_long(self.nonce + b("\x00\x00\x00\x01")) else: fill = (16 - (len(nonce) % 16)) % 16 + 8 ghash_in = (self.nonce + bchr(0) * fill + long_to_bytes(8 * len(self.nonce), 8)) self._j0 = bytes_to_long( _GHASH(hash_subkey).update(ghash_in).digest()) # Step 3 - Prepare GCTR cipher for encryption/decryption self._cipher = factory.new(key, self._factory.MODE_CTR, initial_value=self._j0 + 1, nonce=b(""), **cipher_params) # Step 5 - Bootstrat GHASH self._signer = _GHASH(hash_subkey) # Step 6 - Prepare GCTR cipher for GMAC self._tag_cipher = factory.new(key, self._factory.MODE_CTR, initial_value=self._j0, nonce=b(""), **cipher_params) # Cache for data to authenticate self._cache = b("") self._status = MacStatus.PROCESSING_AUTH_DATA
def __init__(self, factory, key, nonce, mac_len, msg_len, assoc_len, cipher_params): self.block_size = factory.block_size """The block size of the underlying cipher, in bytes.""" self.nonce = bstr(nonce) """The nonce used for this cipher instance""" self._factory = factory self._key = bstr(key) self._mac_len = mac_len self._msg_len = msg_len self._assoc_len = assoc_len self._cipher_params = cipher_params self._mac_tag = None # Cache for MAC tag if self.block_size != 16: raise ValueError("CCM mode is only available for ciphers" " that operate on 128 bits blocks") # MAC tag length (Tlen) if mac_len not in (4, 6, 8, 10, 12, 14, 16): raise ValueError("Parameter 'mac_len' must be even" " and in the range 4..16 (not %d)" % mac_len) # Nonce value if not (nonce and 7 <= len(nonce) <= 13): raise ValueError("Length of parameter 'nonce' must be" " in the range 7..13 bytes") # Create MAC object (the tag will be the last block # bytes worth of ciphertext) self._mac = self._factory.new(key, factory.MODE_CBC, iv=bchr(0) * 16, **cipher_params) self._mac_status = MacStatus.NOT_STARTED self._t = None # Allowed transitions after initialization self._next = [self.update, self.encrypt, self.decrypt, self.digest, self.verify] # Cumulative lengths self._cumul_assoc_len = 0 self._cumul_msg_len = 0 # Cache for unaligned associated data/plaintext. # This is a list, but when the MAC starts, it will become a binary # string no longer than the block size. self._cache = [] # Start CTR cipher, by formatting the counter (A.3) q = 15 - len(nonce) # length of Q, the encoded message length self._cipher = self._factory.new(key, self._factory.MODE_CTR, nonce=bchr(q - 1) + nonce, **cipher_params) # S_0, step 6 in 6.1 for j=0 self._s_0 = self._cipher.encrypt(bchr(0) * 16) # Try to start the MAC if None not in (assoc_len, msg_len): self._start_mac()