def decrypt_ec(data, ec_private_key): ephemeral_public_key = load_ec_public_key(data[:65]) shared_secret = ec_private_key.exchange(ec.ECDH(), ephemeral_public_key) digest = Hash(SHA256(), backend=_CRYPTO_BACKEND) digest.update(shared_secret) encryption_key = digest.finalize() return decrypt_aes_v2(data[65:], encryption_key)
def validate_access_token(access_token, access_scope=scopes['client']): try: token = tokenserializer.loads(access_token) client_id = token.get('client', None) if not client_id: raise BadSignature('Token does not contain a client ID.') client = User.query.filter(User.id == client_id).first() if not client: raise BadSignature('No such client.') h = Hash(SHA256(), default_backend()) h.update(access_token.encode('utf-8')) if not client.token == h.finalize(): abort(401, message='Invalid access token.') if token.get('scope') > access_scope: abort(403, message='Insufficient Privileges.') except SignatureExpired: abort(401, message='Expired access token.') except BadSignature: abort(401, message='Bad access token.') except: raise
def check_pcrs(self, pcrs, qinfo, halg): if not qinfo.pcrSelect.count == 1: raise Exception('too many PCR selections') parray = TPMS_PCR_SELECTION_ARRAY.frompointer( qinfo.pcrSelect.pcrSelections) pcrsel = parray[0] if not pcrsel.hash == TPM2_ALG_SHA256: # FIXME raise Exception('PCR bank does not match') h = Hash(halg(), default_backend()) sel = 0 for ps, v in pcrs.items(): # FIXME, check keys p = int(ps) sel = sel | (1 << p) pv = b16decode(v) h.update(pv) dig = h.finalize() selb = sel.to_bytes(pcrsel.sizeofSelect, 'big') qselb = bytearray() qsarray = BYTE_ARRAY.frompointer(pcrsel.pcrSelect) for i in range(0, pcrsel.sizeofSelect): qselb.append(qsarray[i]) qselb = bytes(qselb) if not qselb == selb: raise Exception('PCR selection does not match') qdig = buffer_to_bytes(qinfo.pcrDigest) if not dig == qdig: raise Exception('PCR digest does not match')
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 _rebuild_index(self): index = {} cipher = CipherContext(self._password) for path in self._secrets.glob('*.secret'): with open(path, 'rb') as file: contents = file.read() plaintext = decompress(cipher.decrypt(contents)) # We extract the path out of the plaintext first. path_size = unpack('>H', plaintext[:2])[0] short_path = plaintext[2:path_size + 2].decode() # This is the true plaintext. plaintext = plaintext[path_size + 2:] # We need to generate a hash for the file. hasher = Hash(BLAKE2b(64)) hasher.update(plaintext) index[short_path] = { 'path': short_path, 'uuid': path.stem, 'checksum': hasher.finalize().hex() } return index
def hash(self, data): digest = Hash(MD5(), backend=openssl_backend) to_hash = "data={}||lpv={}||{}".format( data.decode('ascii'), '.'.join(map(str, self.version)), self.key) digest.update(to_hash.encode('utf8')) intermediate = digest.finalize().hex() return intermediate[8:24]
def encrypt_request(self, serialized_request): padder = self.session_key_padding.padder() serialized_request_padded = padder.update(serialized_request) + padder.finalize() encryptor = self.session_key_cipher.encryptor() serialized_request_ciphertext = encryptor.update(serialized_request_padded) + encryptor.finalize() hasher = Hash(self.digest_algorithm) hasher.update(serialized_request) serialized_request_digest = hasher.finalize() session_key_ciphertext = self.c2_public_key.encrypt( self.session_key, OAEP(MGF1(self.digest_algorithm), self.digest_algorithm, None), ) session_key_ciphertext_size = self.c2_public_key.key_size // 8 serialized_request_digest_size = self.digest_algorithm.digest_size serialized_request_ciphertext_size = len(serialized_request_ciphertext) encrypted_request = struct.pack( '<%ds%ds%ds' % ( session_key_ciphertext_size, serialized_request_digest_size, serialized_request_ciphertext_size ), session_key_ciphertext, serialized_request_digest, serialized_request_ciphertext, ) return encrypted_request
def setup_cipher_rc4(salt, password, encrypt=False): hash = Hash(SHA1(), default_backend()) hash.update(salt + bytes(password, 'ascii')) cipher = Cipher(algorithms.ARC4(hash.finalize()), None, default_backend()) cryptor = cipher.encryptor() if encrypt else cipher.decryptor() cryptor.update(bytes(RC4_SKIP)) return cryptor
def onion_name(key): pub_bytes = key.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.PKCS1) hash = Hash(SHA1(), backend=default_backend()) hash.update(pub_bytes) return b32encode(hash.finalize()[:10]).lower().decode('ascii') + '.onion'
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 _get_ciphertext_digest(ciphertext): if type(ciphertext) is str: ciphertext = bytes(ciphertext, 'utf-8') hash_provider = Hash(SHA512(), default_backend()) hash_provider.update(ciphertext) return base64.b64encode( hash_provider.finalize()).decode('utf-8').rstrip("=")
def setup_cipher_aes(salt, password, encrypt=False): hash = Hash(SHA256(), default_backend()) hash.update(salt + bytes(password, 'ascii')) cipher = Cipher(algorithms.AES(hash.finalize()[:16]), modes.CTR(salt[:16]), default_backend()) cryptor = cipher.encryptor() if encrypt else cipher.decryptor() cryptor.update(bytes(AES_SKIP)) return cryptor
def encrypt_ec(data, ec_public_key): e_private_key, e_public_key = generate_ec_key() shared_secret = e_private_key.exchange(ec.ECDH(), ec_public_key) digest = Hash(SHA256(), backend=_CRYPTO_BACKEND) digest.update(shared_secret) encryption_key = digest.finalize() return unload_ec_public_key(e_public_key) + encrypt_aes_v2( data, encryption_key)
def __init__(self, mail, password, scope=scopes['client']): self.email = mail self.scope = scope h = Hash(SHA256(), default_backend()) h.update(password.encode('utf-8')) self.password = h.finalize()
def sha256(message): """Generates a SHA256 hash of a message""" if isinstance(message, str): message = bytes(message, encoding='utf-8') elif isinstance(message, bytearray): message = bytes(message) digest = Hash(SHA256(), backend=default_backend()) digest.update(message) return digest.finalize()
def get_name(area): halg = tpm2_to_crypto_alg(area.nameAlg) algp = area.nameAlg.to_bytes(2, 'big') pbuf = marshal(area) h = Hash(halg(), default_backend()) h.update(pbuf) dig = h.finalize() name = algp + dig return name
def get_public_key_digest(self): pem_public_key = self.get_pem_public_key() pem_public_key = [x for x in pem_public_key if len(x) > 0] pem_public_key.pop(0) pem_public_key.pop(-1) key_string = ''.join(pem_public_key) hash_provider = Hash(SHA512(), default_backend()) hash_provider.update(bytes(key_string, 'utf-8')) return binascii.hexlify(hash_provider.finalize())
def opdata1_decrypt_master_key(data, key, hmac_key, aes_size=C_AES_SIZE, ignore_hmac=False): key_size = KEY_SIZE[aes_size] bare_key = opdata1_decrypt_item(data, key, hmac_key, aes_size=aes_size, ignore_hmac=ignore_hmac) # XXX: got the following step from jeff@agilebits (as opposed to the # docs anywhere) digest = Hash(SHA512(), backend=_backend) digest.update(bare_key) hashed_key = digest.finalize() return hashed_key[:key_size], hashed_key[key_size:]
def sha512(self, plaintext): """ Perform SHA-512 hash """ digest = Hash(SHA512(), backend=default_backend()) digest.update(bytes(plaintext.encode('UTF-8'))) hash = digest.finalize() #return binascii.hexlify(bytearray(hash)) return base64.b16encode(hash).decode()
def compute_hash(cls, data: bytes) -> bytes: h = Hash(algorithm=cls.get_hash_func(), backend=default_backend()) h.update(data) digest = h.finalize() if cls.trunc_size: digest = digest[:cls.trunc_size] return digest
def decrypt(self, ciphertext: bytes) -> bytes: decryptor = self._cipher.decryptor() # type: ignore assert isinstance(decryptor, CipherContext) plaintext = decryptor.update(ciphertext) + decryptor.finalize() digest = Hash(SHA256()) digest.update(plaintext) computed_hash = digest.finalize() if not bytes_eq(computed_hash, self._data_hash): raise RuntimeError('Decryption error') return plaintext[plaintext[0]:]
def double_sha256(bytestr): hash1 = Hash(SHA256, DEFAULT_BACKEND) hash1.update(bytestr) hash1 = hash1.finalize() hash2 = Hash(SHA256, DEFAULT_BACKEND) hash2.update(hash1) hash2 = hash2.finalize() return hash2
def _shorten_hmac_key(key: bytes) -> bytes: if len(key) > SHA1_BLOCK_SIZE: h = Hash(SHA1(), default_backend()) # nosec h.update(key) key = h.finalize() elif len(key) > HMAC_KEY_SIZE: raise NotSupportedError( "Key lengths > {} bytes not supported".format(HMAC_KEY_SIZE) ) return key
def decrypt(secret, hash, data, file=False): """ Decrypt per telegram docs at https://core.telegram.org/passport. Args: secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a base64 encoded string. hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a base64 encoded string. data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a base64 encoded string. file (:obj:`bool`): Force data to be treated as raw data, instead of trying to b64decode it. Raises: :class:`TelegramDecryptionError`: Given hash does not match hash of decrypted data. Returns: :obj:`bytes`: The decrypted data as bytes. """ # First make sure that if secret, hash, or data was base64 encoded, to decode it into bytes try: secret = b64decode(secret) except (binascii.Error, TypeError): pass try: hash = b64decode(hash) except (binascii.Error, TypeError): pass if not file: try: data = b64decode(data) except (binascii.Error, TypeError): pass # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) digest.update(secret + hash) secret_hash_hash = digest.finalize() # First 32 chars is our key, next 16 is the initialisation vector key, iv = secret_hash_hash[:32], secret_hash_hash[32:32 + 16] # Init a AES-CBC cipher and decrypt the data cipher = Cipher(AES(key), CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() # Calculate SHA256 hash of the decrypted data digest = Hash(SHA256(), backend=default_backend()) digest.update(data) data_hash = digest.finalize() # If the newly calculated hash did not match the one telegram gave us if data_hash != hash: # Raise a error that is caught inside telegram.PassportData and transformed into a warning raise TelegramDecryptionError("Hashes are not equal! {} != {}".format(data_hash, hash)) # Return data without padding return data[bord(data[0]):]
def get_pass(master: str, salt: str, version: Optional[int] = 2) -> str: """ Get derived password from PassHash :param master: Master password :param salt: Site key (e.g. amazon.com) :param version: PassHash version, either 1 or 2 (recommended) :return: """ fullpass = '' if version not in [1, 2]: raise ValueError('Wrong version {}, use either version 1 or 2 ' '(you can find them here http://tinyurl.com/y2qlwnxp)') if version == 2: """In version 2 of PassHash, hash is generated using PBKDF2HMAC-SHA256 using 1000 iterations""" pbkdf2_hash = binascii.hexlify( PBKDF2HMAC(SHA256, 32, salt.encode(), 1000, default_backend()).derive(master.encode())).decode() fullpass = convert(pbkdf2_hash, "0123456789abcdef", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") if version == 1: """In version 1 of PassHash, hash is generated using SHA1(master + salt)""" hashobj = Hash(SHA1(), default_backend()) hashobj.update(master.encode() + salt.encode()) fullpass = convert(binascii.hexlify(hashobj.finalize()).decode(), "0123456789abcdef", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") passwd = "" chars = 0 i = 0 while chars < 12: j = i % 12 if chars == 0 and (fullpass[j:j + 1].upper() != fullpass[j:j + 1]): passwd += fullpass[j:j + 1] chars += 1 elif chars == 1 and (parse_int(fullpass[j:j + 1]) != -99): passwd += fullpass[j:j + 1] chars += 1 elif chars == 2 and (fullpass[j:j + 1].lower() != fullpass[j:j + 1]): passwd += fullpass[j:j + 1] chars += 1 elif chars > 2: passwd += fullpass[j:j + 1] chars += 1 elif chars == 0 and i >= len(fullpass): passwd += 'f' chars += 1 elif chars == 1 and i >= len(fullpass) * 2: passwd += '7' chars += 1 elif chars == 2 and i >= len(fullpass) * 3: passwd += 'T' chars += 1 i += 1 return passwd
def compute_verify_data(self, basekey, handshake_context): hash_len = self.hash.digest_size finished_key = self.expand_label(basekey, b"finished", b"", hash_len) h = Hash(self.hash, backend=default_backend()) h.update(handshake_context) hash_value = h.finalize() hm = HMAC(finished_key, self.hash, default_backend()) hm.update(hash_value) return hm.finalize()
def login(email, password): user = User.query.filter(User.email == email).first() h = Hash(SHA256(), default_backend()) h.update(password.encode('utf-8')) if not user.password == h.finalize(): return None else: return user
def generate(time: datetime) -> "Challenge": assert isinstance(time, datetime) ts = int(time.timestamp()) ts = ts.to_bytes((ts.bit_length() + 7) // 8, 'big') rs = os.urandom(Challenge._hash_algo.digest_size) h = Hash(Challenge._hash_algo(), backend=default_backend()) h.update(ts) h.update(rs) return Challenge(h.finalize())
def file_hash(f): ctx = SHA512() hasher = Hash(ctx, default_backend()) while True: data = f.read(1024) if not data: break hasher.update(data) digest = hasher.finalize() alg = ECDSA(Prehashed(ctx)) return digest, alg
def alternative_evmctl_check(file_path, pubkey): # In RHEL7, evmctl is too old, so we won't be able to run the # evmctl check ima_sig = bytearray(xattr.getxattr(file_path, 'user.ima')) if ima_sig[0] != 3: raise Exception("IMA signature has wrong prefix (%s)" % ima_sig[0]) if ima_sig[1] != 2: raise Exception("IMA signature has wrong version (%s)" % ima_sig[1]) algo_id = ima_sig[2] if algo_id == 7: # SHA224 hasher = hashlib.sha224() crypto_algo = crypto_hashes.SHA224() elif algo_id == 4: # SHA256 hasher = hashlib.sha256() crypto_algo = crypto_hashes.SHA256() elif algo_id == 5: # SHA384 hasher = hashlib.sha384() crypto_algo = crypto_hashes.SHA384() elif algo_id == 6: # SHA512 hasher = hashlib.sha512() crypto_algo = crypto_hashes.SHA512() else: raise Exception("IMA signature has invalid algo: %d" % algo_id) crypto_algo = Prehashed(crypto_algo) if sys.version_info.major == 3: # X962 is only supported on Cryptography 2.5+ # We are a bit lazy and just check for py3 instead of checking this more carefully # Check the Key ID key_id = ima_sig[3:7] keybytes = pubkey.public_bytes( crypto_serialization.Encoding.X962, crypto_serialization.PublicFormat.UncompressedPoint, ) keybytes_digester = Hash(SHA1()) keybytes_digester.update(keybytes) keybytes_digest = keybytes_digester.finalize() correct_keyid = keybytes_digest[-4:] if correct_keyid != key_id: raise Exception("IMA signature has invalid key ID: %s != %s" % (correct_keyid, key_id)) # Check the signature itself (sig_size,) = struct.unpack('>H', ima_sig[7:9]) sig = ima_sig[9:] if len(sig) != sig_size: raise Exception("IMA signature size invalid: %d != %d" % (len(sig), sig_size)) with open(file_path, 'rb') as f: hasher.update(f.read()) file_digest = hasher.digest() pubkey.verify( bytes(sig), bytes(file_digest), crypto_ec.ECDSA(crypto_algo), )
def derive(self, key_material): if not isinstance(key_material, bytes): raise TypeError("key_material must be bytes.") h = Hash(self._algorithm(), backend=self._backend) h.update(key_material) h.update(self._salt) derived_key = h.finalize() for i in xrange(self._iterations-1): h = Hash(self._algorithm(), backend=self._backend) h.update(derived_key) derived_key = h.finalize() return derived_key
def a_decrypt_item(data, key, aes_size=A_AES_SIZE): key_size = KEY_SIZE[aes_size] if data[:len(SALT_MARKER)] == SALT_MARKER: salt = data[len(SALT_MARKER):len(SALT_MARKER) + SALT_SIZE] data = data[len(SALT_MARKER) + SALT_SIZE:] pb_gen = pbkdf1.PBKDF1(key, salt) nkey = pb_gen.read(key_size) iv = pb_gen.read(key_size) else: digest = Hash(MD5(), backend=_backend) digest.update(key) nkey = digest.finalize() iv = '\x00'*key_size aes = Cipher(algorithms.AES(nkey), modes.CBC(iv), backend=_backend) decryptor = aes.decryptor() return padding.pkcs5_unpad(decryptor.update(data) + decryptor.finalize())
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 _generate_property_iv(entity_iv, pk, rk, property_name, isJavaV1): ''' Uses the entity_iv, partition key, and row key to generate and return the iv for the specified property. ''' digest = Hash(SHA256(), default_backend()) if not isJavaV1: digest.update(entity_iv + (rk + pk + property_name).encode('utf-8')) else: digest.update(entity_iv + (pk + rk + property_name).encode('utf-8')) propertyIV = digest.finalize() return propertyIV[:16]
def _get_digest(self, data, digest_algorithm): hasher = Hash(algorithm=digest_algorithm, backend=default_backend()) hasher.update(data) return ensure_str(b64encode(hasher.finalize()))
def sha1(msg): sha = Hash(SHA1(), backend=bend) sha.update(msg) return sha.finalize()
def derive_secret(self, secret, label, messages): h = Hash(self.hash, backend=default_backend()) h.update(messages) hash_messages = h.finalize() hash_len = self.hash.digest_size return self.expand_label(secret, label, hash_messages, hash_len)
class ntor(object): def __init__(self, node): # 5.1.4. The "ntor" handshake # This handshake uses a set of DH handshakes to compute a set of # shared keys which the client knows are shared only with a particular # server, and the server knows are shared with whomever sent the # original handshake (or with nobody at all). Here we use the # "curve25519" group and representation as specified in "Curve25519: # new Diffie-Hellman speed records" by D. J. Bernstein. # [The ntor handshake was added in Tor 0.2.4.8-alpha.] self.node = node # In this section, define: # H(x,t) as HMAC_SHA256 with message x and key t. # H_LENGTH = 32. # ID_LENGTH = 20. # G_LENGTH = 32 # PROTOID = "ntor-curve25519-sha256-1" # t_mac = PROTOID | ":mac" # t_key = PROTOID | ":key_extract" # t_verify = PROTOID | ":verify" # MULT(a,b) = the multiplication of the curve25519 point 'a' by the # scalar 'b'. # G = The preferred base point for curve25519 ([9]) # KEYGEN() = The curve25519 key generation algorithm, returning # a private/public keypair. # m_expand = PROTOID | ":key_expand" # H is defined as hmac() # MULT is included in the curve25519 library as get_shared_key() # KEYGEN() is curve25519.Private() self.protoid = 'ntor-curve25519-sha256-1' self.t_mac = self.protoid + ':mac' self.t_key = self.protoid + ':key_extract' self.t_verify = self.protoid + ':verify' self.m_expand = self.protoid + ':key_expand' # To perform the handshake, the client needs to know an identity key # digest for the server, and an ntor onion key (a curve25519 public # key) for that server. Call the ntor onion key "B". The client # generates a temporary keypair: # x,X = KEYGEN() self.x = curve25519.Private() self.X = self.x.get_public() self.B = curve25519.Public(b64decode(self.node['ntor-onion-key'])) # and generates a client-side handshake with contents: # NODEID Server identity digest [ID_LENGTH bytes] # KEYID KEYID(B) [H_LENGTH bytes] # CLIENT_PK X [G_LENGTH bytes] self.handshake = b64decode(self.node['identity']) self.handshake += self.B.serialize() self.handshake += self.X.serialize() def complete_handshake(self, Y, auth): # The server's handshake reply is: # SERVER_PK Y [G_LENGTH bytes] # AUTH H(auth_input, t_mac) [H_LENGTH bytes] # The client then checks Y is in G^* [see NOTE below], and computes # secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID si = self.x.get_shared_key(curve25519.Public(Y), hash_func) si += self.x.get_shared_key(self.B, hash_func) si += b64decode(self.node['identity']) si += self.B.serialize() si += self.X.serialize() si += Y si += 'ntor-curve25519-sha256-1' # KEY_SEED = H(secret_input, t_key) # verify = H(secret_input, t_verify) key_seed = hmac(self.t_key, si) verify = hmac(self.t_verify, si) # auth_input = verify | ID | B | Y | X | PROTOID | "Server" ai = verify ai += b64decode(self.node['identity']) ai += self.B.serialize() ai += Y ai += self.X.serialize() ai += self.protoid ai += 'Server' # The client verifies that AUTH == H(auth_input, t_mac). if auth != hmac(self.t_mac, ai): raise NtorError('auth input does not match.') # Both parties check that none of the EXP() operations produced the # point at infinity. [NOTE: This is an adequate replacement for # checking Y for group membership, if the group is curve25519.] # Both parties now have a shared value for KEY_SEED. They expand this # into the keys needed for the Tor relay protocol, using the KDF # described in 5.2.2 and the tag m_expand. # 5.2.2. KDF-RFC5869 # For newer KDF needs, Tor uses the key derivation function HKDF from # RFC5869, instantiated with SHA256. (This is due to a construction # from Krawczyk.) The generated key material is: # K = K_1 | K_2 | K_3 | ... # Where H(x,t) is HMAC_SHA256 with value x and key t # and K_1 = H(m_expand | INT8(1) , KEY_SEED ) # and K_(i+1) = H(K_i | m_expand | INT8(i+1) , KEY_SEED ) # and m_expand is an arbitrarily chosen value, # and INT8(i) is a octet with the value "i". # In RFC5869's vocabulary, this is HKDF-SHA256 with info == m_expand, # salt == t_key, and IKM == secret_input. keys = hkdf(key_seed, length=72, info=self.m_expand) # When used in the ntor handshake, the first HASH_LEN bytes form the # forward digest Df; the next HASH_LEN form the backward digest Db; the # next KEY_LEN form Kf, the next KEY_LEN form Kb, and the final # DIGEST_LEN bytes are taken as a nonce to use in the place of KH in the # hidden service protocol. Excess bytes from K are discarded. Df, Db, Kf, Kb = struct.unpack('>20s20s16s16s', keys) # we do what we can with what we've got. del self.X del self.x del self.B del key_seed del keys del verify del ai del auth del si del Y self.send_digest = Hash(SHA1(), backend=bend) self.send_digest.update(Df) self.recv_digest = Hash(SHA1(), backend=bend) self.recv_digest.update(Db) self.encrypt = Cipher(AES(Kf), CTR('\x00' * 16), backend=bend).encryptor() self.decrypt = Cipher(AES(Kb), CTR('\x00' * 16), backend=bend).decryptor() def get_handshake(self): return self.handshake
def complete_handshake(self, Y, auth): # The server's handshake reply is: # SERVER_PK Y [G_LENGTH bytes] # AUTH H(auth_input, t_mac) [H_LENGTH bytes] # The client then checks Y is in G^* [see NOTE below], and computes # secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID si = self.x.get_shared_key(curve25519.Public(Y), hash_func) si += self.x.get_shared_key(self.B, hash_func) si += b64decode(self.node['identity']) si += self.B.serialize() si += self.X.serialize() si += Y si += 'ntor-curve25519-sha256-1' # KEY_SEED = H(secret_input, t_key) # verify = H(secret_input, t_verify) key_seed = hmac(self.t_key, si) verify = hmac(self.t_verify, si) # auth_input = verify | ID | B | Y | X | PROTOID | "Server" ai = verify ai += b64decode(self.node['identity']) ai += self.B.serialize() ai += Y ai += self.X.serialize() ai += self.protoid ai += 'Server' # The client verifies that AUTH == H(auth_input, t_mac). if auth != hmac(self.t_mac, ai): raise NtorError('auth input does not match.') # Both parties check that none of the EXP() operations produced the # point at infinity. [NOTE: This is an adequate replacement for # checking Y for group membership, if the group is curve25519.] # Both parties now have a shared value for KEY_SEED. They expand this # into the keys needed for the Tor relay protocol, using the KDF # described in 5.2.2 and the tag m_expand. # 5.2.2. KDF-RFC5869 # For newer KDF needs, Tor uses the key derivation function HKDF from # RFC5869, instantiated with SHA256. (This is due to a construction # from Krawczyk.) The generated key material is: # K = K_1 | K_2 | K_3 | ... # Where H(x,t) is HMAC_SHA256 with value x and key t # and K_1 = H(m_expand | INT8(1) , KEY_SEED ) # and K_(i+1) = H(K_i | m_expand | INT8(i+1) , KEY_SEED ) # and m_expand is an arbitrarily chosen value, # and INT8(i) is a octet with the value "i". # In RFC5869's vocabulary, this is HKDF-SHA256 with info == m_expand, # salt == t_key, and IKM == secret_input. keys = hkdf(key_seed, length=72, info=self.m_expand) # When used in the ntor handshake, the first HASH_LEN bytes form the # forward digest Df; the next HASH_LEN form the backward digest Db; the # next KEY_LEN form Kf, the next KEY_LEN form Kb, and the final # DIGEST_LEN bytes are taken as a nonce to use in the place of KH in the # hidden service protocol. Excess bytes from K are discarded. Df, Db, Kf, Kb = struct.unpack('>20s20s16s16s', keys) # we do what we can with what we've got. del self.X del self.x del self.B del key_seed del keys del verify del ai del auth del si del Y self.send_digest = Hash(SHA1(), backend=bend) self.send_digest.update(Df) self.recv_digest = Hash(SHA1(), backend=bend) self.recv_digest.update(Db) self.encrypt = Cipher(AES(Kf), CTR('\x00' * 16), backend=bend).encryptor() self.decrypt = Cipher(AES(Kb), CTR('\x00' * 16), backend=bend).decryptor()
def sha1_hash(data): sha = Hash(SHA1(), backend) sha.update(data) return sha.finalize()
# new Z2 instead of Kz in the KDF KDF2_input = b'EAP-NOOB' + base64url_decode(Np2_b64) + base64url_decode(Ns2_b64) KDF2_out = KDF(algorithm=SHA256(), length=288, otherinfo=KDF2_input, backend=default_backend()).derive(Kz) Kms2 = KDF2_out[224:256] Kmp2 = KDF2_out[256:288] # Remove trailing '=' from base64 encoded values Np_b64 = Np_b64.strip('=') Ns_b64 = Ns_b64.strip('=') Np2_b64 = Np2_b64.strip('=') Ns2_b64 = Ns2_b64.strip('=') Noob_b64 = Noob_b64.strip('=') ## NoobId NoobId_input = Hash(SHA256(), backend=default_backend()) NoobId_input.update(b'NoobId') NoobId_input.update(Noob_b64.encode()) NoobId = NoobId_input.finalize()[:16] NoobId_b64 = base64url_encode(NoobId).decode().strip('=') ## Hoob Hoob_values = loads('{"Hoob":[]}', object_pairs_hook=OrderedDict) Hoob_values['Hoob'] = [Dir, Vers, Verp, PeerId, Cryptosuites, Dirs, loads(ServerInfo, object_pairs_hook=OrderedDict), Cryptosuitep, Dirp, Realm, loads(PeerInfo, object_pairs_hook=OrderedDict), 0, PKs_full, Ns_b64, PKp_full, Np_b64, Noob_b64] Hoob_input = Hash(SHA256(), backend=default_backend()) Hoob_input.update(dumps(Hoob_values['Hoob'], separators=(',', ':')).encode()) Hoob = Hoob_input.finalize()[:16] Hoob_b64 = base64url_encode(Hoob).decode().strip('=')
def digest(inp): h = Hash(self._algorithm(), backend=self._backend) h.update(inp) return h.finalize()
def test_validate_encryption(self): # Arrange entity = self._create_default_entity_for_encryption() key_encryption_key = KeyWrapper('key1') self.ts.key_encryption_key = key_encryption_key self.ts.insert_entity(self.table_name, entity) # Act self.ts.key_encryption_key = None entity = self.ts.get_entity(self.table_name, entity['PartitionKey'], entity['RowKey']) # Note the minor discrepancy from the normal decryption process: because the entity was retrieved # without being decrypted, the encrypted_properties list is now stored in an EntityProperty object # and is already raw bytes. encrypted_properties_list = entity['_ClientEncryptionMetadata2'].value encryption_data = entity['_ClientEncryptionMetadata1'] encryption_data = _dict_to_encryption_data(loads(encryption_data)) content_encryption_key = key_encryption_key.unwrap_key(encryption_data.wrapped_content_key.encrypted_key, encryption_data.wrapped_content_key.algorithm) digest = Hash(SHA256(), default_backend()) digest.update(encryption_data.content_encryption_IV + (entity['RowKey'] + entity['PartitionKey'] + '_ClientEncryptionMetadata2').encode('utf-8')) metadataIV = digest.finalize() metadataIV = metadataIV[:16] cipher = _generate_AES_CBC_cipher(content_encryption_key, metadataIV) # Decrypt the data. decryptor = cipher.decryptor() encrypted_properties_list = decryptor.update(encrypted_properties_list) + decryptor.finalize() # Unpad the data. unpadder = PKCS7(128).unpadder() encrypted_properties_list = unpadder.update(encrypted_properties_list) + unpadder.finalize() encrypted_properties_list = encrypted_properties_list.decode('utf-8') # Strip the square braces from the ends and split string into list. encrypted_properties_list = loads(encrypted_properties_list) entity_iv, encrypted_properties, content_encryption_key = \ (encryption_data.content_encryption_IV, encrypted_properties_list, content_encryption_key) decrypted_entity = deepcopy(entity) for property in encrypted_properties_list: value = entity[property] digest = Hash(SHA256(), default_backend()) digest.update(entity_iv + (entity['RowKey'] + entity['PartitionKey'] + property).encode('utf-8')) propertyIV = digest.finalize() propertyIV = propertyIV[:16] cipher = _generate_AES_CBC_cipher(content_encryption_key, propertyIV) # Decrypt the property. decryptor = cipher.decryptor() decrypted_data = (decryptor.update(value.value) + decryptor.finalize()) # Unpad the data. unpadder = PKCS7(128).unpadder() decrypted_data = (unpadder.update(decrypted_data) + unpadder.finalize()) decrypted_data = decrypted_data.decode('utf-8') decrypted_entity[property] = decrypted_data decrypted_entity.pop('_ClientEncryptionMetadata1') decrypted_entity.pop('_ClientEncryptionMetadata2') # Assert self.assertEqual(decrypted_entity['sex'], 'male')