# HG changeset patch # User Thomas Dixon # Date 1235072713 18000 # Node ID 6428dfd7fedef0f2de1c3606a3183f04dec3a6f7 # Parent 4589f8c5eb3c923070b1576acab3dd59acfa5b5c Implemented Salsa20. Added some error checking to RC4's returnByte. Fixed copyright year in Bitwise.d and ByteConverter.d. Added Robert Smith to contributors file. Thanks buddy :) diff -r 4589f8c5eb3c -r 6428dfd7fede CONTRIBUTORS --- a/CONTRIBUTORS Sat Feb 14 19:58:20 2009 -0500 +++ b/CONTRIBUTORS Thu Feb 19 14:45:13 2009 -0500 @@ -1,2 +1,3 @@ Thomas Dixon Daniel Korsgaard <#d @ irc.freenode.net> +Robert Smith diff -r 4589f8c5eb3c -r 6428dfd7fede dcrypt/crypto/ciphers/RC4.d --- a/dcrypt/crypto/ciphers/RC4.d Sat Feb 14 19:58:20 2009 -0500 +++ b/dcrypt/crypto/ciphers/RC4.d Thu Feb 19 14:45:13 2009 -0500 @@ -1,7 +1,7 @@ /** * This file is part of the dcrypt project. * - * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. + * Copyright: Copyright (C) dcrypt contributors 2009. All rights reserved. * License: MIT * Authors: Thomas Dixon */ @@ -22,19 +22,25 @@ ubyte x, y; } + this() { + state = new ubyte[256]; + } + void init(bool encrypt, CipherParameters params) { SymmetricKey keyParams = cast(SymmetricKey)params; + if (!keyParams) throw new InvalidParameterError( name()~": Invalid parameter passed to init"); + if (keyParams.key.length < 0 || keyParams.key.length > 256) throw new InvalidKeyError( name()~": Invalid key length (requires 1-256 bytes)"); + workingKey = keyParams.key; - state = new ubyte[256]; setup(workingKey); - _encrypt = true; - _initialized = true; + + _encrypt = _initialized = true; } char[] name() { @@ -42,10 +48,14 @@ } ubyte returnByte(ubyte input) { + if (!_initialized) + throw new NotInitializedError(name()~": Cipher not initialized"); + y += state[++x]; ubyte t = state[x]; state[x] = state[y]; state[y] = t; + return (input^state[cast(ubyte)(state[x]+state[y])]); } @@ -66,6 +76,7 @@ state[y] = t; output[i] = input[i] ^ state[cast(ubyte)(state[x]+state[y])]; } + return input.length; } @@ -85,6 +96,7 @@ state[i] = state[x]; state[x] = t; } + x = y = 0; } diff -r 4589f8c5eb3c -r 6428dfd7fede dcrypt/crypto/ciphers/Salsa20.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/ciphers/Salsa20.d Thu Feb 19 14:45:13 2009 -0500 @@ -0,0 +1,300 @@ +/** + * This file is part of the dcrypt project. + * + * Copyright: Copyright (C) dcrypt contributors 2009. All rights reserved. + * License: MIT + * Authors: Thomas Dixon + */ + +module dcrypt.crypto.ciphers.Salsa20; + +import dcrypt.crypto.StreamCipher; +import dcrypt.crypto.params.ParametersWithIV; +import dcrypt.misc.ByteConverter; +import dcrypt.misc.Bitwise; + +/** Implementation of Salsa20 designed by Daniel J. Bernstein. */ +class Salsa20 : StreamCipher { + private { + // Constants + final ubyte[] sigma = cast(ubyte[])"expand 32-byte k", + tau = cast(ubyte[])"expand 16-byte k"; + + // Internal state + uint[] state; + + // Keystream and index marker + ubyte[] keyStream; + uint index; + + // Internal copies of the key and IV for resetting the cipher + ubyte[] workingKey, + workingIV; + } + + this() { + state = new uint[16]; + + // State expanded into bytes + keyStream = new ubyte[64]; + } + + void init(bool encrypt, CipherParameters params) { + ParametersWithIV ivParams = cast(ParametersWithIV)params; + + if (!ivParams) + throw new InvalidParameterError( + name()~": init parameters must include an IV. (use ParametersWithIV)"); + + SymmetricKey keyParams = cast(SymmetricKey)ivParams.parameters; + + ubyte[] iv = ivParams.iv, + key = keyParams.key; + + if (key) { + if (key.length != 16 && key.length != 32) + throw new InvalidKeyError( + name()~": Invalid key length. (requires 16 or 32 bytes)"); + + workingKey = key; + keySetup(); + + index = 0; + } + + if (!workingKey) + throw new InvalidKeyError(name()~": Key not set."); + + if (!iv || iv.length != 8) + throw new InvalidParameterError(name()~": 8 byte IV required."); + + workingIV = iv; + ivSetup(); + + _encrypt = _initialized = true; + } + + char[] name() { + return "Salsa20"; + } + + ubyte returnByte(ubyte input) { + if (!_initialized) + throw new NotInitializedError(name()~": Cipher not initialized"); + + if (index == 0) { + salsa20WordToByte(state, keyStream); + state[8]++; + if (!state[8]) + state[9]++; + // As in djb's, changing the IV after 2^70 bytes is the user's responsibility + // lol glwt + } + + ubyte result = (keyStream[index]^input); + index = (index + 1) & 0x3f; + + return result; + } + + uint update(void[] input_, void[] output_) { + if (!_initialized) + throw new NotInitializedError(name()~": Cipher not initialized"); + + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; + + if (input.length > output.length) + throw new ShortBufferError(name()~": Output buffer too short"); + + for (int i = 0; i < input.length; i++) { + if (index == 0) { + salsa20WordToByte(state, keyStream); + state[8]++; + if (!state[8]) + state[9]++; + // As in djb's, changing the IV after 2^70 bytes is the user's responsibility + // lol glwt + } + output[i] = (keyStream[index]^input[i]); + index = (index + 1) & 0x3f; + } + + return input.length; + } + + void reset() { + keySetup(); + ivSetup(); + index = 0; + } + + private void keySetup() { + uint offset; + ubyte[] constants; + + state[1] = ByteConverter.LittleEndian.to!(uint)(workingKey[0..4]); + state[2] = ByteConverter.LittleEndian.to!(uint)(workingKey[4..8]); + state[3] = ByteConverter.LittleEndian.to!(uint)(workingKey[8..12]); + state[4] = ByteConverter.LittleEndian.to!(uint)(workingKey[12..16]); + + if (workingKey.length == 32) { + constants = sigma; + offset = 16; + } else + constants = tau; + + state[11] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset..offset+4]); + state[12] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+4..offset+8]); + state[13] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+8..offset+12]); + state[14] = ByteConverter.LittleEndian.to!(uint)(workingKey[offset+12..offset+16]); + state[ 0] = ByteConverter.LittleEndian.to!(uint)(constants[0..4]); + state[ 5] = ByteConverter.LittleEndian.to!(uint)(constants[4..8]); + state[10] = ByteConverter.LittleEndian.to!(uint)(constants[8..12]); + state[15] = ByteConverter.LittleEndian.to!(uint)(constants[12..16]); + } + + private void ivSetup() { + state[6] = ByteConverter.LittleEndian.to!(uint)(workingIV[0..4]); + state[7] = ByteConverter.LittleEndian.to!(uint)(workingIV[4..8]); + state[8] = state[9] = 0; + } + + private void salsa20WordToByte(uint[] input, ref ubyte[] output) { + uint[] x = new uint[16]; + x[] = input; + + int i; + for (i = 0; i < 10; i++) { + x[ 4] ^= Bitwise.rotateLeft(x[ 0]+x[12], 7); + x[ 8] ^= Bitwise.rotateLeft(x[ 4]+x[ 0], 9); + x[12] ^= Bitwise.rotateLeft(x[ 8]+x[ 4], 13); + x[ 0] ^= Bitwise.rotateLeft(x[12]+x[ 8], 18); + x[ 9] ^= Bitwise.rotateLeft(x[ 5]+x[ 1], 7); + x[13] ^= Bitwise.rotateLeft(x[ 9]+x[ 5], 9); + x[ 1] ^= Bitwise.rotateLeft(x[13]+x[ 9], 13); + x[ 5] ^= Bitwise.rotateLeft(x[ 1]+x[13], 18); + x[14] ^= Bitwise.rotateLeft(x[10]+x[ 6], 7); + x[ 2] ^= Bitwise.rotateLeft(x[14]+x[10], 9); + x[ 6] ^= Bitwise.rotateLeft(x[ 2]+x[14], 13); + x[10] ^= Bitwise.rotateLeft(x[ 6]+x[ 2], 18); + x[ 3] ^= Bitwise.rotateLeft(x[15]+x[11], 7); + x[ 7] ^= Bitwise.rotateLeft(x[ 3]+x[15], 9); + x[11] ^= Bitwise.rotateLeft(x[ 7]+x[ 3], 13); + x[15] ^= Bitwise.rotateLeft(x[11]+x[ 7], 18); + x[ 1] ^= Bitwise.rotateLeft(x[ 0]+x[ 3], 7); + x[ 2] ^= Bitwise.rotateLeft(x[ 1]+x[ 0], 9); + x[ 3] ^= Bitwise.rotateLeft(x[ 2]+x[ 1], 13); + x[ 0] ^= Bitwise.rotateLeft(x[ 3]+x[ 2], 18); + x[ 6] ^= Bitwise.rotateLeft(x[ 5]+x[ 4], 7); + x[ 7] ^= Bitwise.rotateLeft(x[ 6]+x[ 5], 9); + x[ 4] ^= Bitwise.rotateLeft(x[ 7]+x[ 6], 13); + x[ 5] ^= Bitwise.rotateLeft(x[ 4]+x[ 7], 18); + x[11] ^= Bitwise.rotateLeft(x[10]+x[ 9], 7); + x[ 8] ^= Bitwise.rotateLeft(x[11]+x[10], 9); + x[ 9] ^= Bitwise.rotateLeft(x[ 8]+x[11], 13); + x[10] ^= Bitwise.rotateLeft(x[ 9]+x[ 8], 18); + x[12] ^= Bitwise.rotateLeft(x[15]+x[14], 7); + x[13] ^= Bitwise.rotateLeft(x[12]+x[15], 9); + x[14] ^= Bitwise.rotateLeft(x[13]+x[12], 13); + x[15] ^= Bitwise.rotateLeft(x[14]+x[13], 18); + } + + for (i = 0; i < 16; i++) + x[i] += input[i]; + + int j; + for (i = j = 0; i < x.length; i++,j+=int.sizeof) + output[j..j+int.sizeof] = ByteConverter.LittleEndian.from!(uint)(x[i]); + } + + /** Salsa20 test vectors */ + version (UnitTest) { + unittest { + static const char[][] test_keys = [ + "80000000000000000000000000000000", + "0053a6f94c9ff24598eb3e91e4378add", + "00002000000000000000000000000000"~ + "00000000000000000000000000000000", + "0f62b5085bae0154a7fa4da0f34699ec"~ + "3f92e5388bde3184d72a7dd02376c91c" + + ]; + + static const char[][] test_ivs = [ + "0000000000000000", + "0d74db42a91077de", + "0000000000000000", + "288ff65dc42b92f9" + ]; + + static const char[][] test_plaintexts = [ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000", + + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000", + + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000", + + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000"~ + "00000000000000000000000000000000" + + + ]; + + static const char[][] test_ciphertexts = [ + "4dfa5e481da23ea09a31022050859936"~ // Expected output + "da52fcee218005164f267cb65f5cfd7f"~ + "2b4f97e0ff16924a52df269515110a07"~ + "f9e460bc65ef95da58f740b7d1dbb0aa", + + "05e1e7beb697d999656bf37c1b978806"~ + "735d0b903a6007bd329927efbe1b0e2a"~ + "8137c1ae291493aa83a821755bee0b06"~ + "cd14855a67e46703ebf8f3114b584cba", + + "c29ba0da9ebebfacdebbdd1d16e5f598"~ + "7e1cb12e9083d437eaaaa4ba0cdc909e"~ + "53d052ac387d86acda8d956ba9e6f654"~ + "3065f6912a7df710b4b57f27809bafe3", + + "5e5e71f90199340304abb22a37b6625b"~ + "f883fb89ce3b21f54a10b81066ef87da"~ + "30b77699aa7379da595c77dd59542da2"~ + "08e5954f89e40eb7aa80a84a6176663f" + ]; + + Salsa20 s20 = new Salsa20(); + ubyte[] buffer = new ubyte[64]; + char[] result; + for (int i = 0; i < test_keys.length; i++) { + SymmetricKey key = new SymmetricKey(ByteConverter.hexDecode(test_keys[i])); + ParametersWithIV params = new ParametersWithIV(key, ByteConverter.hexDecode(test_ivs[i])); + + // Encryption + s20.init(true, params); + s20.update(ByteConverter.hexDecode(test_plaintexts[i]), buffer); + result = ByteConverter.hexEncode(buffer); + assert(result == test_ciphertexts[i], + s20.name()~": ("~result~") != ("~test_ciphertexts[i]~")"); + + // Decryption + s20.init(false, params); + s20.update(ByteConverter.hexDecode(test_ciphertexts[i]), buffer); + result = ByteConverter.hexEncode(buffer); + assert(result == test_plaintexts[i], + s20.name()~": ("~result~") != ("~test_plaintexts[i]~")"); + } + } + } +} diff -r 4589f8c5eb3c -r 6428dfd7fede dcrypt/misc/Bitwise.d --- a/dcrypt/misc/Bitwise.d Sat Feb 14 19:58:20 2009 -0500 +++ b/dcrypt/misc/Bitwise.d Thu Feb 19 14:45:13 2009 -0500 @@ -1,7 +1,7 @@ /** * This file is part of the dcrypt project. * - * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. + * Copyright: Copyright (C) dcrypt contributors 2009. All rights reserved. * License: MIT * Authors: Thomas Dixon */ diff -r 4589f8c5eb3c -r 6428dfd7fede dcrypt/misc/ByteConverter.d --- a/dcrypt/misc/ByteConverter.d Sat Feb 14 19:58:20 2009 -0500 +++ b/dcrypt/misc/ByteConverter.d Thu Feb 19 14:45:13 2009 -0500 @@ -1,7 +1,7 @@ /** * This file is part of the dcrypt project. * - * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. + * Copyright: Copyright (C) dcrypt contributors 2009. All rights reserved. * License: MIT * Authors: Thomas Dixon */ diff -r 4589f8c5eb3c -r 6428dfd7fede dsss.conf --- a/dsss.conf Sat Feb 14 19:58:20 2009 -0500 +++ b/dsss.conf Thu Feb 19 14:45:13 2009 -0500 @@ -2,4 +2,3 @@ [dcrypt] buildflags = -O target = dcrypt -