Mercurial > projects > dcrypt
diff dcrypt/crypto/ManagedBlockCipher.d @ 12:8c7f8fecdd75
Added ManagedBlockCipher, changed Crypto to just import everything, made Hash.update() return itself (for chaining) and ditched BlockCipherWrapper.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Sat, 30 Aug 2008 14:38:23 -0400 |
parents | dcrypt/crypto/BlockCipherWrapper.d@cd376996cdb3 |
children | 7ea528b61802 |
line wrap: on
line diff
--- /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; + } +}