"""
Script to do 'FIDO Dynamic Linking Verification' based on values from transaction-approval logs.
Copy and Paste your input json to replace the example INPUT_JSON below.
"""
import hashlib
from fido2 import utils
from fido2 import cbor
from fido2.cose import CoseKey
from cryptography.exceptions import InvalidSignature

INPUT_JSON={
    "S": "MEUCIA-h1SXusQwdhlrya9wluvZy-yIaFEviN9BmMNlrR2COAiEAkMmfoOhzTMS_Fjj6KKCGgNYkwvXbTa9Y9u4R-U-R2VU",
    "T": "{\"type\":\"payment\",\"amount\":\"300000000\",\"currency\":\"CHF\"}",
    "CAD": "SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAg",
    "CCD": "{\"type\":\"webauthn.get\",\"challenge\":\"mmdHgeMzJBIEkiQrZbg_ntEPUYvQilNeCG4qQyjEuhrMKiGsRQ8UhDOVtLVLrqcipijZXWDXtcbkE_rbek3hzA\",\"origin\":\"https://localhost\"}",
    "N": "PsJaxWAfuBcDYB4ZA79viZ17AuWMLP0iO2yMkW9-bgE",
    "P": "pQECAyYgASFYIDiiceH54t9pRqOX03nq96wJpQty5GRsv3rhwIHSrDjsIlggQGT8iVfqTmzYXbicDBMmPMZyFLqPyKP9Ul65P-h4lyU"
}

def message_linked_to_challenge(
        message: str,
        nonce: str,
        client_data_json: str) -> bool:
    """
    Check if message is linked to challenge string:
    CCD contains C := SHA512(T | N)
    """
    concat = message + nonce
    hash_bytes = hashlib.sha512(concat.encode('utf-8')).digest()
    computed_challenge = utils.websafe_encode(hash_bytes)

    return computed_challenge in client_data_json

def verify_signature_for_challenge(
        public_key: str,
        challenge_message_bytes: bytes,
        signed_challenge_message: str) -> bool:
    """
    Verify the signature for the challenge message (attToBeSigned).
    I.e. VERIFY(P, CT, S), where CT := CAD + SHA256(CCD)
    """
    public_key_bytes = utils.websafe_decode(public_key)
    signed_challenge_message_bytes = utils.websafe_decode(signed_challenge_message)

    key = CoseKey.parse(cbor.decode(public_key_bytes))

    try:
        key.verify(challenge_message_bytes, signed_challenge_message_bytes)
        return True
    except InvalidSignature:
        return False

def challenge_message(authenticator_data: str, client_data_json: str) -> bytes:
    """
    According to https://w3c.github.io/webauthn/#verifying-assertion, the challenge message
    is the concatenation of authenticatorData and hash_256(clientDataJSON)
    """
    authenticator_data_bytes = utils.websafe_decode(authenticator_data)
    client_data_hash_bytes = hashlib.sha256(client_data_json.encode('utf-8')).digest()
    return authenticator_data_bytes + client_data_hash_bytes


# These two tests link the message M to the challenge and then the challenge to the signature:

####################################
# Test 1: Link Message to Challenge
####################################
print("Test 1: Link of  Message to Challenge:", end=" ")
if message_linked_to_challenge(message=INPUT_JSON["T"], nonce=INPUT_JSON["N"], client_data_json=INPUT_JSON["CCD"]) is True:
    print("\033[32mSUCCESS\033[0m")
else:
    print("\033[31mFAILED\033[0m")

#########################################
# Test 2: Verify Signature for Challenge
#########################################
print("Test 2: Verify Signature for Challenge:", end=" ")
if verify_signature_for_challenge(
    public_key=INPUT_JSON["P"],
    challenge_message_bytes=challenge_message(INPUT_JSON["CAD"], INPUT_JSON["CCD"]),
    signed_challenge_message=INPUT_JSON["S"]) is True:
    print("\033[32mSUCCESS\033[0m")
else:
    print("\033[31mFAILED\033[0m")
