# Copyright (c) 2016 The Johns Hopkins University/Applied Physics Laboratory
# All Rights Reserved.
#
# 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 abc import ABCMeta
from abc import abstractmethod

import six


@six.add_metaclass(ABCMeta)
class CryptographicEngine(object):
    """
    The abstract base class of the cryptographic engine hierarchy.

    A cryptographic engine is responsible for generating all cryptographic
    objects and conducting all cryptographic operations for a KMIP server
    instance.
    """

    @abstractmethod
    def create_symmetric_key(self, algorithm, length):
        """
        Create a symmetric key.

        Args:
            algorithm(CryptographicAlgorithm): An enumeration specifying the
                algorithm for which the created key will be compliant.
            length(int): The length of the key to be created. This value must
                be compliant with the constraints of the provided algorithm.

        Returns:
            dict: A dictionary containing the key data, with the following
                key/value fields:
                * value - the bytes of the key
                * format - a KeyFormatType enumeration for the bytes format
        """

    @abstractmethod
    def create_asymmetric_key_pair(self, algorithm, length):
        """
        Create an asymmetric key pair.

        Args:
            algorithm(CryptographicAlgorithm): An enumeration specifying the
                algorithm for which the created keys will be compliant.
            length(int): The length of the keys to be created. This value must
                be compliant with the constraints of the provided algorithm.

        Returns:
            dict: A dictionary containing the public key data, with the
                following key/value fields:
                * value - the bytes of the key
                * format - a KeyFormatType enumeration for the bytes format
            dict: A dictionary containing the private key data, identical in
                structure to the public key dictionary.
        """

    @abstractmethod
    def mac(self, algorithm, key, data):
        """
        Generate message authentication code.

        Args:
            algorithm(CryptographicAlgorithm): An enumeration specifying the
                algorithm for which the MAC operation will use.
            key(bytes): secret key used in the MAC operation
            data(bytes): The data to be MACed.

        Returns:
            bytes: The MAC data
        """

    @abstractmethod
    def encrypt(self,
                encryption_algorithm,
                encryption_key,
                plain_text,
                cipher_mode=None,
                padding_method=None,
                iv_nonce=None):
        """
        Encrypt data using symmetric encryption.

        Args:
            encryption_algorithm (CryptographicAlgorithm): An enumeration
                specifying the symmetric encryption algorithm to use for
                encryption.
            encryption_key (bytes): The bytes of the symmetric key to use for
                encryption.
            plain_text (bytes): The bytes to be encrypted.
            cipher_mode (BlockCipherMode): An enumeration specifying the
                block cipher mode to use with the encryption algorithm.
                Required in the general case. Optional if the encryption
                algorithm is RC4 (aka ARC4). If optional, defaults to None.
            padding_method (PaddingMethod): An enumeration specifying the
                padding method to use on the data before encryption. Required
                if the cipher mode is for block ciphers (e.g., CBC, ECB).
                Optional otherwise, defaults to None.
            iv_nonce (bytes): The IV/nonce value to use to initialize the mode
                of the encryption algorithm. Optional, defaults to None. If
                required and not provided, it will be autogenerated and
                returned with the cipher text.

        Returns:
            dict: A dictionary containing the encrypted data, with at least
                the following key/value fields:
                * cipher_text - the bytes of the encrypted data
                * iv_nonce - the bytes of the IV/counter/nonce used if it
                    was needed by the encryption scheme and if it was
                    automatically generated for the encryption
        """

    @abstractmethod
    def decrypt(self,
                decryption_algorithm,
                decryption_key,
                cipher_text,
                cipher_mode=None,
                padding_method=None,
                iv_nonce=None):
        """
        Decrypt data using symmetric decryption.

        Args:
            decryption_algorithm (CryptographicAlgorithm): An enumeration
                specifying the symmetric decryption algorithm to use for
                decryption.
            decryption_key (bytes): The bytes of the symmetric key to use for
                decryption.
            cipher_text (bytes): The bytes to be decrypted.
            cipher_mode (BlockCipherMode): An enumeration specifying the
                block cipher mode to use with the decryption algorithm.
                Required in the general case. Optional if the decryption
                algorithm is RC4 (aka ARC4). If optional, defaults to None.
            padding_method (PaddingMethod): An enumeration specifying the
                padding method to use on the data after decryption. Required
                if the cipher mode is for block ciphers (e.g., CBC, ECB).
                Optional otherwise, defaults to None.
            iv_nonce (bytes): The IV/nonce value to use to initialize the mode
                of the decryption algorithm. Optional, defaults to None.

        Returns:
            bytes: the bytes of the decrypted data
        """

    @abstractmethod
    def sign(self,
             digital_signature_algorithm,
             cryptographic_algorithm,
             hash_algorithm,
             signing_key,
             data):
        """
        Generate a signature for the provided data.

        Args:
            digital_signature_algorithm (DigitalSignatureAlgorithm): An
                enumeration specifying the asymmetric cryptographic algorithm
                and hashing algorithm to use for the signature operation. Can
                be None if cryptographic_algorithm and hash_algorithm are set.
            cryptographic_algorithm (CryptographicAlgorithm): An enumeration
                specifying the asymmetric cryptographic algorithm to use for
                the signature operation. Can be None if
                digital_signature_algorithm is set.
            hash_algorithm (HashingAlgorithm): An enumeration specifying the
                hash algorithm to use for the signature operation. Can be None
                if digital_signature_algorithm is set.
            signing_key (bytes): The bytes of the private key to use for the
                signature operation.
            data (bytes): The data to be signed.

        Returns:
            bytes: the bytes of the signature data
        """