Revision: 58543
Updated Code
at July 22, 2012 15:40 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. **TODO**: They should be hashed, but they # are not currently, until I select a hash function with an appropriately sized output, which # won't limit the keyspace available to IARC4. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = key_length >> 1 key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: b'\x01' + bytes(255) + b'\x02' + bytes(255) # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'\xb1\xc5\x8d)\x90\xf9WN\x94' if (__name__ == "__main__"): nonce = b'\x01' + bytes(255) + b'\x02' + bytes(255) key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58542
Updated Code
at July 22, 2012 15:15 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = int(key_length / 2) key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: b'\x01' + bytes(255) + b'\x02' + bytes(255) # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'\xb1\xc5\x8d)\x90\xf9WN\x94' if (__name__ == "__main__"): nonce = b'\x01' + bytes(255) + b'\x02' + bytes(255) key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58541
Updated Code
at July 22, 2012 15:14 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. They should be hashed, but they are not currently, until I select a hash function with an appropriately sized output, which won't limit the keyspace available to IARC4. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = int(key_length / 2) key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: b'\x01' + bytes(255) + b'\x02' + bytes(255) # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'\xb1\xc5\x8d)\x90\xf9WN\x94' if (__name__ == "__main__"): nonce = b'\x01' + bytes(255) + b'\x02' + bytes(255) key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58540
Updated Code
at July 21, 2012 09:12 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = int(key_length / 2) key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: b'\x01' + bytes(255) + b'\x02' + bytes(255) # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'\xb1\xc5\x8d)\x90\xf9WN\x94' if (__name__ == "__main__"): nonce = b'\x01' + bytes(255) + b'\x02' + bytes(255) key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58539
Updated Code
at July 21, 2012 09:08 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = int(key_length / 2) key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: b'\x01' + bytes(255) + b'\x02' + bytes(255) # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'\xb1\xc5\x8d)\x90\xf9WN\x94' if (__name__ == "__main__"): nonce = b'\x01' + bytes(255) + b'\x02' + bytes(255) key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58538
Updated Code
at July 21, 2012 09:05 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. # - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each # state space. Each subkey and subnonce is XOR'd together to produce a new subkey. # - Takes a nonce alongside the key. The key and nonce must be random and of even, equal # length, with 512 bytes per key/nonce suggested. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # Passing the `expires` option to IARC4 will alter this limit. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=512): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Generic error (e.g., key and nonce of different lengths) class KeyError(Exception): pass # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(KeyError): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256, expires=8447): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == expires): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering. def IARC4(key, nonce, drop=8192, expires=255): key_length = len(key) # Keys and nonces must be of even, equal lengths, 512 bytes apiece is recommended. if ((key_length != len(nonce)) or ((key_length % 2) != 0)): raise KeyError # Split key and nonce in half to gain key_a, key_b, nonce_a, and nonce_b key_split = int(key_length / 2) key_a = BitArray(bytes=key[:key_split]) key_b = BitArray(bytes=key[key_split:]) nonce_a = BitArray(bytes=nonce[:key_split]) nonce_b = BitArray(bytes=nonce[key_split:]) # XOR keys and nonces. key_a = (key_a ^ nonce_a).bytes key_b = (key_b ^ nonce_b).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b, expires=(drop + expires)) # Discard first 4096 iterations of each state space for dropped in range(drop): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce: bytes(255) + b'\x01' + bytes(255) + b'\x02' # key: b'key_a' + bytes(251) + b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'B\xfca \xe0\xc5Ic\xa5' if (__name__ == "__main__"): nonce = bytes(255) + b'\x01' + bytes(255) + b'\x02' key = b'key_a' + bytes(251) + b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58537
Updated Code
at July 21, 2012 08:33 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. (Each key should be combined with a nonce first.) # - Uses 2 state spaces (RC4A). Requires a nonce and key for each state space. # - Takes a nonce alongside each key. They are XOR'd to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # # This code should not be considered secure. It has not been cryptanalyzed and should not be used # in production. This code is strictly experimental. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key_a, nonce_a, key_b, nonce_b): # XOR keys and nonces. They must be of equal length. key_a = (BitArray(bytes=key_a) ^ BitArray(bytes=nonce_a)).bytes key_b = (BitArray(bytes=key_b) ^ BitArray(bytes=nonce_b)).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce_a: bytes(255) + b'\x01' # key_a: b'key_a' + bytes(251) # nonce_b: bytes(255) + b'\x02' # key_b: b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'B\xfca \xe0\xc5Ic\xa5' if (__name__ == "__main__"): nonce_a = bytes(255) + b'\x01' key_a = b'key_a' + bytes(251) nonce_b = bytes(255) + b'\x02' key_b = b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key_a, nonce_a, key_b, nonce_b) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key_a, nonce_a, key_b, nonce_b) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce_a: %s\nkey_a: %s\nnonce_b: %s\nkey_b: %s\nplaintext: %s\nciphertext: %s" % (nonce_a, key_a, nonce_b, key_b, plaintext, ciphertext))
Revision: 58536
Updated Code
at July 21, 2012 08:26 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. (Each key should be combined with a nonce first.) # - Uses 2 state spaces (RC4A). Requires a nonce and key for each state space. # - Takes a nonce alongside each key. They are XOR'd to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). # - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key_a, nonce_a, key_b, nonce_b): # XOR keys and nonces. They must be of equal length. key_a = (BitArray(bytes=key_a) ^ BitArray(bytes=nonce_a)).bytes key_b = (BitArray(bytes=key_b) ^ BitArray(bytes=nonce_b)).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce_a: bytes(255) + b'\x01' # key_a: b'key_a' + bytes(251) # nonce_b: bytes(255) + b'\x02' # key_b: b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'B\xfca \xe0\xc5Ic\xa5' if (__name__ == "__main__"): nonce_a = bytes(255) + b'\x01' key_a = b'key_a' + bytes(251) nonce_b = bytes(255) + b'\x02' key_b = b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key_a, nonce_a, key_b, nonce_b) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key_a, nonce_a, key_b, nonce_b) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce_a: %s\nkey_a: %s\nnonce_b: %s\nkey_b: %s\nplaintext: %s\nciphertext: %s" % (nonce_a, key_a, nonce_b, key_b, plaintext, ciphertext))
Revision: 58535
Updated Code
at July 21, 2012 08:21 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Uses KSA from VMPC minus an IV. (Each key should be combined with a nonce first.) # - Uses 2 state spaces (RC4A). Requires a nonce and key for each state space. # - Takes a nonce alongside each key. They are XOR'd to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # VMPC state preparation without vector def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for n in range(state_size*3): i = n % state_size j = state[(((j + state[i]) % state_size) + key[i % key_size]) % state_size] state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key_a, nonce_a, key_b, nonce_b): # XOR keys and nonces. They must be of equal length. key_a = (BitArray(bytes=key_a) ^ BitArray(bytes=nonce_a)).bytes key_b = (BitArray(bytes=key_b) ^ BitArray(bytes=nonce_b)).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce_a: bytes(255) + b'\x01' # key_a: b'key_a' + bytes(251) # nonce_b: bytes(255) + b'\x02' # key_b: b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'B\xfca \xe0\xc5Ic\xa5' if (__name__ == "__main__"): nonce_a = bytes(255) + b'\x01' key_a = b'key_a' + bytes(251) nonce_b = bytes(255) + b'\x02' key_b = b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key_a, nonce_a, key_b, nonce_b) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key_a, nonce_a, key_b, nonce_b) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce_a: %s\nkey_a: %s\nnonce_b: %s\nkey_b: %s\nplaintext: %s\nciphertext: %s" % (nonce_a, key_a, nonce_b, key_b, plaintext, ciphertext))
Revision: 58534
Updated Code
at July 21, 2012 07:38 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Uses 2 state spaces (RC4A). Requires a nonce and key for each state space. # - Takes a nonce alongside the key. They are XOR'd to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key_a, nonce_a, key_b, nonce_b): # XOR keys and nonces. They must be of equal length. key_a = (BitArray(bytes=key_a) ^ BitArray(bytes=nonce_a)).bytes key_b = (BitArray(bytes=key_b) ^ BitArray(bytes=nonce_b)).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce_a: bytes(255) + b'\x01' # key_a: b'key_a' + bytes(251) # nonce_b: bytes(255) + b'\x02' # key_b: b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'n\xc7\x88\xefF>G\xe5\x03' if (__name__ == "__main__"): nonce_a = bytes(255) + b'\x01' key_a = b'key_a' + bytes(251) nonce_b = bytes(255) + b'\x02' key_b = b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key_a, nonce_a, key_b, nonce_b) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key_a, nonce_a, key_b, nonce_b) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce_a: %s\nkey_a: %s\nnonce_b: %s\nkey_b: %s\nplaintext: %s\nciphertext: %s" % (nonce_a, key_a, nonce_b, key_b, plaintext, ciphertext))
Revision: 58533
Updated Code
at July 21, 2012 07:23 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Uses 2 state spaces (RC4A). Requires a nonce and key for each state space. # - Takes a nonce alongside the key. They are XOR'd to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # Generates a random n-byte string. Makes no checks to ensure uniqueness. def random_bytes(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key_a, nonce_a, key_b, nonce_b): # XOR keys and nonces. They must be of equal length. key_a = (BitArray(bytes=key_a) ^ BitArray(bytes=nonce_a)).bytes key_b = (BitArray(bytes=key_b) ^ BitArray(bytes=nonce_b)).bytes # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vector for IARC4 # # nonce_a: bytes(255) + b'\x01' # key_a: b'key_a' + bytes(251) # nonce_b: bytes(255) + b'\x02' # key_b: b'key_b' + bytes(251) # plaintext: b'Plaintext' # ciphertext: b'n\xc7\x88\xefF>G\xe5\x03' if (__name__ == "__main__"): from pprint import pprint as pp nonce_a = bytes(255) + b'\x01' key_a = b'key_a' + bytes(251) nonce_b = bytes(255) + b'\x02' key_b = b'key_b' + bytes(251) plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key_a, nonce_a, key_b, nonce_b) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key_a, nonce_a, key_b, nonce_b) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce_a: %s\nkey_a: %s\nnonce_b: %s\nkey_b: %s\nplaintext: %s\nciphertext: %s" % (nonce_a, key_a, nonce_b, key_b, plaintext, ciphertext))
Revision: 58532
Updated Code
at July 20, 2012 08:19 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is expanded to # generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Key expansion function which tries not to reduce the size of the input to the hash function. # # key_a(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))) # key_b(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])) ^ # (hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])))[(key.length/2):] + # hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))[:(key.length/2)]) def expand_key(hasher, key): hasher_a = hasher.copy() hasher_b = hasher.copy() key = BitArray(bytes=key) split = int(key.length / 2) # XOR the key against itself with its halves swapped, then hash key = key ^ (key[split:] + key[:split]) hasher.update(key.bytes) key = BitArray(bytes=hasher.digest()) # Key A is the hash of the previous hash # Key B is the hash of the previous hash XOR'd against itself with its halves swapped. hasher_a.update(key.bytes) hasher_b.update((key ^ (key[split:] + key[:split])).bytes) return (hasher_a.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Return the prepared PRNG to begin accepting input def _IARC4(text): # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte return _IARC4 # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b',\xe6K\xfbUW\xf2f\xee' # # nonce: b'Nonce' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\xef\xedI\x9cd\xd6w\xd9\x15' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encrypter = IARC4(key, nonce) encryption = encrypter(plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decrypter = IARC4(key, nonce) decryption = decrypter(ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58531
Updated Code
at July 20, 2012 07:14 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is expanded to # generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Key expansion function which tries not to reduce the size of the input to the hash function. # # key_a(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))) # key_b(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])) ^ # (hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])))[(key.length/2):] + # hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))[:(key.length/2)]) def expand_key(hasher, key): hasher_a = hasher.copy() hasher_b = hasher.copy() key = BitArray(bytes=key) split = int(key.length / 2) # XOR the key against itself with its halves swapped, then hash key = key ^ (key[split:] + key[:split]) hasher.update(key.bytes) key = BitArray(bytes=hasher.digest()) # Key A is the hash of the previous hash # Key B is the hash of the previous hash XOR'd against itself with its halves swapped. hasher_a.update(key.bytes) hasher_b.update((key ^ (key[split:] + key[:split])).bytes) return (hasher_a.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b',\xe6K\xfbUW\xf2f\xee' # # nonce: b'Nonce' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\xef\xedI\x9cd\xd6w\xd9\x15' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58530
Updated Code
at July 20, 2012 07:10 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). A # KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is expanded to # generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Key expansion function which tries not to reduce the size of the input to the hash function. # # key_a(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))) # key_b(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])) ^ # (hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])))[(key.length/2):] + # hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))[:(key.length/2)]) def expand_key(hasher, key): hasher_a = hasher.copy() hasher_b = hasher.copy() key = BitArray(bytes=key) split = int(key.length / 2) # XOR the key against itself with its halves swapped, then hash key = key ^ (key[split:] + key[:split]) hasher.update(key.bytes) key = BitArray(bytes=hasher.digest()) # Key A is the hash of the previous hash # Key B is the hash of the previous hash XOR'd against itself with its halves swapped. hasher_a.update(key.bytes) hasher_b.update((key ^ (key[split:] + key[:split])).bytes) return (hasher_a.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 while True: n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] # Limit the PRNG to 255 iterations after dropping 8192 iterations if (n == 8447): break n += 1 j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(8192): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b',\xe6K\xfbUW\xf2f\xee' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'\xef\xedI\x9cd\xd6w\xd9\x15' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58529
Updated Code
at July 20, 2012 06:58 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 4096 iterations of the PRNG's state (RC4-drop4096). A KeyExpiredError will # be raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is expanded to # generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Key expansion function which tries not to reduce the size of the input to the hash function. # # key_a(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))) # key_b(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])) ^ # (hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])))[(key.length/2):] + # hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))[:(key.length/2)]) def expand_key(hasher, key): hasher_a = hasher.copy() hasher_b = hasher.copy() key = BitArray(bytes=key) split = int(key.length / 2) # XOR the key against itself with its halves swapped, then hash key = key ^ (key[split:] + key[:split]) hasher.update(key.bytes) key = BitArray(bytes=hasher.digest()) # Key A is the hash of the previous hash # Key B is the hash of the previous hash XOR'd against itself with its halves swapped. hasher_a.update(key.bytes) hasher_b.update((key ^ (key[split:] + key[:split])).bytes) return (hasher_a.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 # Limit the PRNG to 255 iterations after dropping 4096 iterations while (n < 4351): n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(4096): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\x9f\x0b;\xf7w\xcc\xb3\xcd\xec' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'\xd5\xfc\xa7\xc7c\x9f\xab1\x97' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = b'Nonce' plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58528
Updated Code
at July 20, 2012 06:54 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 4096 iterations of the PRNG's state (RC4-drop4096). A KeyExpiredError will # be raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is expanded to # generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Key expansion function which tries not to reduce the size of the input to the hash function. # # key_a(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))) # key_b(key) = hash(hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])) ^ # (hash(key ^ (key[(key.length/2):] + key[:(key.length/2)])))[(key.length/2):] + # hash(key ^ (key[(key.length/2):] + key[:(key.length/2)]))[:(key.length/2)]) def expand_key(hasher, key): hasher_a = hasher.copy() hasher_b = hasher.copy() key = BitArray(bytes=key) split = int(key.length / 2) # XOR the key against itself with its halves swapped, then hash key = key ^ (key[split:] + key[:split]) hasher.update(key.bytes) key = BitArray(bytes=hasher.digest()) # Key A is the hash of the previous hash # Key B is the hash of the previous hash XOR'd against itself with its halves swapped. hasher_a.update(key.bytes) hasher_b.update((key ^ (key[split:] + key[:split])).bytes) return (hasher.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 # Limit the PRNG to 255 iterations after dropping 4096 iterations while (n < 4351): n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(4096): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\x01\xccd\xecaS\xc2&\x16' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'\x04;\xf7\x98\xb1Y\x81\x87C' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58527
Updated Code
at July 20, 2012 06:34 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded (HMAC-style), XOR'd, and hashed with # RIPEMD to produce a new key. # - Drops the first 4096 iterations of the PRNG's state (RC4-drop4096). A KeyExpiredError will # be raised after 255 iterations of the PRNG, excluding the initial drop. # - Uses 2 state spaces (RC4A). The key resulting from the key and nonce is futher split, # XOR'd, split, and the halves hashed with RIPEMD to generate a key for each state space. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key # Generates a random n-byte string. Makes no checks to ensure uniqueness. def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes # Split the key in half, XOR the halves together, hash each half of the result to yield 2 new keys. # This assumes your input key has already been padded, XOR'd with a padded nonce, and hashed. def expand_key(hasher, key): key = BitArray(bytes=key) split = int(key.length / 2) key = (key[:split] ^ key[split:]).bytes hasher_b = hasher.copy() hasher.update(key[:split]) hasher_b.update(key[split:]) return (hasher.digest(), hasher_b.digest()) # Standard ARC4 state preparation def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state # Raised if a key/nonce combination has been used too many times already (255). class KeyExpiredError(Exception): pass # Uses 2 state spaces (RC4A) def pseudorandom_generator(state_a, state_b, state_size=256): i = 0 j_a = 0 j_b = 0 n = 0 # Limit the PRNG to 255 iterations after dropping 4096 iterations while (n < 4351): n += 1 i = (i + 1) % state_size j_a = (j_a + state_a[i]) % state_size state_a[i] ^= state_a[j_a] state_a[j_a] ^= state_a[i] state_a[i] ^= state_a[j_a] yield state_b[(state_a[i] + state_a[j_a]) % state_size] j_b = (j_b + state_b[i]) % state_size state_b[i] ^= state_b[j_b] state_b[j_b] ^= state_b[i] state_b[i] ^= state_b[j_b] yield state_a[(state_b[i] + state_b[j_b]) % state_size] raise KeyExpiredError # Main keystream generation and ciphering def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Expand the key into two keys for the two state spaces key_a, key_b = expand_key(hasher.copy(), key) # Prepare the PRNG state_a = prepare_state(key_a) state_b = prepare_state(key_b) keystream = pseudorandom_generator(state_a, state_b) # Discard first 4096 iterations for dropped in range(4096): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'"\xe3\xb4\x1e\x17\xe8W\xa5\xb8' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'K\xf8\x8f\x1c\x8bA\xbd\xff\xcc' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58526
Updated Code
at July 20, 2012 05:52 by weilawei
Updated Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded, XOR'd, and hashed to produce a new key. # - Drops the first 4096 iterations of the PRNG's state. (RC4-drop4096) # - A KeyExpiredError will be raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state class KeyExpiredError(Exception): pass def pseudorandom_generator(state): size = len(state) i = 0 j = 0 n = 0 # Limit the PRNG to 255 iterations after dropping 4096 iterations while (n < 4351): n += 1 i = (i + 1) % size j = (j + state[i]) % size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] yield state[(state[i] + state[j]) % size] raise KeyExpiredError def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Prepare the PRNG state = prepare_state(key) keystream = pseudorandom_generator(state) # Discard first 4096 iterations for dropped in range(4096): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\xa8\x01\x02\xcdSt\xfc\x9a\xfc' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'Znb\xa7\xe1\xe2\x9bh\x0c' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Revision: 58525
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at July 20, 2012 05:48 by weilawei
Initial Code
#!/usr/bin/env python3 # This code is public domain. # # Improved ARC4 (IARC4) contains a number of improvements over naive ARC4: # # - Takes a nonce alongside the key. They are padded, XOR'd, and hashed to produce a new key. # - Drops the first 4096 iterations of the PRNG's state. (RC4-drop4096) # - A KeyExpiredError will be raised after 255 iterations of the PRNG, excluding the initial drop. from bitstring import BitArray from Crypto.Hash import RIPEMD from Crypto.Random import random # HMAC-style padding for key and nonce def pad_key(hasher, key): block_size = hasher.digest_size if (len(key) > block_size): hasher.update(key) return hasher.digest() elif (len(key) < block_size): return key + (b'\x00' * (block_size - len(key))) return key def generate_nonce(n_bytes=256): return BitArray(uint=random.getrandbits(n_bytes*8), length=(n_bytes*8)).bytes def prepare_state(key, state_size=256): key_size = len(key) state = [i for i in range(state_size)] j = 0 for i in range(state_size): j = (j + state[i] + key[i % key_size]) % state_size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] return state class KeyExpiredError(Exception): pass def pseudorandom_generator(state): size = len(state) i = 0 j = 0 n = 0 # Limit the PRNG to 255 iterations after dropping 4096 iterations while True and (n < 4351): n += 1 i = (i + 1) % size j = (j + state[i]) % size state[i] ^= state[j] state[j] ^= state[i] state[i] ^= state[j] yield state[(state[i] + state[j]) % size] raise KeyExpiredError def IARC4(key, nonce, text): # Pad both key and nonce using HMAC-style padding function hasher = RIPEMD.new() key = pad_key(hasher.copy(), key) nonce = pad_key(hasher.copy(), nonce) # Combine key and nonce by hash(key ^ nonce) hasher.update((BitArray(bytes=key) ^ BitArray(bytes=nonce)).bytes) key = hasher.digest() # Prepare the PRNG state = prepare_state(key) keystream = pseudorandom_generator(state) # Discard first 4096 iterations for dropped in range(4096): next(keystream) # Combine the keystream and the text stream for key_byte, text_byte in zip(keystream, text): yield key_byte ^ text_byte # Test Vectors for IARC4 # # nonce: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' # key: b'Key' # plaintext: b'Plaintext' # ciphertext: b'\xa8\x01\x02\xcdSt\xfc\x9a\xfc' # # key: b'Key' # nonce: b'Nonce' # plaintext: b'Plaintext' # ciphertext: b'Znb\xa7\xe1\xe2\x9bh\x0c' if (__name__ == "__main__"): from pprint import pprint as pp key = b'Key' nonce = generate_nonce() plaintext = b'Plaintext' stream_length = len(plaintext) encryption = IARC4(key, nonce, plaintext) ciphertext = bytes([next(encryption) for i in range(stream_length)]) decryption = IARC4(key, nonce, ciphertext) plaintext = bytes([next(decryption) for i in range(stream_length)]) print("nonce: %s\nkey: %s\nplaintext: %s\nciphertext: %s" % (nonce, key, plaintext, ciphertext))
Initial URL
Initial Description
This code is public domain. Improved ARC4 (IARC4) contains a number of proposed improvements over naive ARC4: - Uses KSA from VMPC minus an IV. - Uses 2 state spaces (RC4A). Splits the key and nonce to produce a key and nonce for each state space. Each subkey and subnonce is XOR'd together to produce a new subkey. **TODO**: They should be hashed, but they are not currently, until I select a hash function with an appropriately sized output, which won't limit the keyspace available to IARC4. - Takes a nonce alongside the key. The key and nonce must be random and of even, equal length, with 512 bytes per key/nonce suggested. - Drops the first 8192 (4096 per state space) iterations of the PRNG (RC4-drop8192). - A KeyExpiredError is raised after 255 iterations of the PRNG, excluding the initial drop. Passing the `expires` option to IARC4 will alter this limit. This code should not be considered secure. It has not been cryptanalyzed and should not be used in production. This code is strictly experimental.
Initial Title
Improved ARC4 (IARC4)
Initial Tags
Initial Language
Python