Пример #1
0
    def RunBlockCipher2(self, message_byte_array, n_runs, key):
        '''Implemented as using Blowfish symmetric block cipher.

           Runs the second block cipher to assess performance.

           Returns nothing.'''
        for i in range(n_runs):
            ###########
            # Run cryptography library implementation
            self.DisplayCipherStart("cryptography", "Blowfish", i)

            startTime = time.time()

            blockLength = 8
            IV = os.urandom(blockLength)

            blowfish = Cipher(algorithms.Blowfish(key), modes.CBC(IV), default_backend())
            blowfishEncryptor = blowfish.encryptor()
            blowfishDecryptor = blowfish.decryptor()

            paddedMessage = self.AddPadding(message_byte_array, blockLength)

            splitMessage = self.SplitMessage(paddedMessage, 500 * blockLength)

            unpaddedMessage = bytearray()
            for segment in splitMessage:
                unpaddedSegment = self.RemovePadding(blowfishDecryptor.update(blowfishEncryptor.update(segment)), blockLength)
                unpaddedMessage.extend(unpaddedSegment)
            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("cryptographyoutputblowfish." + message_filename.split('.')[1])
            self.cryptographyTimeResults[self.blockCipher2].append(endTime - startTime)
            self.DisplayCipherEnd("cryptography", "Blowfish", i)

            ##############
            # Run PyCrypto library implementation
            self.DisplayCipherStart("PyCrypto", "Blowfish", i)
            startTime = time.time()

            blockLength = 8
            IV = os.urandom(blockLength)
            blowfish = Crypto.Cipher.Blowfish.new(key, mode=Crypto.Cipher.blockalgo.MODE_CBC, IV=IV)
            self.AddPadding(message_byte_array, blockLength)
            extendedMessage = bytearray(IV)
            extendedMessage.extend(paddedMessage)

            encryptedMessage = blowfish.encrypt(bytes(extendedMessage))
            decryptedMessage = blowfish.decrypt(encryptedMessage)
            unpaddedMessage = self.RemovePadding(decryptedMessage[blockLength:], blockLength)

            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("PyCryptooutputblowfish." + message_filename.split('.')[1])
            self.PyCryptoTimeResults[self.blockCipher2].append(endTime - startTime)
            self.DisplayCipherEnd("PyCrypto", "Blowfish", i)            
Пример #2
0
    def RunStreamCipher2(self, message_byte_array, n_runs, key):
        '''Implemented as using AES (Advanced Encryption Standard) symmetric
           cipher in CTR (Counter) mode which allows it to run as a stream
           cipher. The key is 192 bits (24 bytes).

           Runs the second stream cipher to assess performance.

           Returns nothing.'''

        for i in range(n_runs):
            ########
            # Run cryptography library implementation
            self.DisplayCipherStart("cryptography", "AES (CTR)", i)
            startTime = time.time()

            blockLength = 16
            IV = os.urandom(blockLength)

            aes = Cipher(algorithms.AES(key), modes.CTR(IV), default_backend())

            paddedMessage = self.AddPadding(message_byte_array, blockLength)
            splitMessage = self.SplitMessage(paddedMessage, 500 * blockLength)
            unpaddedMessage = self.RunCryptographyAndUnpadSplitMessage(splitMessage, aes, blockLength)

            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("cryptographyoutputaes." + message_filename.split('.')[1])
            self.cryptographyTimeResults[self.streamCipher2].append(endTime - startTime)
            self.DisplayCipherEnd("cryptography", "AES (CTR)", i)

            ##########
            # Run PyCrypto library implementation
            self.DisplayCipherStart("PyCrypto", "AES (CTR)", i)
            startTime = time.time()
            blockLength = 16
            IV = os.urandom(blockLength)

            # AES in CTR mode for the PyCrypto library requires a counter instead
            # of an initialization vector
            aes = Crypto.Cipher.AES.new(key, mode=Crypto.Cipher.blockalgo.MODE_CTR, counter=Crypto.Util.Counter.new(128))

            paddedMessage = self.AddPadding(message_byte_array, blockLength)
            encryptedMessage = aes.encrypt(bytes(paddedMessage))
            decryptedMessage = aes.decrypt(encryptedMessage)
            unpaddedMessage = self.RemovePadding(decryptedMessage[blockLength:], blockLength)

            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("PyCryptooutputaes." + message_filename.split('.')[1])
            self.PyCryptoTimeResults[self.streamCipher2].append(endTime - startTime)
            self.DisplayCipherEnd("PyCrypto", "AES (CTR)", i)
