# HG changeset patch # User Thomas Dixon # Date 1220121503 14400 # Node ID 8c7f8fecdd754f12cd075aa15a898f3e2b9d2948 # Parent 02970e63257d9d22a1ffca0a3ec7b85a1329998a Added ManagedBlockCipher, changed Crypto to just import everything, made Hash.update() return itself (for chaining) and ditched BlockCipherWrapper. diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/BlockCipherWrapper.d --- a/dcrypt/crypto/BlockCipherWrapper.d Wed Aug 20 23:33:02 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/** - * This file is part of the dcrypt project. - * - * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. - * License: MIT - * Authors: Thomas Dixon - */ - -module dcrypt.crypto.BlockCipherWrapper; - -import dcrypt.crypto.BlockCipher; -import dcrypt.crypto.BlockCipherPadding; - -/** - * Wraps a block cipher, enabling the encryption of a stream - * whose length is a multiple of blockSize. Padding, if specified, - * is to be applied otherwise and the result subsequently processed. - */ -class BlockCipherWrapper : BlockCipher { - BlockCipher cipher; - BlockCipherPadding padding; - bool encrypt; - - this(BlockCipher c, BlockCipherPadding p=null) { - cipher = c; - padding = p; // null signifies no padding is to be applied - } - - void init(bool encrypt, CipherParameters params) { - this.encrypt = encrypt; - cipher.init(encrypt, params); - } - - char[] name() { - if (padding !is null) - return cipher.name~"/"~padding.name; - return cipher.name; - } - - uint blockSize() { - return cipher.blockSize; - } - - ubyte[] process(void[] input_) { - ubyte[] input = cast(ubyte[]) input_; - - uint padLen = blockSize - (input.length % blockSize); - if (padding !is null && !encrypt && padLen != blockSize) - throw new ShortBufferError( - name()~": Encrypted padded block should be multiple of block size."); - - ubyte[] output = new ubyte[input.length]; - - uint len = input.length, - i = 0; - while (len >= blockSize) { - output[i..i+blockSize] = cipher.process(input[i..i+blockSize]); - len -= blockSize; - i += blockSize; - } - - if (padding !is null) { - if (encrypt) { - output.length = output.length + padLen; - ubyte[] t = new ubyte[blockSize]; - if (len > 0) { - t[0..len] = input[i..i+len]; - t[len..blockSize] = padding.pad(padLen); - } else - t[] = padding.pad(padLen); - output[i..i+blockSize] = cipher.process(t); - } else { - output.length = output.length - padding.unpad(output); - } - } else - if (len > 0) - output[i..i+len] = cipher.process(input[i..i+len]); - - return output; - } - - void reset() { - cipher.reset(); - } -} \ No newline at end of file diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/Cipher.d --- a/dcrypt/crypto/Cipher.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/Cipher.d Sat Aug 30 14:38:23 2008 -0400 @@ -31,14 +31,15 @@ /** * Process a block of plaintext data from the input array - * and return the encrypted data. + * and place it in the output array. * * Params: * input_ = Array containing input data. + * output_ = Array to hold the output data. * - * Returns: The encrypted data. + * Returns: The amount of encrypted data processed. */ - ubyte[] process(void[] input_); + uint update(void[] input_, void[] output_); /** Returns: The name of the algorithm of this cipher. */ char[] name(); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/Crypto.d --- a/dcrypt/crypto/Crypto.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/Crypto.d Sat Aug 30 14:38:23 2008 -0400 @@ -6,16 +6,17 @@ * Authors: Thomas Dixon */ +/** Import _everything_ */ module dcrypt.crypto.Crypto; // Hash functions public import dcrypt.crypto.Hash; -import dcrypt.crypto.hashes.MD5; -import dcrypt.crypto.hashes.SHA1; -import dcrypt.crypto.hashes.SHA224; -import dcrypt.crypto.hashes.SHA256; -import dcrypt.crypto.hashes.SHA384; -import dcrypt.crypto.hashes.SHA512; +public import dcrypt.crypto.hashes.MD5; +public import dcrypt.crypto.hashes.SHA1; +public import dcrypt.crypto.hashes.SHA224; +public import dcrypt.crypto.hashes.SHA256; +public import dcrypt.crypto.hashes.SHA384; +public import dcrypt.crypto.hashes.SHA512; // Message authentication codes public import dcrypt.crypto.MAC; @@ -29,138 +30,22 @@ public import dcrypt.crypto.Cipher; public import dcrypt.crypto.BlockCipher; public import dcrypt.crypto.StreamCipher; -public import dcrypt.crypto.BlockCipherWrapper; -import dcrypt.crypto.ciphers.Blowfish; -import dcrypt.crypto.ciphers.RC6; -import dcrypt.crypto.ciphers.TEA; -import dcrypt.crypto.ciphers.XTEA; -import dcrypt.crypto.ciphers.AES; -import dcrypt.crypto.ciphers.RC4; +public import dcrypt.crypto.ManagedBlockCipher; +public import dcrypt.crypto.ciphers.Blowfish; +public import dcrypt.crypto.ciphers.RC6; +public import dcrypt.crypto.ciphers.TEA; +public import dcrypt.crypto.ciphers.XTEA; +public import dcrypt.crypto.ciphers.AES; +public import dcrypt.crypto.ciphers.RC4; // Block modes -import dcrypt.crypto.modes.CBC; -import dcrypt.crypto.modes.CTR; +public import dcrypt.crypto.modes.CBC; +public import dcrypt.crypto.modes.CTR; // Block padding public import dcrypt.crypto.BlockCipherPadding; -import dcrypt.crypto.padding.NullByte; -import dcrypt.crypto.padding.X923; -import dcrypt.crypto.padding.PKCS7; -import dcrypt.crypto.padding.RFC1321; - -enum Hashes { - MD5=0, - SHA1, - SHA224, - SHA256, - SHA384, - SHA512 -} - -enum Ciphers { - // Block ciphers - Blowfish=0, - AES, - RC6, - TEA, - XTEA, - - // Stream ciphers - RC4 -} - -enum Modes { - ECB=-1, - CTR, - CBC -} - -enum Padding { - None=-1, - NullByte, // lol - X923, - PKCS7, - RFC1321 -} +public import dcrypt.crypto.padding.NullByte; +public import dcrypt.crypto.padding.X923; +public import dcrypt.crypto.padding.PKCS7; +public import dcrypt.crypto.padding.RFC1321; -struct Crypto { - static Hash createHash(uint hash) { - switch (hash) { - case Hashes.MD5: - return new MD5; - case Hashes.SHA1: - return new SHA1; - case Hashes.SHA224: - return new SHA224; - case Hashes.SHA256: - return new SHA256; - case Hashes.SHA384: - return new SHA384; - case Hashes.SHA512: - return new SHA512; - default: - throw new InvalidParameterError("Unknown hash function passed to createHash()"); - } - } - - static Cipher createCipher(uint cipher, int mode=Modes.ECB, int padding=Padding.None) { - BlockCipher c = null, - m = null; - BlockCipherPadding p = null; - switch (cipher) { - case Ciphers.Blowfish: - c = new Blowfish; - break; - case Ciphers.AES: - c = new AES; - break; - case Ciphers.RC6: - c = new RC6; - break; - case Ciphers.TEA: - c = new TEA; - break; - case Ciphers.XTEA: - c = new XTEA; - break; - case Ciphers.RC4: - return new RC4; // Note the return - default: - throw new InvalidParameterError("Unknown cipher passed to createCipher()"); - } - - switch (mode) { - case Modes.ECB: - break; - case Modes.CTR: - m = new CTR(c); - break; - case Modes.CBC: - m = new CBC(c); - break; - default: - throw new InvalidParameterError("Unknown mode passed to createCipher()"); - } - - switch (padding) { - case Padding.None: - break; - case Padding.NullByte: - p = new NullByte; - break; - case Padding.X923: - p = new X923; - break; - case Padding.PKCS7: - p = new PKCS7; - break; - case Padding.RFC1321: - p = new RFC1321; - break; - default: - throw new InvalidParameterError("Unknown padding passed to createCipher()"); - } - - return new BlockCipherWrapper(((m is null) ? c : m), p); - } -} diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/Hash.d --- a/dcrypt/crypto/Hash.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/Hash.d Sat Aug 30 14:38:23 2008 -0400 @@ -48,8 +48,10 @@ * * Params: * input_ = Data to be processed. + * + * Returns: Self */ - void update(void[] input_) { + Hash update(void[] input_) { ubyte[] input = cast(ubyte[]) input_; foreach (ubyte i; input) { bytes++; @@ -59,7 +61,7 @@ index = 0; } } - + return this; } /** Hash function's internal transformation. */ diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ManagedBlockCipher.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/ManagedBlockCipher.d Sat Aug 30 14:38:23 2008 -0400 @@ -0,0 +1,199 @@ +/** + * This file is part of the dcrypt project. + * + * Copyright: Copyright (C) dcrypt contributors 2008. All rights reserved. + * License: MIT + * Authors: Thomas Dixon + */ + +module dcrypt.crypto.ManagedBlockCipher; + +public import dcrypt.crypto.BlockCipher; +import dcrypt.crypto.BlockCipherPadding; + +/** + * Wraps a block cipher, enabling the encryption of a stream. + * Padding, if specified, is to be applied in the finish() call. + * + * Based on PaddedBufferedBlockCipher from BC. + */ +class ManagedBlockCipher : BlockCipher { + BlockCipher cipher; + BlockCipherPadding padding; + + protected { + ubyte[] buffer; + uint index; + bool encrypt, + streamMode = false; + } + + /** + * Create a managed block cipher. + * + * Params: + * cipher = Block cipher we're wrapping + * padding = Padding or null if no padding + * + * Returns: A new ManagedBlockCipher + */ + this(BlockCipher cipher, BlockCipherPadding padding=null) { + this.cipher = cipher; + + char[] mode = cipher.name; + int i; + for (i = 0; i < mode.length; i++) + if (mode[i] == '/') + break; + if (i < mode.length) { + mode = mode[i+1..i+4]; + this.streamMode = (mode == "CTR" || mode == "CFB" || mode == "OFB"); + } + + this.padding = padding; // null signifies no padding is to be applied + buffer = new ubyte[blockSize]; + } + + void init(bool encrypt, CipherParameters params) { + this.encrypt = encrypt; + cipher.init(encrypt, params); + } + + char[] name() { + if (padding is null) + return cipher.name; + return cipher.name~"/"~padding.name; + } + + uint blockSize() { + return cipher.blockSize; + } + + /** + * Update the cipher object with data from input_ and if it fills + * a block, place it in output. + * + * Returns: The number of bytes placed in output_. + */ + uint update(void[] input_, void[] output_) { + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; + + if (encrypt && input.length > output.length) + throw new ShortBufferError("Managed "~name()~": Output buffer too short"); + + uint result = 0, + len = input.length, + diff = buffer.length - index, + i = 0; + if (len > diff) { + buffer[index..buffer.length] = input[i..diff]; + result += cipher.update(buffer, output[i..i+blockSize]); + index = 0; + len -= diff; + i += blockSize; + + while (len > blockSize) { + result += cipher.update(input[i..i+blockSize], output[i..i+blockSize]); + len -= blockSize; + i += blockSize; + } + } + + buffer[0..len] = input[i..i+len]; + index += len; + + return result; + } + + /** + * Finalize the cipher, passing all remaining buffered input + * through the cipher (padding it first, if specified) and + * subsequently placing it in output_. + * + * Returns: The number of bytes placed in output_. + */ + uint finish(void[] output_) { + ubyte[] output = cast(ubyte[]) output_; + uint result = 0; + if (encrypt) { + if (index == blockSize) { + if (padding !is null && output.length < (blockSize << 1)) + throw new ShortBufferError("Managed "~name()~": Output buffer too short"); + result += cipher.update(buffer, output[result..result+blockSize]); + index = 0; + } + + if (padding !is null) { + uint diff = buffer.length - index; + buffer[index..buffer.length] = padding.pad(diff); + index += diff; + } + + if (index) + result += cipher.update(buffer[0..index], output[result..result+index]); + } else { + if (streamMode || index == blockSize) { + result += cipher.update(buffer[0..index], buffer[0..index]); + index = 0; + } else { + reset(); + throw new ShortBufferError( + "Managed "~name()~": Padded last block not equal to cipher's blocksize"); + } + try { + if (padding !is null) + result -= padding.unpad(buffer); + output[0..result] = buffer[0..result]; + } finally { + reset(); + } + } + reset(); + return result; + } + + /** + * Params: + * len = Number of bytes you plan on passing to update() + * + * Returns: The number of bytes to be output upon a call to update() + * with an input length of len bytes. + */ + uint updateOutputSize(uint len) { + uint result = len + index; + return result - (result % blockSize); + } + + /** + * Params: + * len = Number of bytes you plan on passing to update() + * + * Returns: The number of bytes to be output with a call to update() + * using an input of len bytes, followed by a call to finish(). + * This method takes into account padding, mode, etc. Will + * return 0 if your input is likely to error (i.e. len is 14 + * for AES in ECB mode). + */ + uint finishOutputSize(uint len) { + uint result = len + index, + diff = result % blockSize; + + // Input is a multiple of block size + if (!diff) + return result; + + // No padding, return result if stream mode, 0 if not (it'll error) + if (padding is null) + return (streamMode ? result : 0); + + // Padding, return len(input+padding) if encrypting or 0 if not (it'll error) + return (encrypt ? result - diff + blockSize : 0); + } + + void reset() { + cipher.reset(); + index = 0; + buffer[] = 0; + } +} diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/AES.d --- a/dcrypt/crypto/ciphers/AES.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/AES.d Sat Aug 30 14:38:23 2008 -0400 @@ -800,14 +800,18 @@ RS[cast(ubyte) t0]; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized."); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; - if (input.length < blockSize) + if (input.length < BLOCK_SIZE) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < BLOCK_SIZE) + throw new ShortBufferError(name()~": Output buffer too short"); s0 = w[0] ^ Util.ubytesToUintBig(input, 0); s1 = w[1] ^ Util.ubytesToUintBig(input, 4); @@ -816,13 +820,12 @@ if (encrypt) encryptBlock(); else decryptBlock(); - ubyte[] output = new ubyte[blockSize]; Util.uintToUbytesBig(s0, output, 0); Util.uintToUbytesBig(s1, output, 4); Util.uintToUbytesBig(s2, output, 8); Util.uintToUbytesBig(s3, output, 12); - return output; + return BLOCK_SIZE; } void reset() {} @@ -905,14 +908,14 @@ // Encryption t.init(true, key); - buffer = t.process(Util.hexToUbytes(test_plaintexts[i])); + t.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); - buffer = t.process(Util.hexToUbytes(test_ciphertexts[i])); + t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/Blowfish.d --- a/dcrypt/crypto/ciphers/Blowfish.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/Blowfish.d Sat Aug 30 14:38:23 2008 -0400 @@ -255,14 +255,18 @@ + S3[cast(ubyte)x]); } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized."); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; - if (input.length < blockSize) + if (input.length < BLOCK_SIZE) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < BLOCK_SIZE) + throw new ShortBufferError(name()~": Output buffer too short"); uint xl = Util.ubytesToUintBig(input, 0), xr = Util.ubytesToUintBig(input, 4), @@ -275,11 +279,10 @@ } xr ^= P[i]; - ubyte[] output = new ubyte[blockSize]; Util.uintToUbytesBig(xr, output, 0); Util.uintToUbytesBig(xl, output, 4); - return output; + return BLOCK_SIZE; } void reset() { @@ -301,31 +304,31 @@ ubyte[] t = new ubyte[BLOCK_SIZE]; // Initialized to 0's for (int i = 0; i < PBOX_SIZE;) { - t = process(t); + update(t, t); P[i++] = Util.ubytesToUintBig(t, 0); P[i++] = Util.ubytesToUintBig(t, 4); } for (int i = 0; i < SBOX_SIZE;) { - t = process(t); + update(t, t); S0[i++] = Util.ubytesToUintBig(t, 0); S0[i++] = Util.ubytesToUintBig(t, 4); } for (int i = 0; i < SBOX_SIZE;) { - t = process(t); + update(t, t); S1[i++] = Util.ubytesToUintBig(t, 0); S1[i++] = Util.ubytesToUintBig(t, 4); } for (int i = 0; i < SBOX_SIZE;) { - t = process(t); + update(t, t); S2[i++] = Util.ubytesToUintBig(t, 0); S2[i++] = Util.ubytesToUintBig(t, 4); } for (int i = 0; i < SBOX_SIZE;) { - t = process(t); + update(t, t); S3[i++] = Util.ubytesToUintBig(t, 0); S3[i++] = Util.ubytesToUintBig(t, 4); } @@ -375,14 +378,14 @@ // Encryption t.init(true, key); - buffer = t.process(Util.hexToUbytes(test_plaintexts[i])); + t.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); - buffer = t.process(Util.hexToUbytes(test_ciphertexts[i])); + t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/RC4.d --- a/dcrypt/crypto/ciphers/RC4.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/RC4.d Sat Aug 30 14:38:23 2008 -0400 @@ -49,12 +49,15 @@ return (input^state[cast(ubyte)(state[x]+state[y])]); } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized"); ubyte[] input = cast(ubyte[]) input_, - output = new ubyte[input.length]; + 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++) { y += state[++x]; @@ -63,7 +66,7 @@ state[y] = t; output[i] = input[i] ^ state[cast(ubyte)(state[x]+state[y])]; } - return output; + return input.length; } void reset() { @@ -182,7 +185,7 @@ r.init(true, new SymmetricKey(Util.hexToUbytes(test_key))); // Encryption - buffer = r.process(Util.hexToUbytes(test_plaintexts[i])); + r.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], r.name~": ("~result~") != ("~test_ciphertexts[i]~")"); @@ -190,7 +193,7 @@ r.reset(); // Decryption - buffer = r.process(Util.hexToUbytes(test_ciphertexts[i])); + r.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], r.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/RC6.d --- a/dcrypt/crypto/ciphers/RC6.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/RC6.d Sat Aug 30 14:38:23 2008 -0400 @@ -65,14 +65,18 @@ initialized = true; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized"); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; - if (input.length > blockSize) + if (input.length < BLOCK_SIZE) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < BLOCK_SIZE) + throw new ShortBufferError(name()~": Output buffer too short"); uint A = Util.ubytesToUintLittle(input, 0), B = Util.ubytesToUintLittle(input, 4), @@ -115,13 +119,12 @@ B -= S[0]; } - ubyte[] output = new ubyte[blockSize]; Util.uintToUbytesLittle(A, output, 0); Util.uintToUbytesLittle(B, output, 4); Util.uintToUbytesLittle(C, output, 8); Util.uintToUbytesLittle(D, output, 12); - return output; + return BLOCK_SIZE; } void reset() { @@ -192,14 +195,14 @@ // Encryption t.init(true, key); - buffer = t.process(Util.hexToUbytes(test_plaintexts[i])); + t.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); - buffer = t.process(Util.hexToUbytes(test_ciphertexts[i])); + t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/TEA.d --- a/dcrypt/crypto/ciphers/TEA.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/TEA.d Sat Aug 30 14:38:23 2008 -0400 @@ -54,14 +54,18 @@ initialized = true; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized"); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; - if (input.length < blockSize) + if (input.length < BLOCK_SIZE) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < BLOCK_SIZE) + throw new ShortBufferError(name()~": Output buffer too short"); uint v0 = Util.ubytesToUintBig(input, 0), v1 = Util.ubytesToUintBig(input, 4); @@ -79,11 +83,10 @@ } } - ubyte[] output = new ubyte[blockSize]; Util.uintToUbytesBig(v0, output, 0); Util.uintToUbytesBig(v1, output, 4); - return output; + return BLOCK_SIZE; } /** Some TEA test vectors. */ @@ -118,14 +121,14 @@ // Encryption t.init(true, key); - buffer = t.process(Util.hexToUbytes(test_plaintexts[i])); + t.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); - buffer = t.process(Util.hexToUbytes(test_ciphertexts[i])); + t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/ciphers/XTEA.d --- a/dcrypt/crypto/ciphers/XTEA.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/ciphers/XTEA.d Sat Aug 30 14:38:23 2008 -0400 @@ -64,14 +64,18 @@ initialized = true; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError(name()~": Cipher not initialized"); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; - if (input.length < blockSize) + if (input.length < BLOCK_SIZE) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < BLOCK_SIZE) + throw new ShortBufferError(name()~": Output buffer too short"); uint v0 = Util.ubytesToUintBig(input, 0), v1 = Util.ubytesToUintBig(input, 4); @@ -88,11 +92,10 @@ } } - ubyte[] output = new ubyte[blockSize]; Util.uintToUbytesBig(v0, output, 0); Util.uintToUbytesBig(v1, output, 4); - return output; + return BLOCK_SIZE; } /** Some XTEA test vectors. */ @@ -145,14 +148,14 @@ // Encryption t.init(true, key); - buffer = t.process(Util.hexToUbytes(test_plaintexts[i])); + t.update(Util.hexToUbytes(test_plaintexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], t.name~": ("~result~") != ("~test_ciphertexts[i]~")"); // Decryption t.init(false, key); - buffer = t.process(Util.hexToUbytes(test_ciphertexts[i])); + t.update(Util.hexToUbytes(test_ciphertexts[i]), buffer); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], t.name~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/modes/CBC.d --- a/dcrypt/crypto/modes/CBC.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/modes/CBC.d Sat Aug 30 14:38:23 2008 -0400 @@ -9,7 +9,7 @@ module dcrypt.crypto.modes.CBC; import dcrypt.crypto.BlockCipher; -import dcrypt.crypto.params.ParametersWithIV; +public import dcrypt.crypto.params.ParametersWithIV; version (UnitTest) { import dcrypt.crypto.ciphers.XTEA; @@ -67,15 +67,19 @@ initialized = true; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError( name()~": Block mode not initialized"); - ubyte[] input = cast(ubyte[]) input_; - + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; + if (input.length < blockSize) throw new ShortBufferError(name()~": Input buffer too short"); + + if (output.length < blockSize) + throw new ShortBufferError(name()~": Output buffer too short"); if (encrypt) { // P_i XOR C_i-1 @@ -83,27 +87,29 @@ previousCiphertext[i] ^= input[i]; // E_k(P_i XOR C_i-1) - cbcOutput[] = m_cipher.process(previousCiphertext); + m_cipher.update(previousCiphertext, cbcOutput); // Store C_i for next block previousCiphertext[] = cbcOutput; + // C_i = E_k(P_i XOR C_i-1) + output[0..blockSize] = cbcOutput; } else { // Temporarily store C_i ubyte[] t = input[0..blockSize]; // D_k(C_i) - cbcOutput[] = m_cipher.process(t); + m_cipher.update(t, cbcOutput); // P_i = D_k(C_i) XOR C_i-1 for (int i = 0; i < blockSize; i++) - cbcOutput[i] ^= previousCiphertext[i]; + output[i] = (cbcOutput[i] ^ previousCiphertext[i]); // Store C_i for next block previousCiphertext[] = t; } - return cbcOutput; + return blockSize; } uint blockSize() { @@ -145,7 +151,7 @@ "c09d3c606614d84b8d184fa29c5cb5f6"~ "f26fa5a0b6b63ba0f7ebf2f8735f85e3" ]; - + XTEA x = new XTEA(); CBC c = new CBC(x); ubyte[] iv = new ubyte[x.blockSize], // Initialized to 0 @@ -158,7 +164,7 @@ // Encryption c.init(true, params); for (int j = 0; j < 32; j+=x.blockSize) - buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_plaintexts[i])[j..j+x.blockSize]); + c.update(Util.hexToUbytes(test_plaintexts[i])[j..j+x.blockSize], buffer[j..j+x.blockSize]); result = Util.ubytesToHex(buffer); assert(result == test_ciphertexts[i], c.name()~": ("~result~") != ("~test_ciphertexts[i]~")"); @@ -166,7 +172,7 @@ // Decryption c.init(false, params); for (int j = 0; j < 32; j+=x.blockSize) - buffer[j..j+x.blockSize] = c.process(Util.hexToUbytes(test_ciphertexts[i])[j..j+x.blockSize]); + c.update(Util.hexToUbytes(test_ciphertexts[i])[j..j+x.blockSize], buffer[j..j+x.blockSize]); result = Util.ubytesToHex(buffer); assert(result == test_plaintexts[i], c.name()~": ("~result~") != ("~test_plaintexts[i]~")"); diff -r 02970e63257d -r 8c7f8fecdd75 dcrypt/crypto/modes/CTR.d --- a/dcrypt/crypto/modes/CTR.d Wed Aug 20 23:33:02 2008 -0400 +++ b/dcrypt/crypto/modes/CTR.d Sat Aug 30 14:38:23 2008 -0400 @@ -9,7 +9,7 @@ module dcrypt.crypto.modes.CTR; import dcrypt.crypto.BlockCipher; -import dcrypt.crypto.params.ParametersWithIV; +public import dcrypt.crypto.params.ParametersWithIV; /** This class implements the counter (CTR/SIC/ICM) block mode, @@ -62,16 +62,21 @@ initialized = true; } - ubyte[] process(void[] input_) { + uint update(void[] input_, void[] output_) { if (!initialized) throw new NotInitializedError( name()~": Block mode not initialized"); - ubyte[] input = cast(ubyte[]) input_; + ubyte[] input = cast(ubyte[]) input_, + output = cast(ubyte[]) output_; + uint len = (counter.length > input.length) ? input.length : counter.length; + if (len > output.length) + throw new ShortBufferError(name()~": Output buffer too short"); + // Encrypt the counter - counterOutput[] = m_cipher.process(counter); + m_cipher.update(counter, counterOutput); // XOR output with plaintext to create ciphertext for (int i = 0; i < len; i++) @@ -81,7 +86,9 @@ for (int i = counter.length-1; i >= 0; i--) if (++counter[i]) break; - return counterOutput[0..len]; + output[0..len] = counterOutput[0..len]; + + return len; } uint blockSize() {