Mercurial > projects > dcrypt
view dcrypt/crypto/ManagedBlockCipher.d @ 28:ad687db713a4
Further reworked the code for hash padding. Replaced all instances of 'char[]' with 'string' and removed a few 'const' modifiers as per Glenn Haecker's patch for D2 compatibility. Updated CONTRIBUTORS file.
author | Thomas Dixon <reikon@reikon.us> |
---|---|
date | Sun, 10 May 2009 22:38:48 -0400 |
parents | 8b5eaf3c2979 |
children |
line wrap: on
line source
/** * 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; string 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); } string 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 // decrypt { 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 ((padding is null) ? result : result+blockSize); // 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; } }