Пример #3
0
    def RunStreamCipher1(self, message_byte_array, n_runs, key):
        '''Implemented as using ARC4 symmetric block cipher using 192 bits (24
           bytes).

           Runs the first stream cipher to assess performance.

           Returns nothing.'''

        for i in range(n_runs):
            ########
            # Run cryptography library implementation
            self.DisplayCipherStart("cryptography", "ARC4", i)
            startTime = time.time()

            arc4 = Cipher(algorithms.ARC4(key), None, default_backend())
            arc4Encryptor = arc4.encryptor()
            arc4Decryptor = arc4.decryptor()

            splitMessage = self.SplitMessage(message_byte_array, 8000)
            unpaddedMessage = bytearray()
            for segment in splitMessage:
                unpaddedSegment = arc4Decryptor.update(arc4Encryptor.update(segment))
                unpaddedMessage.extend(unpaddedSegment)

            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("cryptographyoutputarc4." + message_filename.split('.')[1])
            self.cryptographyTimeResults[self.streamCipher1].append(endTime - startTime)
            self.DisplayCipherEnd("cryptography", "ARC4", i)
            
            ###########
            # Run PyCrypto library implementation
            self.DisplayCipherStart("PyCrypto", "ARC4", i)
            startTime = time.time()
            arc4 = Crypto.Cipher.ARC4.new(key)
            encryptedMessage = arc4.encrypt(message_byte_array)
            decryptedMessage = arc4.decrypt(encryptedMessage)

            endTime = time.time()
            
            # If output of the algorithm is desired, uncomment the line below
            #BinaryFileHandler.WriteByteDataFile("PyCryptooutputarc4." + message_filename.split('.')[1])
            self.PyCryptoTimeResults[self.streamCipher1].append(endTime - startTime)
            self.DisplayCipherEnd("PyCrypto", "ARC4", i)
Пример #4
0
def encode_with_ssh_key(key_path: str, plain_string: str) -> str:
    """ encrypts a plain string using some data from key file

    :param key_path: path to key file
    :param plain_string: text that has to be encrypted
    :return: encrypted text
    """
    key, iv = get_key_and_iv(key_path)
    try:
        from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
        from cryptography.hazmat.backends import default_backend
        backend = default_backend()
        cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=backend)
        encryptor = cipher.encryptor()
        encrypted_bytes = encryptor.update(
            plain_string.encode()) + encryptor.finalize()
    except ModuleNotFoundError:
        import Crypto.PublicKey.RSA  # used for generating ssh key
        import Crypto.Cipher.AES

        cipher = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CFB, iv)
        encrypted_bytes = cipher.encrypt(plain_string.encode())

    return base64.b64encode(encrypted_bytes).decode()
