|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, |
|
create_string_buffer, get_raw_buffer, |
|
SmartPointer, c_size_t, c_uint8_ptr) |
|
|
|
|
|
_raw_arc4_lib = load_pycryptodome_raw_lib("Crypto.Cipher._ARC4", """ |
|
int ARC4_stream_encrypt(void *rc4State, const uint8_t in[], |
|
uint8_t out[], size_t len); |
|
int ARC4_stream_init(uint8_t *key, size_t keylen, |
|
void **pRc4State); |
|
int ARC4_stream_destroy(void *rc4State); |
|
""") |
|
|
|
|
|
class ARC4Cipher: |
|
"""ARC4 cipher object. Do not create it directly. Use |
|
:func:`Crypto.Cipher.ARC4.new` instead. |
|
""" |
|
|
|
def __init__(self, key, *args, **kwargs): |
|
"""Initialize an ARC4 cipher object |
|
|
|
See also `new()` at the module level.""" |
|
|
|
if len(args) > 0: |
|
ndrop = args[0] |
|
args = args[1:] |
|
else: |
|
ndrop = kwargs.pop('drop', 0) |
|
|
|
if len(key) not in key_size: |
|
raise ValueError("Incorrect ARC4 key length (%d bytes)" % |
|
len(key)) |
|
|
|
self._state = VoidPointer() |
|
result = _raw_arc4_lib.ARC4_stream_init(c_uint8_ptr(key), |
|
c_size_t(len(key)), |
|
self._state.address_of()) |
|
if result != 0: |
|
raise ValueError("Error %d while creating the ARC4 cipher" |
|
% result) |
|
self._state = SmartPointer(self._state.get(), |
|
_raw_arc4_lib.ARC4_stream_destroy) |
|
|
|
if ndrop > 0: |
|
|
|
|
|
|
|
self.encrypt(b'\x00' * ndrop) |
|
|
|
self.block_size = 1 |
|
self.key_size = len(key) |
|
|
|
def encrypt(self, plaintext): |
|
"""Encrypt a piece of data. |
|
|
|
:param plaintext: The data to encrypt, of any size. |
|
:type plaintext: bytes, bytearray, memoryview |
|
:returns: the encrypted byte string, of equal length as the |
|
plaintext. |
|
""" |
|
|
|
ciphertext = create_string_buffer(len(plaintext)) |
|
result = _raw_arc4_lib.ARC4_stream_encrypt(self._state.get(), |
|
c_uint8_ptr(plaintext), |
|
ciphertext, |
|
c_size_t(len(plaintext))) |
|
if result: |
|
raise ValueError("Error %d while encrypting with RC4" % 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: bytes, bytearray, memoryview |
|
:returns: the decrypted byte string, of equal length as the |
|
ciphertext. |
|
""" |
|
|
|
try: |
|
return self.encrypt(ciphertext) |
|
except ValueError as e: |
|
raise ValueError(str(e).replace("enc", "dec")) |
|
|
|
|
|
def new(key, *args, **kwargs): |
|
"""Create a new ARC4 cipher. |
|
|
|
:param key: |
|
The secret key to use in the symmetric cipher. |
|
Its length must be in the range ``[1..256]``. |
|
The recommended length is 16 bytes. |
|
:type key: bytes, bytearray, memoryview |
|
|
|
:Keyword Arguments: |
|
* *drop* (``integer``) -- |
|
The amount of bytes to discard from the initial part of the keystream. |
|
In fact, such part has been found to be distinguishable from random |
|
data (while it shouldn't) and also correlated to key. |
|
|
|
The recommended value is 3072_ bytes. The default value is 0. |
|
|
|
:Return: an `ARC4Cipher` object |
|
|
|
.. _3072: http://eprint.iacr.org/2002/067.pdf |
|
""" |
|
return ARC4Cipher(key, *args, **kwargs) |
|
|
|
|
|
|
|
block_size = 1 |
|
|
|
key_size = range(1, 256+1) |
|
|