# Copyright (c) 2015 Nordic Semiconductor. All Rights Reserved.
#
# The information contained herein is property of Nordic Semiconductor ASA.
# Terms and conditions of usage are described in detail in NORDIC
# SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
#
# Licensees are granted free, non-transferable use of the information. NO
# WARRANTY of ANY KIND is provided. This heading must NOT be removed from
# the file.

import hashlib
import binascii

try:
    from ecdsa import SigningKey
    from ecdsa.curves import NIST256p
    from ecdsa.keys import sigencode_string
except Exception:
    print("Failed to import ecdsa, cannot do signing")

from nordicsemi.exceptions import InvalidArgumentException, IllegalStateException


class Signing(object):
    """
    Class for singing of hex-files
    """
    def gen_key(self, filename):
        """
        Generate a new Signing key using NIST P-256 curve
        """
        self.sk = SigningKey.generate(curve=NIST256p)

        with open(filename, "w") as sk_file:
            sk_file.write(self.sk.to_pem())

    def load_key(self, filename):
        """
        Load signing key (from pem file)
        """
        with open(filename, "r") as sk_file:
            sk_pem = sk_file.read()

        self.sk = SigningKey.from_pem(sk_pem)

        sk_hex = "".join(c.encode('hex') for c in self.sk.to_string())

    def sign(self, init_packet_data):
        """
        Create signature for init package using P-256 curve and SHA-256 as hashing algorithm
        Returns R and S keys combined in a 64 byte array
        """
        # Add assertion of init_packet
        if self.sk is None:
            raise IllegalStateException("Can't save key. No key created/loaded")

        # Sign the init-packet
        signature = self.sk.sign(init_packet_data, hashfunc=hashlib.sha256, sigencode=sigencode_string)
        return signature

    def verify(self, init_packet, signature):
        """
        Verify init packet
        """
        # Add assertion of init_packet
        if self.sk is None:
            raise IllegalStateException("Can't save key. No key created/loaded")

        vk = self.sk.get_verifying_key()

        # Verify init packet
        try:
            vk.verify(signature, init_packet, hashfunc=hashlib.sha256)
        except:
            return False

        return True

    def get_vk(self, output_type):
        """
        Get verification key (as hex, code or pem)
        """
        if self.sk is None:
            raise IllegalStateException("Can't get key. No key created/loaded")

        if output_type is None:
            raise InvalidArgumentException("Invalid output type for signature.")
        elif output_type == 'hex':
            return self.get_vk_hex()
        elif output_type == 'code':
            return self.get_vk_code()
        elif output_type == 'pem':
            return self.get_vk_pem()
        else:
            raise InvalidArgumentException("Invalid argument. Can't get key")

    def get_vk_hex(self):
        """
        Get the verification key as hex
        """
        if self.sk is None:
            raise IllegalStateException("Can't get key. No key created/loaded")

        vk = self.sk.get_verifying_key()
        vk_hexlify = binascii.hexlify(vk.to_string())

        vk_hex = "Verification key Qx: {0}\n".format(vk_hexlify[0:64])
        vk_hex += "Verification key Qy: {0}".format(vk_hexlify[64:128])

        return vk_hex

    def get_vk_code(self):
        """
        Get the verification key as code
        """
        if self.sk is None:
            raise IllegalStateException("Can't get key. No key created/loaded")

        vk = self.sk.get_verifying_key()
        vk_hex = binascii.hexlify(vk.to_string())

        vk_x_separated = ""
        vk_x_str = vk_hex[0:64]
        for i in range(0, len(vk_x_str), 2):
            vk_x_separated += "0x" + vk_x_str[i:i+2] + ", "
        vk_x_separated = vk_x_separated[:-2]

        vk_y_separated = ""
        vk_y_str = vk_hex[64:128]
        for i in range(0, len(vk_y_str), 2):
            vk_y_separated += "0x" + vk_y_str[i:i+2] + ", "
        vk_y_separated = vk_y_separated[:-2]

        vk_code = "static uint8_t Qx[] = {{ {0} }};\n".format(vk_x_separated)
        vk_code += "static uint8_t Qy[] = {{ {0} }};".format(vk_y_separated)

        return vk_code

    def get_vk_pem(self):
        """
        Get the verification key as PEM
        """
        if self.sk is None:
            raise IllegalStateException("Can't get key. No key created/loaded")

        vk = self.sk.get_verifying_key()
        vk_pem = vk.to_pem()

        return vk_pem