Пример #5
0
class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
    """
    The hasattr(self, "pc_cls") tests correspond to the legacy API of the
    crypto library. With cryptography v2.0, both CCM and GCM should follow
    the else case.

    Note that the "fixed_iv" in TLS RFCs is called "salt" in the AEAD RFC 5116.
    """
    type = "aead"
    fixed_iv_len = 4
    nonce_explicit_len = 8

    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
        """
        'key' and 'fixed_iv' are to be provided as strings, whereas the internal
        'nonce_explicit' is an integer (it is simpler for incrementation).
        /!\ The whole 'nonce' may be called IV in certain RFCs.
        """
        self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True}
        if key is None:
            self.ready["key"] = False
            key = b"\0" * self.key_len
        if fixed_iv is None:
            self.ready["fixed_iv"] = False
            fixed_iv = b"\0" * self.fixed_iv_len
        if nonce_explicit is None:
            self.ready["nonce_explicit"] = False
            nonce_explicit = 0

        if isinstance(nonce_explicit, str):
            nonce_explicit = pkcs_os2ip(nonce_explicit)

        # we use super() in order to avoid any deadlock with __setattr__
        super(_AEADCipher, self).__setattr__("key", key)
        super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv)
        super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit)

        if hasattr(self, "pc_cls"):
            self._cipher = Cipher(self.pc_cls(key),
                                  self.pc_cls_mode(self._get_nonce()),
                                  backend=default_backend())
        else:
            self._cipher = self.cipher_cls(key)

    def __setattr__(self, name, val):
        if name == "key":
            if self._cipher is not None:
                if hasattr(self, "pc_cls"):
                    self._cipher.algorithm.key = val
                else:
                    self._cipher._key = val
            self.ready["key"] = True
        elif name == "fixed_iv":
            self.ready["fixed_iv"] = True
        elif name == "nonce_explicit":
            if isinstance(val, str):
                val = pkcs_os2ip(val)
            self.ready["nonce_explicit"] = True
        super(_AEADCipher, self).__setattr__(name, val)

    def _get_nonce(self):
        return (self.fixed_iv +
                pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))

    def _update_nonce_explicit(self):
        """
        Increment the explicit nonce while avoiding any overflow.
        """
        ne = self.nonce_explicit + 1
        self.nonce_explicit = ne % 2**(self.nonce_explicit_len * 8)

    def auth_encrypt(self, P, A, seq_num=None):
        """
        Encrypt the data then prepend the explicit part of the nonce. The
        authentication tag is directly appended with the most recent crypto
        API. Additional data may be authenticated without encryption (as A).

        The 'seq_num' should never be used here, it is only a safeguard needed
        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
        actually is a _AEADCipher_TLS13 (even though others are not).
        """
        if False in six.itervalues(self.ready):
            raise CipherError(P, A)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce()
            self._cipher.mode._tag = None
            encryptor = self._cipher.encryptor()
            encryptor.authenticate_additional_data(A)
            res = encryptor.update(P) + encryptor.finalize()
            res += encryptor.tag
        else:
            if isinstance(self._cipher, AESCCM):
                res = self._cipher.encrypt(self._get_nonce(),
                                           P,
                                           A,
                                           tag_length=self.tag_len)
            else:
                res = self._cipher.encrypt(self._get_nonce(), P, A)

        nonce_explicit = pkcs_i2osp(self.nonce_explicit,
                                    self.nonce_explicit_len)
        self._update_nonce_explicit()
        return nonce_explicit + res

    def auth_decrypt(self, A, C, seq_num=None, add_length=True):
        """
        Decrypt the data and authenticate the associated data (i.e. A).
        If the verification fails, an AEADTagError is raised. It is the user's
        responsibility to catch it if deemed useful. If we lack the key, we
        raise a CipherError which contains the encrypted input.

        Note that we add the TLSCiphertext length to A although we're supposed
        to add the TLSCompressed length. Fortunately, they are the same,
        but the specifications actually messed up here. :'(

        The 'add_length' switch should always be True for TLS, but we provide
        it anyway (mostly for test cases, hum).

        The 'seq_num' should never be used here, it is only a safeguard needed
        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
        actually is a _AEADCipher_TLS13 (even though others are not).
        """
        nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len],
                                      C[self.nonce_explicit_len:-self.tag_len],
                                      C[-self.tag_len:])

        if False in six.itervalues(self.ready):
            raise CipherError(nonce_explicit_str, C, mac)

        self.nonce_explicit = pkcs_os2ip(nonce_explicit_str)
        if add_length:
            A += struct.pack("!H", len(C))

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce()
            self._cipher.mode._tag = mac
            decryptor = self._cipher.decryptor()
            decryptor.authenticate_additional_data(A)
            P = decryptor.update(C)
            try:
                decryptor.finalize()
            except InvalidTag:
                raise AEADTagError(nonce_explicit_str, P, mac)
        else:
            try:
                if isinstance(self._cipher, AESCCM):
                    P = self._cipher.decrypt(self._get_nonce(),
                                             C + mac,
                                             A,
                                             tag_length=self.tag_len)
                else:
                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A)
            except InvalidTag:
                raise AEADTagError(nonce_explicit_str,
                                   "<unauthenticated data>", mac)
        return nonce_explicit_str, P, mac

    def snapshot(self):
        c = self.__class__(self.key, self.fixed_iv, self.nonce_explicit)
        c.ready = self.ready.copy()
        return c
