def checkHMAC(self, fp, segments_start, segments_end, fileHMAC): '''Check the file's integrity''' filehash = HMAC(self.hmackey, primitives.hashes.SHA256(), backend) filehash.update(self.FileMACPrefix) for segmentIndex, startpos, datalen in self.segment_ranges( segments_start, segments_end): print(" Segment %d" % (segmentIndex)) fp.seek(startpos) segmentIV = fp.read(self.SegIVLen) segmentMAC = fp.read(self.SegMACLen) # Verify the segment's own MAC against the segment data segmenthash = HMAC(self.hmackey, primitives.hashes.SHA256(), backend) segmenthash.update(segmentIV) segmenthash.update(struct.pack('>I', segmentIndex)) segmenthash.update(fp.read(datalen)) # The cryptography module doesn't handle truncated HMACs directly computed = segmenthash.finalize() # print(computed.decode("utf-8")) assert primitives.constant_time.bytes_eq(computed[:self.SegMACLen], segmentMAC) # Add the segment's MAC to the file-MAC context filehash.update(segmentMAC) # Finally, verify the file MAC print(" File hash") filehash.verify(fileHMAC) # Raises on mismatch.
def nc_decrypt(key, ct, iv) -> list: """ :param key: either cli2ser_key or ser2cli_key :param ct: bytes :param iv: bytes :return: list Return values: rv[1] = derived key rv[2] = pt """ validate_aes256_key("nc_decrypt", key) validate_ct("nc_decrypt", ct) validate_iv("nc_decrypt", iv) rv = [] # NOTE: same block used for encryption dec1 = HMAC(NC_KEY, SHA256(), backend=default_backend()) dec1.update(key) dec1_out = dec1.finalize() # log.debug("nc_decrypt dec1_out: {}, {}".format(dec1_out, type(dec1_out))) rv.append(dec1_out) dec2 = HMAC(dec1_out, SHA256(), backend=default_backend()) dec2.update(NC_STR_ENC2) dec2.update(b'\x01') dec2_out = dec2.finalize() # log.debug("nc_decrypt dec2_out: {}, {}".format(dec2_out, type(dec2_out))) # log.debug("nc_decrypt dec2_out is the symmetric key: {}".format(dec2_out)) rv.append(dec2_out) mode = CBC(iv) cipher = Cipher(AES(dec2_out), mode, backend=default_backend()) # NOTE: decryptor does not unpad automatically decryptor = cipher.decryptor() pt_p = decryptor.update(ct) + decryptor.finalize() # log.debug("nc_decrypt: pt_p: {}, {}. {}".format(pt_p, len(pt_p), type(pt_p))) # if (len(pt_p) % AES_BLOCK_BYTES != 0): # unpadder = PKCS7(AES_BLOCK_BITS).unpadder() # pt = unpadder.update(pt_p) + unpadder.finalize() # else: # pt = pt_p unpadder = PKCS7(AES_BLOCK_BITS).unpadder() pt = unpadder.update(pt_p) + unpadder.finalize() # log.debug("nc_decrypt: pt: {}, {}. {}".format(pt, len(pt), type(pt))) rv.append(pt) return rv
def nc_encrypt(key, pt, iv) -> list: """ :param key: either cli2ser_key or ser2cli_key :param pt: bytes :param iv: bytes :return: list Return values: rv[1] = derived key rv[2] = ct """ validate_aes256_key("nc_encrypt", key) validate_pt("nc_encrypt", pt) validate_iv("nc_encrypt", iv) rv = [] # NOTE: same block used for decryption enc1 = HMAC(NC_KEY, SHA256(), backend=default_backend()) enc1.update(key) enc1_out = enc1.finalize() # log.debug("nc_encrypt enc1_out: {}, {}".format(enc1_out, type(enc1_out))) rv.append(enc1_out) enc2 = HMAC(enc1_out, SHA256(), backend=default_backend()) enc2.update(NC_STR_ENC2) enc2.update(b'\x01') enc2_out = enc2.finalize() # log.debug("nc_encrypt enc2_out: {}, {}".format(enc2_out, type(enc2_out))) # log.debug("nc_encrypt enc2_out is the symmetric key: {}".format(enc2_out)) rv.append(enc2_out) mode = CBC(iv) cipher = Cipher(AES(enc2_out), mode, backend=default_backend()) # NOTE: encryptor does not pad automatically encryptor = cipher.encryptor() if (len(pt) % AES_BLOCK_BYTES != 0): padder = PKCS7(AES_BLOCK_BITS).padder() pt_p = padder.update(pt) + padder.finalize() else: pt_p = pt # log.debug("nc_encrypt: pt_p {}, {}, {}".format(pt_p, len(pt_p), type(pt_p))) ct = encryptor.update(pt_p) + encryptor.finalize() # log.debug("nc_encrypt ct {}, {}, {}".format(ct, len(ct), type(ct))) rv.append(ct) return rv
def _verify_signature(self, data: bytes) -> None: h = HMAC(self._signing_key, hashes.SHA256()) h.update(data[:-32]) try: h.verify(data[-32:]) except InvalidSignature: raise InvalidToken
def verifyThenDecrypt(cipher, emailTime, key): encryptKey = key[16:] signKey = key[:16] payload = base64.urlsafe_b64decode(cipher) #verify timestamp to prevent replay try: timestamp, = struct.unpack(">Q", payload[1:9]) except struct.error: raise ValueError('Invalid message') if timestamp + TTL < emailTime: raise Exception('Invalid timestamp: replay attack detected') #verify HMAC hasher = HMAC(signKey, hashes.SHA256(), backend=default_backend()) hasher.update(payload[:-32]) try: hasher.verify(payload[-32:]) except InvalidSignature: raise Exception('Invalid HMAC: data modification detected') #decrypt cipher text iv = payload[9:25] ciphertext = payload[25:-32] decryptor = Cipher(algorithms.AES(encryptKey), modes.CBC(iv), default_backend()).decryptor() paddedPlaintext = decryptor.update(ciphertext) paddedPlaintext += decryptor.finalize() unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() plaintext = unpadder.update(paddedPlaintext) plaintext += unpadder.finalize() return plaintext
def decrypt_ctr(key, iv, aad, data): """ verify HMAC and decrypt using CTR mode key: 16 byte key iv: 12 bytes nonce. 4 zero bytes are appended to create the 16 byte nonce used for CTR mode aad: bytes. additional unencrypted data to include in the signature data: bytes. plaint text to encrypt """ nonce = iv + b'\x00\x00\x00\x00' ct = data[:-16] tag = data[-16:] h = HMAC(key, SHA256(), backend=default_backend()) h.update(aad) h.update(ct) act = h.finalize()[:16] if not bytes_eq(tag, act): raise ValueError() decryptor = Cipher(AES(key), CTR(nonce), backend=default_backend()).decryptor() return decryptor.update(ct) + decryptor.finalize()
def encrypt_ctr(key, iv, aad, data): """ encrypt using CTR then sign with an HMAC Note: this implementation is similar to AES-CCM, but is slower. The implementation is left in only for benchmarking purposes. Note: in theory this should parallize well, however in practice it is about 2x slower than GCM mode. The HMAC is not the bottleneck key: 16 byte key iv: 12 bytes nonce. 4 zero bytes are appended to create the 16 byte nonce used for CTR mode aad: bytes. additional unencrypted data to include in the signature data: bytes. plaint text to encrypt future work: require key to be 32 bytes, and then use 16 bytes for the HMAC and the other 16 bytes for CTR mode """ nonce = iv + b'\x00\x00\x00\x00' encryptor = Cipher(AES(key), CTR(nonce), backend=default_backend()).encryptor() ct = encryptor.update(data) + encryptor.finalize() h = HMAC(key, SHA256(), backend=default_backend()) h.update(aad) h.update(ct) tag = h.finalize()[:16] return ct + tag
def encrypt_with_hmac(self, data, data_id, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") if not isinstance(data_id, int): raise TypeError("data_id must be int.") main_parts = ( struct.pack(config.FORMAT_CHAR, data_id) + data ) # PKCS7 padding padded_data = self.add_padding(main_parts, algorithms.AES.block_size) # AES with CBC mode encryptor = Cipher(algorithms.AES(self.aes_key), modes.CBC(iv), backend=self.backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() basic_parts = ( b"\x80" + iv + ciphertext ) h = HMAC(self.mac_key, hashes.SHA256(), backend=self.backend) h.update(basic_parts) hmac = h.finalize() return basic_parts + hmac
def _encrypt_from_parts(self, data, current_time, iv): if not isinstance(data, bytes): raise TypeError("data must be bytes.") # PROBLEM: MAC-then-Encrypt # HMAC(HDR+R) h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) basic_parts = (b"\x80" + struct.pack(">Q", current_time) + data) h.update(basic_parts) hmac = h.finalize() # R+T+pad padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data + hmac) + padder.finalize() print('Message:', data, 'MAC:', hmac, 'Pad:', padded_data[len(data + hmac):], sep=' ') print('Plaintext to be encrypted: ', padded_data) encryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).encryptor() ciphertext = encryptor.update(padded_data) + encryptor.finalize() final_message = b"\x80" + \ struct.pack(">Q", current_time) + iv + ciphertext return base64.urlsafe_b64encode(final_message)
def check_integrity(self, token, ttl=None): # check integrity of files # with our implementation, this is actually superfluous, since if the integrity is compromised, you can't get to # this function. We check the integrity when we decrypt. if not isinstance(token, bytes): raise TypeError("token must be bytes.") current_time = int(time.time()) try: data = base64.urlsafe_b64decode(token) except (TypeError, binascii.Error): raise InvalidToken if not data or six.indexbytes(data, 0) != 0x80: raise InvalidToken try: timestamp, = struct.unpack(">Q", data[1:9]) except struct.error: raise InvalidToken if ttl is not None: if timestamp + ttl < current_time: raise InvalidToken if current_time + _MAX_CLOCK_SKEW < timestamp: raise InvalidToken h = HMAC(self._signing_key, hashes.SHA512(), backend=self._backend) h.update(data[:-64]) try: h.verify(data[-64:]) except InvalidSignature: raise InvalidToken
def _encrypt_from_parts(self, data, adata="", salt="", signing_key="", encryption_key=""): if not isinstance(data, bytes): raise TypeError("data must be bytes.") # print("signing_key = " + str(len(signing_key)), signing_key) # print("encryption_key = " + str(len(encryption_key)), encryption_key) padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() encryptor = Cipher(algorithms.AES(encryption_key), modes.CBC("0" * 16), self._backend).encryptor() # ctx = AES( iv || msg ) ctx = encryptor.update(padded_data) + encryptor.finalize() basic_parts = (b"\x82" + salt + ctx) h = HMAC(signing_key, hashes.SHA256(), backend=self._backend) h.update(basic_parts + adata) # tag = HMAC( 0x81 || iv || ctx ) tag = h.finalize() return base64.urlsafe_b64encode(basic_parts + tag)
def process_header(self, data): if self._cipher_key_len is None: if data[0:6] != FILEMAGIC: raise EncryptorError("Invalid magic bytes") self._cipher_key_len = struct.unpack(">H", data[6:8])[0] else: pad = padding.OAEP(mgf=padding.MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None) try: plainkey = self.rsa_private_key.decrypt(data, pad) except AssertionError: raise EncryptorError("Decrypting key data failed") if len(plainkey) != 64: raise EncryptorError("Integrity check failed") key = plainkey[0:16] nonce = plainkey[16:32] auth_key = plainkey[32:64] self._header_size = 8 + len(data) self.cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend()).decryptor() self.authenticator = HMAC(auth_key, SHA256(), backend=default_backend())
def enc(self, byts): ''' Encrypt the given bytes and return an envelope dict in msgpack form. Args: byts (bytes): The message to be encrypted. Returns: bytes: The encrypted message. This is a msgpacked dictionary containing the IV, ciphertext and HMAC values. ''' iv = os.urandom(16) # pad the bytes using PKCS7 padr = padding.PKCS7(self.bsize).padder() byts = padr.update(byts) + padr.finalize() mode = modes.CBC(iv) algo = algorithms.AES(self.ekey) encr = Cipher(algo, mode, self.bend).encryptor() # encrypt the bytes and prepend the IV byts = encr.update(byts) + encr.finalize() macr = HMAC(self.skey, hashes.SHA256(), backend=self.bend) macr.update(iv + byts) hmac = macr.finalize() envl = {'iv': iv, 'hmac': hmac, 'data': byts} return s_msgpack.en(envl)
def aes_cbc_dec(aesKey, bsEncMsg, bsMacMsg): ''' AuthenticatedEncryption - Check mac then decrypt given encrypted message. ''' ### Prepare for mac sha256 = SHA256() hmac = HMAC(aesKey, sha256, default_backend()) ### do mac hmac.update(bsEncMsg) macMsg = hmac.finalize() if (macMsg != bsMacMsg): raise Exception("ERRR:AEDecrypt:Mismatch, skipping") return None ### Prepare for decryption blockLen = 16 iv = os.urandom(blockLen) aes = AES(aesKey) cbc = CBC(iv) aesCbc = Cipher(aes, cbc, default_backend()) aesCbcDec = aesCbc.decryptor() ### do decrypt decMsg = aesCbcDec.update(bsEncMsg) decFina = aesCbcDec.finalize() decMsg = decMsg + decFina # do pkcs7 depadding unpad = PKCS7(blockLen * 8).unpadder() decMsg = unpad.update(decMsg) decMsg += unpad.finalize() # Discard the initial random block, as corresponding enc and this dec uses # non communicated random iv and inturn discardable random 0th block decMsg = decMsg[blockLen:] return decMsg
def sign(self, pairs): """ Generate a signature for a sequence of (key, value) pairs @param pairs: The pairs to sign, in order @type pairs: Iterable[six.text_type, six.text_type], six.binary_type is deprecated @return: The binary signature of this sequence of pairs @rtype: six.binary_type """ warning_msg = "Binary values for pairs are deprecated. Use text input instead." pairs = [(string_to_text(a, warning_msg), string_to_text(b, warning_msg)) for a, b in pairs] kv = kvform.seqToKV(pairs) try: algorithm = self.hmac_algorithms[self.assoc_type] except KeyError: raise ValueError('Unknown association type: %r' % (self.assoc_type, )) hmac = HMAC(self.secret, algorithm, backend=default_backend()) hmac.update(kv.encode('utf-8')) return hmac.finalize()
def decrypt(privkey, data): s = serialize.Deserializer(data) iv = s.bytes(16) curve = s.uint(2) assert curve == 0x02ca x_len = s.uint(2) assert x_len <= 32 # TODO Should we assert this? And should we assert no leading zero bytes? x = s.bytes(x_len) y_len = s.uint(2) assert y_len <= 32 # TODO Should we assert this? And should we assert no leading zero bytes? y = s.bytes(y_len) encrypted = s.bytes(-32) assert encrypted != b'' mac = s.bytes(32) pubkey = x.rjust(32, b'\x00') + y.rjust(32, b'\x00') public_key = _pub_to_public(pubkey) private_key = _priv_to_private(privkey) secret = private_key.exchange(ec.ECDH(), public_key) key = hashlib.sha512(secret).digest() enckey = key[0:32] mackey = key[32:64] maccer = HMAC(mackey, hashes.SHA256(), openssl.backend) maccer.update(data[0:-32]) maccer.verify(mac) cipher = Cipher(algorithms.AES(enckey), modes.CBC(iv), openssl.backend) decryptor = cipher.decryptor() padded = decryptor.update(encrypted) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() return unpadder.update(padded) + unpadder.finalize()
def encrypt(pubkey, data): public_key = _pub_to_public(pubkey) private_key = ec.generate_private_key(ec.SECP256K1(), openssl.backend) secret = private_key.exchange(ec.ECDH(), public_key) key = hashlib.sha512(secret).digest() enckey = key[0:32] mackey = key[32:64] iv = os.urandom(16) padder = padding.PKCS7(128).padder() paddeddata = padder.update(data) + padder.finalize() cipher = Cipher(algorithms.AES(enckey), modes.CBC(iv), openssl.backend) encryptor = cipher.encryptor() ciphertext = encryptor.update(paddeddata) + encryptor.finalize() s = serialize.Serializer() s.bytes(iv) s.uint(0x02ca, 2) public_numbers = private_key.public_key().public_numbers() x = public_numbers.x.to_bytes(32, 'big').lstrip(b'\x00') s.uint(len(x), 2) s.bytes(x) y = public_numbers.y.to_bytes(32, 'big').lstrip(b'\x00') s.uint(len(y), 2) s.bytes(y) s.bytes(ciphertext) maccer = HMAC(mackey, hashes.SHA256(), openssl.backend) maccer.update(s.data) mac = maccer.finalize() s.bytes(mac) return s.data
def salted_hmac(key_salt, value, secret=None): """ Returns the HMAC-HASH of 'value', using a key generated from key_salt and a secret (which defaults to settings.SECRET_KEY). A different key_salt should be passed in for every application of HMAC. :type key_salt: any :type value: any :type secret: any :rtype: HMAC """ if secret is None: secret = settings.SECRET_KEY key_salt = force_bytes(key_salt) secret = force_bytes(secret) # We need to generate a derived key from our base key. We can do this by # passing the key_salt and our base key through a pseudo-random function and # SHA1 works nicely. digest = hashes.Hash(settings.CRYPTOGRAPHY_DIGEST, backend=settings.CRYPTOGRAPHY_BACKEND) digest.update(key_salt + secret) key = digest.finalize() # If len(key_salt + secret) > sha_constructor().block_size, the above # line is redundant and could be replaced by key = key_salt + secret, since # the hmac module does the same thing for keys longer than the block size. # However, we need to ensure that we *always* do this. h = HMAC(key, settings.CRYPTOGRAPHY_DIGEST, backend=settings.CRYPTOGRAPHY_BACKEND) h.update(force_bytes(value)) return h
def decrypt_encryption_key_fallback(self): """Decrypts the encryption key using the FALLBACK method. In this method, the context string, usually "CredEncryption" or "PSEEncryption", is encrypted using a derivation of a fixed key hardcoded in CommonCryptoLib, and used as key to encrypt the actual encryption key used in the file with the AES cipher. :return: Encryption key decrypted :rtype: string """ log_lps.debug("Obtaining encryption key with FALLBACK LPS mode") digest = Hash(SHA1(), backend=default_backend()) digest.update(cred_key_lps_fallback) hashed_key = digest.finalize() hmac = HMAC(hashed_key, SHA1(), backend=default_backend()) hmac.update(self.context) default_key = hmac.finalize()[:16] iv = "\x00" * 16 decryptor = Cipher(algorithms.AES(default_key), modes.CBC(iv), backend=default_backend()).decryptor() encryption_key = decryptor.update( self.encrypted_key) + decryptor.finalize() return encryption_key
def scramble_salt(self, password, salt, server_key, client_key, rounds=None): """Scrambles a given salt using the specified server key. """ msg = salt + server_key + client_key hmac_digest = self.salt_key(password, salt, rounds) hash = Hash(self.ALGORITHM(), self.backend) hash.update(hmac_digest) hash_digest = hash.finalize() key_hash = Hash(self.ALGORITHM(), self.backend) key_hash.update(hash_digest) key_hash_digest = key_hash.finalize() sig = HMAC(key_hash_digest, self.ALGORITHM(), self.backend) sig.update(msg) sig_digest = sig.finalize() return self.xor(sig_digest, hash_digest)
def _decrypt_data(self, data, timestamp, ttl): current_time = int(time.time()) if ttl is not None: if timestamp + ttl < current_time: raise InvalidToken if current_time + _MAX_CLOCK_SKEW < timestamp: raise InvalidToken h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(data[:-32]) try: h.verify(data[-32:]) except InvalidSignature: raise InvalidToken iv = data[9:25] ciphertext = data[25:-32] decryptor = Cipher(algorithms.AES(self._encryption_key), modes.CBC(iv), self._backend).decryptor() plaintext_padded = decryptor.update(ciphertext) try: plaintext_padded += decryptor.finalize() except ValueError: raise InvalidToken unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() unpadded = unpadder.update(plaintext_padded) try: unpadded += unpadder.finalize() except ValueError: raise InvalidToken return unpadded
def _a(self, secret, hash_algorithm, n, seed): if n == 0: return seed else: h = HMAC(secret, hash_algorithm, default_backend()) h.update(self._a(secret, hash_algorithm, n - 1, seed)) return h.finalize()
def _verify_signature(self, data): h = HMAC(self._signing_key, hashes.SHA256(), backend=self._backend) h.update(data[:-32]) try: h.verify(data[-32:]) except InvalidSignature: raise InvalidToken
def hmac_verify(key, data, hash_alg, backend, sig = None): h = HMAC( key, hash_alg, backend=backend ) h.update(data) return h.verify(sig)
def hmac(key, data, hash_alg, backend): h = HMAC( key, hash_alg, backend=backend ) h.update(data) return h.finalize()
def signature(self, value): """ :type value: any :rtype: HMAC """ h = HMAC(self.key, self.digest, backend=settings.CRYPTOGRAPHY_BACKEND) h.update(force_bytes(value)) return h
def nc_mac(key, ct, iv, pt_type) -> list: """NC AES encryption key computation :param key: either cli2ser_key or ser2cli_key :param ct: bytes :param iv: bytes :param pt_type: string in PT_TYPES :return: list Return values: rv[2] = mac """ validate_aes256_key("nc_mac", key) validate_ct("nc_mac", ct) validate_iv("nc_mac", iv) validate_pt_type("nc_mac", pt_type) rv = [] mac1 = HMAC(NC_KEY, SHA256(), backend=default_backend()) mac1.update(key) mac1_out = mac1.finalize() # log.debug("nc_mac mac1_out: {}, {}".format(mac1_out, type(mac1_out))) rv.append(mac1_out) mac2 = HMAC(mac1_out, SHA256(), backend=default_backend()) mac2.update(NC_STR_SIG1) mac2.update(b'\x01') mac2_out = mac2.finalize() # log.debug("nc_mac mac2_out: {}, {}".format(mac2_out, type(mac2_out))) rv.append(mac2_out) # NOTE: conditional to the type of ct (and indeed pt) mac3 = HMAC(mac2_out, SHA256(), backend=default_backend()) mac3_inp_hex = NC_MAC_PRE_IV + iv.hex() + NC_MAC_POST_IV[pt_type] + ct.hex( ) mac3_inp = unhexlify(mac3_inp_hex) mac3.update(mac3_inp) mac3_out = mac3.finalize() # log.debug("nc_mac mac3_out: {}, {}".format(mac3_out, type(mac3_out))) rv.append(mac3_out) return rv
def _get_hmac(key, ciphertext, digest_method): hmac = HMAC( key, get_digest(digest_method), backend=default_backend() ) hmac.update(ciphertext) return hmac.finalize()
def _verify(data, mac_key): h = HMAC(mac_key, _MAC_HASH, _backend) h.update(data[:-_MAC_LEN]) try: h.verify(data[-_MAC_LEN:]) return True except InvalidSignature: return False
def __init__(self, key, backend=None): if backend is None: backend = default_backend() key = base64.urlsafe_b64decode(key) if len(key) != 32: raise ValueError( "Fernet key must be 32 url-safe base64-encoded bytes.") h = HMAC(key, hashes.SHA256(), backend=backend) h.update(b"\x00") self._signing_key = h.finalize()[:16] h = HMAC(key, hashes.SHA256(), backend=backend) h.update(b"\x01") self._encryption_key = h.finalize()[:16] self._backend = backend