def verify1(self, credentials, session_pub_key, encrypted): """First verification step.""" # No additional hashing used self._shared = self._verify_private.get_shared_key( curve25519.Public(session_pub_key), hashfunc=lambda x: x) session_key = hkdf_expand('Pair-Verify-Encrypt-Salt', 'Pair-Verify-Encrypt-Info', self._shared) chacha = chacha20.Chacha20Cipher(session_key, session_key) decrypted_tlv = tlv8.read_tlv( chacha.decrypt(encrypted, nounce='PV-Msg02'.encode())) identifier = decrypted_tlv[tlv8.TLV_IDENTIFIER] signature = decrypted_tlv[tlv8.TLV_SIGNATURE] if identifier != credentials.atv_id: raise Exception('incorrect device response') # TODO: new exception info = session_pub_key + \ bytes(identifier) + self._verify_public.serialize() ltpk = VerifyingKey(bytes(credentials.ltpk)) ltpk.verify(bytes(signature), bytes(info)) # throws if no match device_info = self._verify_public.serialize() + \ credentials.client_id + session_pub_key device_signature = SigningKey(credentials.ltsk).sign(device_info) tlv = tlv8.write_tlv({tlv8.TLV_IDENTIFIER: credentials.client_id, tlv8.TLV_SIGNATURE: device_signature}) return chacha.encrypt(tlv, nounce='PV-Msg03'.encode())
def decrypt_message(self, ct, decryptor): decryptor = self.derive_keys(decryptor) pt = self.decrypt(ct, decryptor['message_key'], decryptor['message_counter']) if not pt: return None bob_ephemeral, msg = pt[:32], pt[32:] self.receive = decryptor # it's encrypted with the same key it advertised.. shouldn't happen. if bob_ephemeral == self.receive['bob'].serialize(): raise # it's advertising the receive_pending key, we need to respond again because # they may not have gotten it. elif self.receive_pending \ and bob_ephemeral == self.receive_pending['bob'].serialize(): pass # yay a new key! we need to respond. elif not self.receive_pending \ or bob_ephemeral != self.receive_pending['bob'].serialize(): self.receive_pending = { 'alice': curve25519.Private(), 'bob': curve25519.Public(bob_ephemeral), 'receiver': True, 'acked': False } # uncharted territory... else: raise return msg
def recover_seed(key='', modulus=None, pos=1): # recreate the master private key from the passphrase master = curve25519.Private(secret=sha256(key).digest()) # extract the ephemeral public key from modulus ephem_pub = curve25519.Public(modulus[pos:pos + 32]) # compute seed with master private and ephemeral public return (master.get_shared_key(ephem_pub), ephem_pub)
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 setkey(data, server, witem): data = data.split(' ') try: keys[data[0]] = curve25519.Public(b64decode(data[1])) save_keys() except: print 'Usage: /setkey <nick> <key>.' print 'Set key for %s to: %s' % (data[0], data[1]) return
def setConnInfoParams(self, secret): self.secret = secret self.sharedSecret = self.privateKey.get_shared_key(curve25519.Public(secret[:32]), lambda a: a) sharedSecretExpanded = HKDF(self.sharedSecret, 80) hmacValidation = HmacSha256(sharedSecretExpanded[32:64], secret[:32] + secret[64:]) if hmacValidation != secret[32:64]: raise ValueError("Hmac mismatch") keysEncrypted = sharedSecretExpanded[64:] + secret[64:] keysDecrypted = AESDecrypt(sharedSecretExpanded[:32], keysEncrypted) self.encKey = keysDecrypted[:32] self.macKey = keysDecrypted[32:64]
def _seqno_1(self, pairing_data): if self.has_paired: server_pub_key = self._verify_public.serialize() client_pub_key = pairing_data[tlv8.TLV_PUBLIC_KEY] self._shared = self._verify_private.get_shared_key( curve25519.Public(client_pub_key), hashfunc=lambda x: x) session_key = hkdf_expand('Pair-Verify-Encrypt-Salt', 'Pair-Verify-Encrypt-Info', self._shared) info = server_pub_key + self.atv_device_id + client_pub_key signature = SigningKey(self._signing_key.to_seed()).sign(info) tlv = tlv8.write_tlv({ tlv8.TLV_IDENTIFIER: self.atv_device_id, tlv8.TLV_SIGNATURE: signature }) chacha = chacha20.Chacha20Cipher(session_key, session_key) encrypted = chacha.encrypt(tlv, nounce='PV-Msg02'.encode()) msg = messages.crypto_pairing({ tlv8.TLV_SEQ_NO: b'\x02', tlv8.TLV_PUBLIC_KEY: server_pub_key, tlv8.TLV_ENCRYPTED_DATA: encrypted }) self.output_key = hkdf_expand('MediaRemote-Salt', 'MediaRemote-Write-Encryption-Key', self._shared) self.input_key = hkdf_expand('MediaRemote-Salt', 'MediaRemote-Read-Encryption-Key', self._shared) log_binary(_LOGGER, 'Keys', Output=self.output_key, Input=self.input_key) else: msg = messages.crypto_pairing({ tlv8.TLV_SALT: binascii.unhexlify(self.salt), tlv8.TLV_PUBLIC_KEY: binascii.unhexlify(self._session.public), tlv8.TLV_SEQ_NO: b'\x02' }) self._send(msg)
def load_keys(): try: tmp_keys = json.load(open(key_path)) fix_key_perms() except: keys['my_key'] = curve25519.Private() save_keys() return for key in tmp_keys: if key == 'my_key': keys[key] = curve25519.Private(b64decode(tmp_keys[key])) else: keys[key] = curve25519.Public(b64decode(tmp_keys[key]))
async def _generate_final_keys(self, secret): self.secret = base64.b64decode(secret) self.shared_secret = self.private.get_shared_key( curve25519.Public(self.secret[:32]), lambda a: a) self.shared_secret_expended = hkdf(80, self.shared_secret) if not self._is_final_valid(): raise ValueError('hmac final keys mismatch') enc_key = self.shared_secret_expended[64:] + self.secret[64:] denc_key = AESDecrypt(self.shared_secret_expended[:32], enc_key) self.enc_key = denc_key[:32] self.mac_key = denc_key[32:64] self._ready.set()
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Key is hashed before being returned, we don't want it; This fixes that. lambda x: x, ) mac = self.state.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self.state.private_key.sign(material) output_key = hap_hkdf(shared_key, self.PVERIFY_1_SALT, self.PVERIFY_1_INFO) self._set_encryption_ctx(client_public, private_key, public_key, shared_key, output_key) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = ChaCha20Poly1305(output_key) aead_message = bytes( cipher.encrypt(self.PVERIFY_1_NONCE, bytes(message), b"")) data = tlv.encode( HAP_TLV_TAGS.SEQUENCE_NUM, b"\x02", HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize(), ) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def __init__(self, ref_dict, priv_key_list, pub_key_list): self.conn_data = { "private_key": None, "public_key": None, "secret": None, "shared_secret": None, "shared_secret_ex": None, "aes_key": None, "mac_key": None } self.conn_data["private_key"] = curve25519.Private("".join( [chr(x) for x in priv_key_list])) self.conn_data["public_key"] = self.conn_data[ "private_key"].get_public() assert (self.conn_data["public_key"].serialize() == "".join( [chr(x) for x in pub_key_list])) self.conn_data["secret"] = base64.b64decode(ref_dict["secret"]) self.conn_data["shared_secret"] = self.conn_data[ "private_key"].get_shared_key( curve25519.Public(self.conn_data["secret"][:32]), lambda key: key) shared_expended = self.conn_data["shared_secret_ex"] = HKDF( self.conn_data["shared_secret"], 80) check_hmac = hmac_sha256( shared_expended[32:64], self.conn_data["secret"][:32] + self.conn_data["secret"][64:]) if check_hmac != self.conn_data["secret"][32:64]: raise ValueError("Error hmac mismatch") keysDecrypted = AESDecrypt( shared_expended[:32], shared_expended[64:] + self.conn_data["secret"][64:]) self.conn_data["aes_key"] = keysDecrypted[:32] self.conn_data["mac_key"] = keysDecrypted[32:64]
def verify2(self, atv_public_key, data): """ Last device verification step. """ # Generate a shared secret key public = curve25519.Public(atv_public_key) shared = self._verify_private.get_shared_key( public, hashfunc=lambda x: x) # No additional hashing used # Derive new AES key and IV from shared key aes_key = hash_sha512('Pair-Verify-AES-Key', shared)[0:16] aes_iv = hash_sha512('Pair-Verify-AES-IV', shared)[0:16] # Sign public keys and encrypt with AES signer = SigningKey(self._auth_private) signed = signer.sign(self._verify_public.serialize() + atv_public_key) signature, _ = aes_encrypt(modes.CTR, aes_key, aes_iv, data, signed) # Signature is prepended with 0x00000000 (alignment?) return b'\x00\x00\x00\x00' + signature
def _seqno1_paired(self, pairing_data): server_pub_key = self.keys.verify_pub.serialize() client_pub_key = pairing_data[tlv8.TLV_PUBLIC_KEY] shared_key = self.keys.verify.get_shared_key( curve25519.Public(client_pub_key), hashfunc=lambda x: x) session_key = hkdf_expand('Pair-Verify-Encrypt-Salt', 'Pair-Verify-Encrypt-Info', shared_key) info = server_pub_key + self.unique_id + client_pub_key signature = self.keys.sign.sign(info) tlv = tlv8.write_tlv({ tlv8.TLV_IDENTIFIER: self.unique_id, tlv8.TLV_SIGNATURE: signature }) chacha = chacha20.Chacha20Cipher(session_key, session_key) encrypted = chacha.encrypt(tlv, nounce='PV-Msg02'.encode()) msg = messages.crypto_pairing({ tlv8.TLV_SEQ_NO: b'\x02', tlv8.TLV_PUBLIC_KEY: server_pub_key, tlv8.TLV_ENCRYPTED_DATA: encrypted }) self.output_key = hkdf_expand('MediaRemote-Salt', 'MediaRemote-Write-Encryption-Key', shared_key) self.input_key = hkdf_expand('MediaRemote-Salt', 'MediaRemote-Read-Encryption-Key', shared_key) log_binary(_LOGGER, 'Keys', Output=self.output_key, Input=self.input_key) self.delegate.send(msg)
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logging.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Key is hashed before being returned, we don't want it; This fixes that. lambda x: x) mac = self._state.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self._state.private_key.sign(material) output_key = hap_hkdf(shared_key, PVERIFY_1_SALT, PVERIFY_1_INFO) enc_context = { "client_public": client_public, "private_key": private_key, "public_key": public_key, "shared_key": shared_key, "pre_session_key": output_key } message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = CHACHA20_POLY1305(output_key, "python") aead_message = bytes( cipher.seal(PVERIFY_1_NONCE, bytearray(message), b"")) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize()) return data, enc_context
def _pair_verify_one(self, tlv_objects): """Generate new session key pair and send a proof to the client. @param tlv_objects: The TLV data received from the client. @type tlv_object: dict """ logger.debug("Pair verify [1/2].") client_public = tlv_objects[HAP_TLV_TAGS.PUBLIC_KEY] private_key = curve25519.Private() public_key = private_key.get_public() shared_key = private_key.get_shared_key( curve25519.Public(client_public), # Why not randomly hash the key before returning it # so that it is incompatible with another # implementation and force clients to use hacks? lambda x: x) mac = self.accessory.mac.encode() material = public_key.serialize() + mac + client_public server_proof = self.accessory.private_key.sign(material) output_key = hap_hkdf(shared_key, self.PVERIFY_1_SALT, self.PVERIFY_1_INFO) self._set_encryption_ctx(client_public, private_key, public_key, shared_key, output_key) message = tlv.encode(HAP_TLV_TAGS.USERNAME, mac, HAP_TLV_TAGS.PROOF, server_proof) cipher = CHACHA20_POLY1305(output_key, "python") aead_message = bytes( cipher.seal(self.PVERIFY_1_NONCE, bytearray(message), b"")) data = tlv.encode(HAP_TLV_TAGS.SEQUENCE_NUM, b'\x02', HAP_TLV_TAGS.ENCRYPTED_DATA, aead_message, HAP_TLV_TAGS.PUBLIC_KEY, public_key.serialize()) self.send_response(200) self.send_header("Content-Type", self.PAIRING_RESPONSE_TYPE) self.end_response(data)
def get_enc_mac_keys(encoded_secret, privateKey): secret = b64decode(encoded_secret) if len(secret) != 144: raise ValueError('Invalid secret size') sharedSecret = privateKey.get_shared_key(curve25519.Public(secret[:32]), lambda a: a) key_material = Hmac(b'\0' * 32).hash(sharedSecret) sharedSecretExpanded = hkdf_expand(key_material, length=80, hash=hashlib.sha256) if not Hmac(sharedSecretExpanded[32:64]).is_valid( secret[:32] + secret[64:], expected=secret[32:64]): raise ValueError('Hmac validation failed') keysEncrypted = sharedSecretExpanded[64:] + secret[64:] keysDecrypted = Aes(sharedSecretExpanded[:32]).decrypt(keysEncrypted) encKey = keysDecrypted[:32] macKey = keysDecrypted[32:64] return encKey, macKey
def __init__(self, ref_dict, priv_key_list, pub_key_list): self.decrypted_serialized_content = None self.message_tag = None self.decrypted_content = None self.original_content = None self.private_key = None self.public_key = None self.secret = None self.shared_secret = None self.aes_key = None self.mac_key = None self.private_key = curve25519.Private("".join( [chr(x) for x in priv_key_list])) self.public_key = self.private_key.get_public() assert (self.public_key.serialize() == "".join( [chr(x) for x in pub_key_list])) self.secret = base64.b64decode(ref_dict["secret"]) self.shared_secret = self.private_key.get_shared_key( curve25519.Public(self.secret[:32]), lambda key: key) shared_expended = HKDF(self.shared_secret, 80) check_hmac = hmac_sha256(shared_expended[32:64], self.secret[:32] + self.secret[64:]) if check_hmac != self.secret[32:64]: raise ValueError("Error hmac mismatch") keys_decrypted = aes_decrypt(shared_expended[:32], shared_expended[64:] + self.secret[64:]) self.aes_key = keys_decrypted[:32] self.mac_key = keys_decrypted[32:64]
if __name__ == "__main__": # passphrase and filename as arguments if len(sys.argv) == 3: # Load an x.509 certificate from a file x509 = X509.load_cert(sys.argv[2]) # Pull the modulus out of the certificate orig_modulus = unhexlify(x509.get_pubkey().get_modulus()) (seed, ephem_pub) = recover_seed(key=sys.argv[1], modulus=orig_modulus, pos=80) # no arguments, just generate a private key else: # deserialize master ECDH public key embedded in program master_pub = curve25519.Public(unhexlify(MASTER_PUB_HEX)) # generate a random (yes, actually random) ECDH private key ephem = curve25519.Private() # derive the corresponding public key for later embedding ephem_pub = ephem.get_public() # combine the ECDH keys to generate the seed seed = ephem.get_shared_key(master_pub) prng = AESPRNG(seed) ephem_pub = ephem_pub.serialize() # deterministic key generation from seed rsa = build_key(embed=ephem_pub, pos=80, randfunc=prng.randbytes) if 'orig_modulus' in locals(): if long(hexlify(orig_modulus), 16) != long(rsa.n):
def receive_key(self, bob_ephemeral): self.receive['bob'] = curve25519.Public(bob_ephemeral) if 'counter' in self.receive: del self.receive['counter']
def send_key(self, bob_ephemeral): self.send['bob'] = curve25519.Public(bob_ephemeral) if 'counter' in self.send: del self.send['counter']
def pairVerifyStep1(self, tlvData): publicKey = tlvData['public_key']['data'] accessoryLTSK = ed25519.SigningKey(self.longTermKey, encoding='hex') # accessoryLTPK = accessoryLTSK.get_verifying_key() accessoryPairingID = HapHandler.getId() iosDevicePublicKey = curve25519.Public(''.join( [chr(x) for x in publicKey])) # Step 1 private = curve25519.Private() public = private.get_public() publicSerialized = public.serialize() # Step 2 shared = private.get_shared_key(iosDevicePublicKey, hashfunc=lambda x: x) # Step 3 accessoryPairingInfo = publicSerialized + accessoryPairingID + iosDevicePublicKey.serialize( ) # Step 4 accessorySignature = accessoryLTSK.sign(accessoryPairingInfo) # Step 5 response = [] response.append({ 'type': 'identifier', 'length': len(accessoryPairingID), 'data': accessoryPairingID }) response.append({ 'type': 'signature', 'length': len(accessorySignature), 'data': accessorySignature }) subTLV = pack(response) # tlv # Step 6 inputKey = shared salt = 'Pair-Verify-Encrypt-Salt' info = 'Pair-Verify-Encrypt-Info' key = Hkdf(salt, inputKey, hash=hashlib.sha512) sessionKey = key.expand(info, length=32) key = Hkdf('Control-Salt', shared, hash=hashlib.sha512) writeKey = key.expand('Control-Write-Encryption-Key', length=32) key = Hkdf('Control-Salt', shared, hash=hashlib.sha512) readKey = key.expand('Control-Read-Encryption-Key', length=32) self.sessionStorage = { 'clientPublicKey': iosDevicePublicKey.serialize(), 'secretKey': private.serialize(), 'publicKey': publicSerialized, 'sharedSec': shared, 'hkdfPairEncKey': sessionKey, 'writeKey': writeKey, 'readKey': readKey } # Step 7 ciphertext, mac = HapHandler.encryptAndSeal(sessionKey, 'PV-Msg02', [ord(x) for x in subTLV]) self.sendTLVResponse([ { 'type': 'state', 'length': 1, 'data': 2 }, { 'type': 'public_key', 'length': len(publicSerialized), 'data': publicSerialized }, { 'type': 'encrypted_data', 'length': len(ciphertext + mac), 'data': ciphertext + mac }, ])
def get_shared(self, public): public = curve25519.Public(public) return self._private.get_shared_key(public, hashfunc=lambda x: x)
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()