Пример #6
0
class _AEADCipher_TLS13(six.with_metaclass(_AEADCipherMetaclass, object)):
    """
    The hasattr(self, "pc_cls") enable support for the legacy implementation
    of GCM in the cryptography library. They should not be used, and might
    eventually be removed, with cryptography v2.0. XXX
    """
    type = "aead"

    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
        """
        'key' and 'fixed_iv' are to be provided as strings. This IV never
        changes: it is either the client_write_IV or server_write_IV.

        Note that 'nonce_explicit' is never used. It is only a safeguard for a
        call in session.py to the TLS 1.2/ChaCha20Poly1305 case (see RFC 7905).
        """
        self.ready = {"key": True, "fixed_iv": True}
        if key is None:
            self.ready["key"] = False
            key = b"\0" * self.key_len
        if fixed_iv is None:
            self.ready["fixed_iv"] = False
            fixed_iv = b"\0" * self.fixed_iv_len

        # we use super() in order to avoid any deadlock with __setattr__
        super(_AEADCipher_TLS13, self).__setattr__("key", key)
        super(_AEADCipher_TLS13, self).__setattr__("fixed_iv", fixed_iv)

        if hasattr(self, "pc_cls"):
            self._cipher = Cipher(self.pc_cls(key),
                                  self.pc_cls_mode(fixed_iv),
                                  backend=default_backend())
        else:
            self._cipher = self.cipher_cls(key)

    def __setattr__(self, name, val):
        if name == "key":
            if self._cipher is not None:
                if hasattr(self, "pc_cls"):
                    self._cipher.algorithm.key = val
                else:
                    self._cipher._key = val
            self.ready["key"] = True
        elif name == "fixed_iv":
            self.ready["fixed_iv"] = True
        super(_AEADCipher_TLS13, self).__setattr__(name, val)

    def _get_nonce(self, seq_num):
        padlen = self.fixed_iv_len - len(seq_num)
        padded_seq_num = b"\x00" * padlen + seq_num
        return strxor(padded_seq_num, self.fixed_iv)

    def auth_encrypt(self, P, A, seq_num):
        """
        Encrypt the data, and append the computed authentication code.
        TLS 1.3 does not use additional data, but we leave this option to the
        user nonetheless.

        Note that the cipher's authentication tag must be None when encrypting.
        """
        if False in six.itervalues(self.ready):
            raise CipherError(P, A)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._tag = None
            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
            encryptor = self._cipher.encryptor()
            encryptor.authenticate_additional_data(A)
            res = encryptor.update(P) + encryptor.finalize()
            res += encryptor.tag
        else:
            if (conf.crypto_valid_advanced
                    and isinstance(self._cipher, AESCCM)):
                res = self._cipher.encrypt(self._get_nonce(seq_num),
                                           P,
                                           A,
                                           tag_length=self.tag_len)
            else:
                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A)
        return res

    def auth_decrypt(self, A, C, seq_num):
        """
        Decrypt the data and verify the authentication code (in this order).
        Note that TLS 1.3 is not supposed to use any additional data A.
        If the verification fails, an AEADTagError is raised. It is the user's
        responsibility to catch it if deemed useful. If we lack the key, we
        raise a CipherError which contains the encrypted input.
        """
        C, mac = C[:-self.tag_len], C[-self.tag_len:]
        if False in six.itervalues(self.ready):
            raise CipherError(C, mac)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
            self._cipher.mode._tag = mac
            decryptor = self._cipher.decryptor()
            decryptor.authenticate_additional_data(A)
            P = decryptor.update(C)
            try:
                decryptor.finalize()
            except InvalidTag:
                raise AEADTagError(P, mac)
        else:
            try:
                if (conf.crypto_valid_advanced
                        and isinstance(self._cipher, AESCCM)):
                    P = self._cipher.decrypt(self._get_nonce(seq_num),
                                             C + mac,
                                             A,
                                             tag_length=self.tag_len)
                else:
                    if (conf.crypto_valid_advanced
                            and isinstance(self, Cipher_CHACHA20_POLY1305)):
                        A += struct.pack("!H", len(C))
                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac,
                                             A)
            except InvalidTag:
                raise AEADTagError("<unauthenticated data>", mac)
        return P, mac

    def snapshot(self):
        c = self.__class__(self.key, self.fixed_iv)
        c.ready = self.ready.copy()
        return c
