# Copyright 2013 Donald Stufft and individual contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import, division, print_function

import hashlib
from binascii import hexlify, unhexlify

import pytest

from nacl import bindings as c
from nacl.exceptions import CryptoError


def tohex(b):
    return hexlify(b).decode("ascii")


def test_hash():
    msg = b"message"
    h1 = c.crypto_hash(msg)
    assert len(h1) == c.crypto_hash_BYTES
    assert tohex(h1) == ("f8daf57a3347cc4d6b9d575b31fe6077"
                         "e2cb487f60a96233c08cb479dbf31538"
                         "cc915ec6d48bdbaa96ddc1a16db4f4f9"
                         "6f37276cfcb3510b8246241770d5952c")
    assert tohex(h1) == hashlib.sha512(msg).hexdigest()

    h2 = c.crypto_hash_sha512(msg)
    assert len(h2) == c.crypto_hash_sha512_BYTES
    assert tohex(h2) == tohex(h1)

    h3 = c.crypto_hash_sha256(msg)
    assert len(h3) == c.crypto_hash_sha256_BYTES
    assert tohex(h3) == ("ab530a13e45914982b79f9b7e3fba994"
                         "cfd1f3fb22f71cea1afbf02b460c6d1d")
    assert tohex(h3) == hashlib.sha256(msg).hexdigest()


def test_secretbox():
    key = b"\x00" * c.crypto_secretbox_KEYBYTES
    msg = b"message"
    nonce = b"\x01" * c.crypto_secretbox_NONCEBYTES
    ct = c.crypto_secretbox(msg, nonce, key)
    assert len(ct) == len(msg) + c.crypto_secretbox_BOXZEROBYTES
    assert tohex(ct) == "3ae84dfb89728737bd6e2c8cacbaf8af3d34cc1666533a"
    msg2 = c.crypto_secretbox_open(ct, nonce, key)
    assert msg2 == msg

    with pytest.raises(CryptoError):
        c.crypto_secretbox_open(
            msg + b"!",
            nonce,
            key,
        )


def test_aes256gcm():
    key = b"\x00" * c.crypto_aead_aes256gcm_KEYBYTES
    msg = b"message"
    nonce = b"\x01" * c.crypto_aead_aes256gcm_NPUBBYTES
    if c.crypto_aead_aes256gcm_is_available():
        ciphertext = c.crypto_aead_aes256gcm_encrypt(msg, nonce, key, None, 0)
        cipher = ciphertext[0:len(msg)]
        tag = ciphertext[len(msg):]
        msg2 = c.crypto_aead_aes256gcm_decrypt(
            cipher, tag, nonce,
            key, None, 0)
        assert msg2[:] == msg

        with pytest.raises(CryptoError):
            c.crypto_aead_aes256gcm_decrypt(
                msg + b"!",
                tag,
                nonce,
                key,
                None,
                0
            )


def test_secretbox_wrong_length():
    with pytest.raises(ValueError):
        c.crypto_secretbox(b"", b"", b"")
    with pytest.raises(ValueError):
        c.crypto_secretbox(b"", b"", b"\x00" * c.crypto_secretbox_KEYBYTES)
    with pytest.raises(ValueError):
        c.crypto_secretbox_open(b"", b"", b"")
    with pytest.raises(ValueError):
        c.crypto_secretbox_open(
            b"", b"", b"\x00" * c.crypto_secretbox_KEYBYTES)


def test_box():
    A_pubkey, A_secretkey = c.crypto_box_keypair()
    assert len(A_secretkey) == c.crypto_box_SECRETKEYBYTES
    assert len(A_pubkey) == c.crypto_box_PUBLICKEYBYTES
    B_pubkey, B_secretkey = c.crypto_box_keypair()

    k1 = c.crypto_box_beforenm(B_pubkey, A_secretkey)
    assert len(k1) == c.crypto_box_BEFORENMBYTES
    k2 = c.crypto_box_beforenm(A_pubkey, B_secretkey)
    assert tohex(k1) == tohex(k2)

    message = b"message"
    nonce = b"\x01" * c.crypto_box_NONCEBYTES
    ct1 = c.crypto_box_afternm(message, nonce, k1)
    assert len(ct1) == len(message) + c.crypto_box_BOXZEROBYTES

    ct2 = c.crypto_box(message, nonce, B_pubkey, A_secretkey)
    assert tohex(ct2) == tohex(ct1)

    m1 = c.crypto_box_open(ct1, nonce, A_pubkey, B_secretkey)
    assert m1 == message

    m2 = c.crypto_box_open_afternm(ct1, nonce, k1)
    assert m2 == message

    with pytest.raises(CryptoError):
        c.crypto_box_open(
            message + b"!", nonce, A_pubkey, A_secretkey)


def test_box_wrong_lengths():
    A_pubkey, A_secretkey = c.crypto_box_keypair()
    with pytest.raises(ValueError):
        c.crypto_box(b"abc", "\x00", A_pubkey, A_secretkey)
    with pytest.raises(ValueError):
        c.crypto_box(