123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- # ===================================================================
- #
- # Copyright (c) 2014, Legrandin <helderijs@gmail.com>
- # All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions
- # are met:
- #
- # 1. Redistributions of source code must retain the above copyright
- # notice, this list of conditions and the following disclaimer.
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions and the following disclaimer in
- # the documentation and/or other materials provided with the
- # distribution.
- #
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- # POSSIBILITY OF SUCH DAMAGE.
- # ===================================================================
- from Crypto.Random import get_random_bytes
- from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
- create_string_buffer,
- get_raw_buffer, VoidPointer,
- SmartPointer, c_size_t,
- expect_byte_string, c_ulong)
- _raw_chacha20_lib = load_pycryptodome_raw_lib("Crypto.Cipher._chacha20",
- """
- int chacha20_init(void **pState,
- const uint8_t *key,
- size_t keySize,
- const uint8_t *nonce,
- size_t nonceSize);
- int chacha20_destroy(void *state);
- int chacha20_encrypt(void *state,
- const uint8_t in[],
- uint8_t out[],
- size_t len);
- int chacha20_seek(void *state,
- unsigned long block_high,
- unsigned long block_low,
- unsigned offset);
- """)
- class ChaCha20Cipher:
- """ChaCha20 cipher object. Do not create it directly. Use :py:func:`new` instead.
- :var nonce: The nonce with length 8
- :vartype nonce: byte string
- """
- block_size = 1
- def __init__(self, key, nonce):
- """Initialize a ChaCha20 cipher object
- See also `new()` at the module level."""
- expect_byte_string(key)
- expect_byte_string(nonce)
- self.nonce = nonce
- self._next = ( self.encrypt, self.decrypt )
- self._state = VoidPointer()
- result = _raw_chacha20_lib.chacha20_init(
- self._state.address_of(),
- key,
- c_size_t(len(key)),
- nonce,
- c_size_t(len(nonce)))
- if result:
- raise ValueError("Error %d instantiating a ChaCha20 cipher")
- self._state = SmartPointer(self._state.get(),
- _raw_chacha20_lib.chacha20_destroy)
- def encrypt(self, plaintext):
- """Encrypt a piece of data.
- :param plaintext: The data to encrypt, of any size.
- :type plaintext: byte string
- :returns: the encrypted byte string, of equal length as the
- plaintext.
- """
- if self.encrypt not in self._next:
- raise TypeError("Cipher object can only be used for decryption")
- self._next = ( self.encrypt, )
- return self._encrypt(plaintext)
- def _encrypt(self, plaintext):
- """Encrypt without FSM checks"""
- expect_byte_string(plaintext)
- ciphertext = create_string_buffer(len(plaintext))
- result = _raw_chacha20_lib.chacha20_encrypt(
- self._state.get(),
- plaintext,
- ciphertext,
- c_size_t(len(plaintext)))
- if result:
- raise ValueError("Error %d while encrypting with ChaCha20" % result)
- return get_raw_buffer(ciphertext)
- def decrypt(self, ciphertext):
- """Decrypt a piece of data.
- :param ciphertext: The data to decrypt, of any size.
- :type ciphertext: byte string
- :returns: the decrypted byte string, of equal length as the
- ciphertext.
- """
- if self.decrypt not in self._next:
- raise TypeError("Cipher object can only be used for encryption")
- self._next = ( self.decrypt, )
- try:
- return self._encrypt(ciphertext)
- except ValueError, e:
- raise ValueError(str(e).replace("enc", "dec"))
- def seek(self, position):
- """Seek to a certain position in the key stream.
- :param integer position:
- The absolute position within the key stream, in bytes.
- """
- offset = position & 0x3f
- position >>= 6
- block_low = position & 0xFFFFFFFF
- block_high = position >> 32
- result = _raw_chacha20_lib.chacha20_seek(
- self._state.get(),
- c_ulong(block_high),
- c_ulong(block_low),
- offset
- )
- if result:
- raise ValueError("Error %d while seeking with ChaCha20" % result)
- def new(**kwargs):
- """Create a new ChaCha20 cipher
- :keyword key: The secret key to use. It must be 32 bytes long.
- :type key: byte string
- :keyword nonce:
- A mandatory value that must never be reused for any other encryption
- done with this key. It must be 8 bytes long.
- If not provided, a random byte string will be generated (you can read
- it back via the ``nonce`` attribute of the returned object).
- :type nonce: byte string
- :Return: a :class:`Crypto.Cipher.ChaCha20.ChaCha20Cipher` object
- """
- try:
- key = kwargs.pop("key")
- except KeyError, e:
- raise TypeError("Missing parameter %s" % e)
- nonce = kwargs.pop("nonce", None)
- if nonce is None:
- nonce = get_random_bytes(8)
- if len(key) != 32:
- raise ValueError("ChaCha20 key must be 32 bytes long")
- if len(nonce) != 8:
- raise ValueError("ChaCha20 nonce must be 8 bytes long")
- if kwargs:
- raise TypeError("Unknown parameters: " + str(kwargs))
- return ChaCha20Cipher(key, nonce)
- # Size of a data block (in bytes)
- block_size = 1
- # Size of a key (in bytes)
- key_size = 32
|