Пример #7
0
# ********** BEGIN 'PYCRYPTO' LIBRARY ******************************************
# ********** ######################## ******************************************
    
    lib_run = ['PyCrypto']
    
    # BEGIN BLOCK CIPHER OPERATIONS
    set_run = ['block']
    setup = """\
from __main__ import cipher, message, enctext
"""
    
    # Create cipher, create cipher text for decryption, time operations, update
    # result with each new operation
    algo_run = ['DES3']
    cipher = DES3.new(key, DES3.MODE_CBC, iv8)
    enctext = cipher.encrypt(message)
    enc_run = ['encrypt']
    enc_trial = Timer("cipher.encrypt(message)", setup)
    enc_run.append(enc_trial.repeat(RUN_COUNT, 1))
    algo_run.append(enc_run)
    dec_run = ['decrypt']
    dec_trial = Timer("cipher.decrypt(enctext)", setup)
    dec_run.append(dec_trial.repeat(RUN_COUNT, 1))
    algo_run.append(dec_run)
    set_run.append(algo_run)
    
    # Create cipher, create cipher text for decryption, time operations, update
    # result with each new operation
    algo_run = ['AES']
    cipher = AES.new(key, AES.MODE_CBC, iv16)
    enctext = cipher.encrypt(message)
Пример #8
0
class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
    """
    The hasattr(self, "pc_cls") tests correspond to the legacy API of the
    crypto library. With cryptography v2.0, both CCM and GCM should follow
    the else case.

    Note that the "fixed_iv" in TLS RFCs is called "salt" in the AEAD RFC 5116.
    """
    type = "aead"
    fixed_iv_len = 4
    nonce_explicit_len = 8

    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
        """
        'key' and 'fixed_iv' are to be provided as strings, whereas the internal  # noqa: E501
        'nonce_explicit' is an integer (it is simpler for incrementation).
        /!\ The whole 'nonce' may be called IV in certain RFCs.
        """
        self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True}
        if key is None:
            self.ready["key"] = False
            key = b"\0" * self.key_len
        if fixed_iv is None:
            self.ready["fixed_iv"] = False
            fixed_iv = b"\0" * self.fixed_iv_len
        if nonce_explicit is None:
            self.ready["nonce_explicit"] = False
            nonce_explicit = 0

        if isinstance(nonce_explicit, str):
            nonce_explicit = pkcs_os2ip(nonce_explicit)

        # we use super() in order to avoid any deadlock with __setattr__
        super(_AEADCipher, self).__setattr__("key", key)
        super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv)
        super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit)

        if hasattr(self, "pc_cls"):
            self._cipher = Cipher(self.pc_cls(key),
                                  self.pc_cls_mode(self._get_nonce()),
                                  backend=default_backend())
        else:
            self._cipher = self.cipher_cls(key)

    def __setattr__(self, name, val):
        if name == "key":
            if self._cipher is not None:
                if hasattr(self, "pc_cls"):
                    self._cipher.algorithm.key = val
                else:
                    self._cipher._key = val
            self.ready["key"] = True
        elif name == "fixed_iv":
            self.ready["fixed_iv"] = True
        elif name == "nonce_explicit":
            if isinstance(val, str):
                val = pkcs_os2ip(val)
            self.ready["nonce_explicit"] = True
        super(_AEADCipher, self).__setattr__(name, val)

    def _get_nonce(self):
        return (self.fixed_iv +
                pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))

    def _update_nonce_explicit(self):
        """
        Increment the explicit nonce while avoiding any overflow.
        """
        ne = self.nonce_explicit + 1
        self.nonce_explicit = ne % 2**(self.nonce_explicit_len * 8)

    def auth_encrypt(self, P, A, seq_num=None):
        """
        Encrypt the data then prepend the explicit part of the nonce. The
        authentication tag is directly appended with the most recent crypto
        API. Additional data may be authenticated without encryption (as A).

        The 'seq_num' should never be used here, it is only a safeguard needed
        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
        actually is a _AEADCipher_TLS13 (even though others are not).
        """
        if False in six.itervalues(self.ready):
            raise CipherError(P, A)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce()
            self._cipher.mode._tag = None
            encryptor = self._cipher.encryptor()
            encryptor.authenticate_additional_data(A)
            res = encryptor.update(P) + encryptor.finalize()
            res += encryptor.tag
        else:
            if isinstance(self._cipher, AESCCM):
                res = self._cipher.encrypt(self._get_nonce(), P, A,
                                           tag_length=self.tag_len)
            else:
                res = self._cipher.encrypt(self._get_nonce(), P, A)

        nonce_explicit = pkcs_i2osp(self.nonce_explicit,
                                    self.nonce_explicit_len)
        self._update_nonce_explicit()
        return nonce_explicit + res

    def auth_decrypt(self, A, C, seq_num=None, add_length=True):
        """
        Decrypt the data and authenticate the associated data (i.e. A).
        If the verification fails, an AEADTagError is raised. It is the user's
        responsibility to catch it if deemed useful. If we lack the key, we
        raise a CipherError which contains the encrypted input.

        Note that we add the TLSCiphertext length to A although we're supposed
        to add the TLSCompressed length. Fortunately, they are the same,
        but the specifications actually messed up here. :'(

        The 'add_length' switch should always be True for TLS, but we provide
        it anyway (mostly for test cases, hum).

        The 'seq_num' should never be used here, it is only a safeguard needed
        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
        actually is a _AEADCipher_TLS13 (even though others are not).
        """
        nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len],
                                      C[self.nonce_explicit_len:-self.tag_len],
                                      C[-self.tag_len:])

        if False in six.itervalues(self.ready):
            raise CipherError(nonce_explicit_str, C, mac)

        self.nonce_explicit = pkcs_os2ip(nonce_explicit_str)
        if add_length:
            A += struct.pack("!H", len(C))

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce()
            self._cipher.mode._tag = mac
            decryptor = self._cipher.decryptor()
            decryptor.authenticate_additional_data(A)
            P = decryptor.update(C)
            try:
                decryptor.finalize()
            except InvalidTag:
                raise AEADTagError(nonce_explicit_str, P, mac)
        else:
            try:
                if isinstance(self._cipher, AESCCM):
                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A,
                                             tag_length=self.tag_len)
                else:
                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A)
            except InvalidTag:
                raise AEADTagError(nonce_explicit_str,
                                   "<unauthenticated data>",
                                   mac)
        return nonce_explicit_str, P, mac

    def snapshot(self):
        c = self.__class__(self.key, self.fixed_iv, self.nonce_explicit)
        c.ready = self.ready.copy()
        return c
