def rsa_sign(self, msg, user_pri_key): signer = user_pri_key.signer( PSS(mgf=MGF1(SHA512()), salt_length=PSS.MAX_LENGTH), SHA512()) signer.update(msg) signature = signer.finalize() sig_length = (len(signature)).to_bytes(4, byteorder='big') return sig_length + signature + msg
def validate_machine_authentication(self, authentication_string, provided_nonce): # Ensure the input string is JSON and contains all required fields validate_set = ["key_digest", "nonce_digest", "signature"] try: if type(authentication_string) is bytes: authentication_string = authentication_string.decode('utf-8') authentication_dict = json.loads(authentication_string) for field in validate_set: field_value = authentication_dict[field] if not type(field_value) == str: print( "[!] Invalid data supplied in authentication string.") raise ValueError # Ensure the nonce hash has been computed properly before continuing for i in range(0, 10000): provided_nonce = self._compute_hash(provided_nonce) if not provided_nonce == authentication_dict["nonce_digest"]: print(provided_nonce) print(authentication_dict["nonce_digest"]) print( "[!] Nonce digest does not match our digest of provided nonce." ) raise ValueError # Check if we have a public key on file for the provided public key digest stored_machine_info = self._execute_query( "SELECT * FROM authorized_machines " "WHERE machine_key_digest=? LIMIT 1", (authentication_dict["key_digest"], )) if stored_machine_info: stored_machine_info = stored_machine_info[0] if len(stored_machine_info) == 3: # Verify the provided signature with the stored public key. public_key = serialization.load_pem_public_key( stored_machine_info[2], default_backend()) public_key.verify( base64.b64decode(authentication_dict["signature"]), bytes(authentication_dict["nonce_digest"], 'utf-8'), padding.PSS(mgf=padding.MGF1(SHA512()), salt_length=padding.PSS.MAX_LENGTH), SHA512()) print("[+] Authenticated machine with alias: {0}.".format( stored_machine_info[0])) return True else: raise LookupError( "[!] Invalid data stored for provided public key.") except ValueError: print( "[!] Value error during authentication, invalid JSON may have been supplied." ) return False except KeyError: print( "[!] Not all required fields present in authentication string, machine is not authorized." ) return False except InvalidSignature: print("[!] Signature mismatch, machine is not authorized.") return False
def _sign_ciphertext_digest(self, ciphertext_digest): if type(ciphertext_digest) is str: ciphertext_digest = bytes(ciphertext_digest, 'utf-8') return base64.b64encode( self.rsa_private_key.sign( ciphertext_digest, padding.PSS(mgf=padding.MGF1(SHA512()), salt_length=padding.PSS.MAX_LENGTH), SHA512())).decode('utf-8')
def rsa_verify(self, msg, user_pub_key): sig_length_bytes = msg[:4] sig_length = int.from_bytes(sig_length_bytes, byteorder='big') signature = msg[4:4 + sig_length] s = msg[4 + sig_length:] verifier = user_pub_key.verifier( signature, PSS(mgf=MGF1(SHA512()), salt_length=PSS.MAX_LENGTH), SHA512()) verifier.update(s) verifier.verify() return s
def exchange(config: Config, context: dict, expected_tlv_state: TlvState, encrypted_data: bytes) -> List[dict]: """pair_setup M5 and M6""" srp = context.get('srp') if expected_tlv_state != TlvState.m5 or not srp: return _error(TlvState.m6, TlvError.unknown, 'Unexpected pair_setup state') hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_ENCRYPT, info=INFO_ENCRYPT, backend=default_backend()) decrypt_key = hkdf.derive(srp.session_key) chacha = ChaCha20Poly1305(decrypt_key) try: data = chacha.decrypt(NONCE_SETUP_M5, encrypted_data, None) except InvalidTag: return _error(TlvState.m6, TlvError.authentication, 'pair_setup M5: invalid auth tag during chacha decryption') try: tlv = tlv_parser.decode(data)[0] except ValueError: return _error(TlvState.m6, TlvError.authentication, 'unable to decode decrypted tlv data') hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_CONTROLLER, info=INFO_CONTROLLER, backend=default_backend()) ios_device_x = hkdf.derive(srp.session_key) ios_device_info = ios_device_x + tlv[TlvCode.identifier].encode() + tlv[TlvCode.public_key] if not _verify_ed25519(key=tlv[TlvCode.public_key], message=ios_device_info, signature=tlv[TlvCode.signature]): return _error(TlvState.m6, TlvError.authentication, 'ios_device_info ed25519 signature verification is failed') config.add_pairing(tlv[TlvCode.identifier], tlv[TlvCode.public_key], ControllerPermission.admin) # save pairing # M6 response generation hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_ACCESSORY, info=INFO_ACCESSORY, backend=default_backend()) accessory_x = hkdf.derive(srp.session_key) signing_key = ed25519.SigningKey(config.accessory_ltsk) public_key = signing_key.get_verifying_key().to_bytes() accessory_info = accessory_x + config.device_id.encode() + public_key accessory_signature = signing_key.sign(accessory_info) sub_tlv = tlv_parser.encode([{ TlvCode.identifier: config.device_id, TlvCode.public_key: public_key, TlvCode.signature: accessory_signature, }]) encrypted_data = chacha.encrypt(NONCE_SETUP_M6, sub_tlv, None) config.pair_setup_mode = False return [{ TlvCode.state: TlvState.m6, TlvCode.encrypted_data: encrypted_data, }]
def _signature_is_valid(self, ciphertext_digest, encoded_signature): if type(encoded_signature) is str: encoded_signature = bytes(encoded_signature, 'utf-8') if type(ciphertext_digest) is str: ciphertext_digest = bytes(ciphertext_digest, 'utf-8') signature = base64.b64decode(encoded_signature) try: self.peer_public_key.verify( signature, ciphertext_digest, padding.PSS(mgf=padding.MGF1(SHA512()), salt_length=padding.PSS.MAX_LENGTH), SHA512()) return True except InvalidSignature: return False
def get_machine_authentication_string(self, authentication_nonce): nonce_digest = authentication_nonce for i in range(0, 10000): nonce_digest = self._compute_hash(nonce_digest) signature = self.private_key.sign( bytes(nonce_digest, 'utf-8'), padding.PSS(mgf=padding.MGF1(SHA512()), salt_length=padding.PSS.MAX_LENGTH), SHA512()) authentication_dict = { "key_digest": self.get_public_key_digest().decode('utf-8'), "nonce_digest": nonce_digest, "signature": base64.b64encode(signature).decode('utf-8') } return json.dumps(authentication_dict)
def verify_start(config: Config, context: dict, ios_device_public_key: bytes) -> List[dict]: """pair_verify M1 and M2""" curve25519 = X25519PrivateKey.generate() accessory_curve25519_public_key: bytes = curve25519.public_key().public_bytes() shared_secret: bytes = curve25519.exchange(X25519PublicKey.from_public_bytes(ios_device_public_key)) accessory_info: bytes = accessory_curve25519_public_key + config.device_id.encode() + ios_device_public_key signing_key = ed25519.SigningKey(config.accessory_ltsk) accessory_signature = signing_key.sign(accessory_info) sub_tlv = tlv_parser.encode([{ TlvCode.identifier: config.device_id, TlvCode.signature: accessory_signature, }]) hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_VERIFY, info=INFO_VERIFY, backend=default_backend()) session_key = hkdf.derive(shared_secret) chacha = ChaCha20Poly1305(session_key) encrypted_data = chacha.encrypt(NONCE_VERIFY_M2, sub_tlv, None) context['session_key'] = session_key context['shared_secret'] = shared_secret context['accessory_curve25519_public_key'] = accessory_curve25519_public_key context['ios_device_curve25519_public_key'] = ios_device_public_key return [{ TlvCode.state: TlvState.m2, TlvCode.public_key: accessory_curve25519_public_key, TlvCode.encrypted_data: encrypted_data, }]
def sign_message(private_key, data): signature = private_key.sign(data, ECDSA(SHA512())) public_key = encode_public_key(private_key.public_key()) header = {"key": b64encode(public_key).decode(), 'sig': b64encode(signature).decode()} return b'S' + format_header(header) + data
def generate_message_signature(message: Union[str, bytes], private_key: Union[RSAPrivateKey, str]) -> str: """Return the BASE64 encoded SHA514 signature of a message. Args: message (str): The message to sign. private_key (str): The private key content used to sign the message. Returns: str: The BASE64 encoded SHA514 signature of a message. """ # Convert the message string to bytes if isinstance(message, str): message = message.encode() # Convert the private key content to a RSAPrivateKey object if isinstance(private_key, str): private_key = load_rsa_private_key(private_key) # Sign the message using the RSAPrivateKey object signature: bytes = private_key.sign(message, PKCS1v15(), SHA512()) # Return the BASE64 encoded SHA512 signature b64_bytes: bytes = base64.b64encode(signature) return b64_bytes.decode('ascii')
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 verify_finish(config: Config, context: dict, encrypted_data: bytes) -> List[dict]: """pair_verify M3 and M4""" session_key = context.get('session_key') accessory_curve25519_public_key = context.get('accessory_curve25519_public_key') ios_device_curve25519_public_key = context.get('ios_device_curve25519_public_key') if not session_key or not accessory_curve25519_public_key or not ios_device_curve25519_public_key: return _error(TlvState.m4, TlvError.authentication, 'verify_finished call before successful verify_start') chacha = ChaCha20Poly1305(session_key) try: data = chacha.decrypt(NONCE_VERIFY_M3, encrypted_data, None) except InvalidTag: return _error(TlvState.m4, TlvError.authentication, 'invalid auth tag during chacha decryption') try: tlv = tlv_parser.decode(data)[0] except ValueError: return _error(TlvState.m4, TlvError.authentication, 'unable to decode decrypted tlv data') ios_device_ltpk = config.get_pairing(tlv[TlvCode.identifier])[1] if not ios_device_ltpk: return _error(TlvState.m4, TlvError.authentication, 'unable to find requested ios device in config file') ios_device_info = ios_device_curve25519_public_key + tlv[TlvCode.identifier].encode() + \ accessory_curve25519_public_key if not _verify_ed25519(ios_device_ltpk, message=ios_device_info, signature=tlv[TlvCode.signature]): return _error(TlvState.m4, TlvError.authentication, 'ios_device_info ed25519 signature verification is failed') context['paired'] = True context['ios_device_pairing_id'] = tlv[TlvCode.identifier] hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_CONTROL, info=INFO_CONTROL_WRITE, backend=default_backend()) context['decrypt_key'] = hkdf.derive(context['shared_secret']) hkdf = HKDF(algorithm=SHA512(), length=32, salt=SALT_CONTROL, info=INFO_CONTROL_READ, backend=default_backend()) context['encrypt_key'] = hkdf.derive(context['shared_secret']) return [{ TlvCode.state: TlvState.m4, }]
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 __init__(self, data_secret: bytes, data_hash: bytes) -> None: digest = Hash(SHA512()) digest.update(data_secret) digest.update(data_hash) secret_hash = digest.finalize() key = secret_hash[:self._key_size] iv = secret_hash[self._key_size:self._key_size + self._iv_size] self._data_hash: Final[bytes] = data_hash self._cipher: Final[Cipher] = Cipher(AES(key), CBC(iv))
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 __call__(self, request): hmac = HMAC(self.secret.encode('utf-8'), SHA512(), default_backend()) hmac.update(request.body.encode('utf-8')) signature = hexlify(hmac.finalize()) request.headers['Key'] = self.key request.headers['Sign'] = signature return request
def get_hash(algorithm): """Return hash algorithm to use for signature""" if algorithm == b'rsa-sha2-512': return SHA512() elif algorithm in (b'rsa-sha2-256', b'rsa2048-sha256'): return SHA256() else: return SHA1()
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 sign(self, data: Union[str, Dict[Any, Any]]) -> str: if isinstance(data, dict): data = json.dumps(data, cls=DecimalEncoder) payload = sub('[\n\t\r ]', '', data).encode('utf-8') else: payload = data.encode('utf-8') signature = self.private_key.sign(payload, padding.PKCS1v15(), SHA512()) return b64encode(signature).decode('utf-8')
def derive_keyhash_v2(domain, password, salt, iterations): kdf = PBKDF2HMAC(algorithm=SHA512(), length=64, salt=salt, iterations=iterations, backend=_CRYPTO_BACKEND) derived_key = kdf.derive((domain + password).encode('utf-8')) hf = HMAC(derived_key, SHA256(), backend=_CRYPTO_BACKEND) hf.update(domain.encode('utf-8')) return hf.finalize()
def test_init(self, one_key_of_many): key_pem, key_serialized = one_key_of_many if isinstance(key_pem, Path): private_key_pem_file = key_pem private_key_pem = None else: private_key_pem_file = None private_key_pem = key_pem login = "******" expiration_time = 20 read_only = True global_key = True label = f"{__project__} {__version__}" authentication_url = "http etc" connection_timeout = 30 token = AccessToken( login=login, private_key=private_key_pem, private_key_file=private_key_pem_file, expiration_time=expiration_time, read_only=read_only, global_key=global_key, label=label, authentication_url=authentication_url, connection_timeout=connection_timeout, ) assert token.login == login assert token.time_to_live == expiration_time assert token.read_only == read_only assert token.global_key == global_key assert token.label == label assert token.authentication_url == authentication_url assert token.connection_timeout == connection_timeout payload = ( "Apparently private keys can't be compared directly when extracted (salts?)." "But they MUST create the same signature when signing the same data!" ) assert token.private_key.sign( str.encode(payload), PKCS1v15(), SHA512() ) == key_serialized.sign(str.encode(payload), PKCS1v15(), SHA512())
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_ec2_sig_alg( alg_id: COSEAlgorithmIdentifier) -> EllipticCurveSignatureAlgorithm: """Turn an "ECDSA" COSE algorithm identifier into a corresponding signature algorithm """ if alg_id == COSEAlgorithmIdentifier.ECDSA_SHA_256: return ECDSA(SHA256()) if alg_id == COSEAlgorithmIdentifier.ECDSA_SHA_512: return ECDSA(SHA512()) raise UnsupportedAlgorithm(f"Unrecognized EC2 signature alg {alg_id}")
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 get_rsa_pss_sig_alg(alg_id: COSEAlgorithmIdentifier) -> HashAlgorithm: """Turn an "RSASSA_PSS" COSE algorithm identifier into a corresponding signature algorithm """ if alg_id == COSEAlgorithmIdentifier.RSASSA_PSS_SHA_256: return SHA256() if alg_id == COSEAlgorithmIdentifier.RSASSA_PSS_SHA_384: return SHA384() if alg_id == COSEAlgorithmIdentifier.RSASSA_PSS_SHA_512: return SHA512() raise UnsupportedAlgorithm(f"Unrecognized RSA PSS signature alg {alg_id}")
def keygen(key_name: Text, passcode: Text, return_key: Optional[bool] = None) -> Union[None, Text]: """Generates key. key_name: The name with which the key needs to be stored in as an environment variable. passcode: The password that you would like to encrypt using salt. return_key: If made True, it will return the key an then save it in an environment variable. Generates key with salt & stores it within an environment variable. Caution: This key or password is the master password that would be used with the other encrypt and decrypt functions. Hence do not forget. """ # You can find the reference code here: # https://nitratine.net/blog/post/encryption-and-decryption-in-python/ from base64 import urlsafe_b64encode from os import urandom from subprocess import PIPE, Popen from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.hashes import SHA512 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC try: # Converting the input string into bytes. password = passcode.encode() # Creating a salt for encryption. # Using SHA512 hashing algorithm, you can use SHA128 or SHA256 # as well. kdf = PBKDF2HMAC(algorithm=SHA512(), length=32, salt=urandom(128), iterations=100000, backend=default_backend()) # Generating encrypted and salted key but changing it to str # format so that it can be stored in an environment variable. # This is a personal preference. key = urlsafe_b64encode(kdf.derive( password)).decode(encoding=_ENCODING) if key_name is None: key_name = _KEY_NAME Popen(f"setx {key_name} {key}", stdout=PIPE, shell=True) if return_key is True: return key except Exception as error: print('An error occured while performing this operation because of' f' {error} in function "{stack()[0][3]}" on line' f' {exc_info()[-1].tb_lineno}.')
def compute_key_digest(key_data_array): # Clean empty lines key_data_array = [x for x in key_data_array if len(x) > 0] # Clean BEGIN and END tags key_data_array.pop(0) key_data_array.pop(-1) # Join the base64 string key_data = ''.join(key_data_array) hash_provider = Hash(SHA512(), default_backend()) hash_provider.update(bytes(key_data, 'utf-8')) digest = binascii.hexlify(hash_provider.finalize()).decode('utf-8') return digest
def get_shared_key(self, key_length=32, shared_secret=None): if not shared_secret and not self.shared_secret: raise ArithmeticError("No shared secret provided.") elif shared_secret and not self.shared_secret: self.shared_secret = shared_secret hash_provider = HMAC( hex(self.shared_secret).encode('utf-8'), SHA512(), default_backend()) for i in range(0, 10000): hash_provider.update(hex(i).encode('utf-8')) shared_digest = base64.b64encode(hash_provider.finalize()) return shared_digest[:key_length]
def _otp(self): """ Current one time password implementation, time-based "TOTP" https://cryptography.io/en/latest/hazmat/primitives/twofactor/ """ if not self._otp_enabled or len(self._otp_secret) < 1: raise ValueError("2FA/OTP is not enabled for this user") key = self._otp_secret.decode('hex') return TOTP(key, self.OTP_LENGTH, SHA512(), self.OTP_STEP, backend=default_backend())
def _generate_signature_header(self, payload: Dict) -> Dict: """Sign the payload and include it in the headers. :param payload: payload (claim) for the request for the access token :type payload: Dict :return: headers which include the signature of the payload :rtype: Dict """ signature = self.private_key.sign(str.encode(payload), PKCS1v15(), SHA512()) return { "Content-Type": "application/json", "Signature": b64encode(signature).decode("ascii"), }