Пример #9
0
class _AEADCipher_TLS13(six.with_metaclass(_AEADCipherMetaclass, object)):
    """
    The hasattr(self, "pc_cls") enable support for the legacy implementation
    of GCM in the cryptography library. They should not be used, and might
    eventually be removed, with cryptography v2.0. XXX
    """
    type = "aead"

    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
        """
        'key' and 'fixed_iv' are to be provided as strings. This IV never
        changes: it is either the client_write_IV or server_write_IV.

        Note that 'nonce_explicit' is never used. It is only a safeguard for a
        call in session.py to the TLS 1.2/ChaCha20Poly1305 case (see RFC 7905).
        """
        self.ready = {"key": True, "fixed_iv": True}
        if key is None:
            self.ready["key"] = False
            key = b"\0" * self.key_len
        if fixed_iv is None:
            self.ready["fixed_iv"] = False
            fixed_iv = b"\0" * self.fixed_iv_len

        # we use super() in order to avoid any deadlock with __setattr__
        super(_AEADCipher_TLS13, self).__setattr__("key", key)
        super(_AEADCipher_TLS13, self).__setattr__("fixed_iv", fixed_iv)

        if hasattr(self, "pc_cls"):
            self._cipher = Cipher(self.pc_cls(key),
                                  self.pc_cls_mode(fixed_iv),
                                  backend=default_backend())
        else:
            self._cipher = self.cipher_cls(key)

    def __setattr__(self, name, val):
        if name == "key":
            if self._cipher is not None:
                if hasattr(self, "pc_cls"):
                    self._cipher.algorithm.key = val
                else:
                    self._cipher._key = val
            self.ready["key"] = True
        elif name == "fixed_iv":
            self.ready["fixed_iv"] = True
        super(_AEADCipher_TLS13, self).__setattr__(name, val)

    def _get_nonce(self, seq_num):
        padlen = self.fixed_iv_len - len(seq_num)
        padded_seq_num = b"\x00" * padlen + seq_num
        return strxor(padded_seq_num, self.fixed_iv)

    def auth_encrypt(self, P, A, seq_num):
        """
        Encrypt the data, and append the computed authentication code.
        TLS 1.3 does not use additional data, but we leave this option to the
        user nonetheless.

        Note that the cipher's authentication tag must be None when encrypting.
        """
        if False in six.itervalues(self.ready):
            raise CipherError(P, A)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._tag = None
            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
            encryptor = self._cipher.encryptor()
            encryptor.authenticate_additional_data(A)
            res = encryptor.update(P) + encryptor.finalize()
            res += encryptor.tag
        else:
            if (conf.crypto_valid_advanced and
                    isinstance(self._cipher, AESCCM)):
                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A,
                                           tag_length=self.tag_len)
            else:
                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A)
        return res

    def auth_decrypt(self, A, C, seq_num):
        """
        Decrypt the data and verify the authentication code (in this order).
        Note that TLS 1.3 is not supposed to use any additional data A.
        If the verification fails, an AEADTagError is raised. It is the user's
        responsibility to catch it if deemed useful. If we lack the key, we
        raise a CipherError which contains the encrypted input.
        """
        C, mac = C[:-self.tag_len], C[-self.tag_len:]
        if False in six.itervalues(self.ready):
            raise CipherError(C, mac)

        if hasattr(self, "pc_cls"):
            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
            self._cipher.mode._tag = mac
            decryptor = self._cipher.decryptor()
            decryptor.authenticate_additional_data(A)
            P = decryptor.update(C)
            try:
                decryptor.finalize()
            except InvalidTag:
                raise AEADTagError(P, mac)
        else:
            try:
                if (conf.crypto_valid_advanced and
                        isinstance(self._cipher, AESCCM)):
                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A,  # noqa: E501
                                             tag_length=self.tag_len)
                else:
                    if (conf.crypto_valid_advanced and
                            isinstance(self, Cipher_CHACHA20_POLY1305)):
                        A += struct.pack("!H", len(C))
                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A)  # noqa: E501
            except InvalidTag:
                raise AEADTagError("<unauthenticated data>", mac)
        return P, mac

    def snapshot(self):
        c = self.__class__(self.key, self.fixed_iv)
        c.ready = self.ready.copy()
        return c