# HG changeset patch # User Thomas Dixon # Date 1218692724 14400 # Node ID 3de3a2de13a0e73f21b8c9cc0713b8ae45a36df9 # Parent a5789a7b3b3b505aa6393d7a3dd7bfd2be3b1015 Added MAC base class and HMAC. Added StreamCipherWrapper as part of the work on the high-level cipher API. Running on fumes, so hopefully there isn't too much stupid mixed into the code. diff -r a5789a7b3b3b -r 3de3a2de13a0 dcrypt/crypto/MAC.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/MAC.d Thu Aug 14 01:45:24 2008 -0400 @@ -0,0 +1,52 @@ +/** + * 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.MAC; + +public import dcrypt.crypto.params.CipherParameters; +public import dcrypt.crypto.errors.InvalidParameterError; +import dcrypt.misc.Util; + +/** Base MAC class */ +abstract class MAC { + /** + * Initialize a MAC. + * + * Params: + * params = Parameters to be passed to the MAC. (Key, etc.) + */ + void init(CipherParameters params); + + /** + * Introduce data into the MAC. + * + * Params: + * input_ = Data to be processed. + */ + void update(void[] input_); + + /** Returns: The name of this MAC. */ + char[] name(); + + /** Reset MAC to its state immediately subsequent the last init. */ + void reset(); + + /** Returns: The block size in bytes that this MAC will operate on. */ + uint blockSize(); + + /** Returns: The output size of the MAC in bytes. */ + uint macSize(); + + /** Returns: The computed MAC. */ + ubyte[] finish(); + + /** Returns: The computed MAC in hexadecimal. */ + char[] hexFinish() { + return Util.ubytesToHex(finish()); + } +} \ No newline at end of file diff -r a5789a7b3b3b -r 3de3a2de13a0 dcrypt/crypto/StreamCipherWrapper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/StreamCipherWrapper.d Thu Aug 14 01:45:24 2008 -0400 @@ -0,0 +1,44 @@ +/** + * 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.StreamCipherWrapper; + +import dcrypt.crypto.SymmetricCipher; +import dcrypt.crypto.StreamCipher; +public import dcrypt.crypto.params.CipherParameters; + +/** High-level API for stream ciphers. */ +class StreamCipherWrapper : SymmetricCipher { + private StreamCipher cipher; + + this(StreamCipher sc) { + cipher = sc; + } + + void init(bool encrypt, CipherParameters params) { + cipher.init(encrypt, params); + } + + uint update(void[] input_, uint inOff, + uint len, void[] output_, uint outOff) { + cipher.processBytes(input_, inOff, len, output_, outOff); + return len; + } + + uint finish(void[] output_, uint outOff) { + return 0; + } + + void reset() { + cipher.reset(); + } + + char[] name() { + return cipher.name; + } +} diff -r a5789a7b3b3b -r 3de3a2de13a0 dcrypt/crypto/macs/HMAC.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dcrypt/crypto/macs/HMAC.d Thu Aug 14 01:45:24 2008 -0400 @@ -0,0 +1,155 @@ +/** + * 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.macs.HMAC; + +import dcrypt.crypto.MAC; +import dcrypt.crypto.Hash; +import dcrypt.crypto.params.SymmetricKey; +import dcrypt.crypto.errors.NotInitializedError; + +version (UnitTest) { + import dcrypt.crypto.hashes.SHA1; +} + +/** Conforms: RFC2104 */ +class HMAC : MAC { + private { + ubyte[] ipad, opad, key; + Hash inner, outer; + bool initialized; + } + + this (Hash hash, void[] key=null) { + hash.reset(); + + inner = hash; + outer = hash.copy(); + + ipad = new ubyte[blockSize]; + opad = new ubyte[blockSize]; + + reset(); + + if (key) + init(new SymmetricKey(key)); // I'm lazy. + } + + void init(CipherParameters params) { + SymmetricKey keyParams = cast(SymmetricKey)params; + if (!keyParams) + throw new InvalidParameterError( + name()~": Invalid parameter passed to init"); + + if (keyParams.key.length > blockSize) { + inner.update(keyParams.key); + key = inner.digest(); + } else + key = keyParams.key; + + foreach (uint i, ubyte j; key) { + ipad[i] ^= j; + opad[i] ^= j; + } + + inner.update(ipad); + outer.update(opad); + + initialized = true; + } + + void update(void[] input_) { + if (!initialized) + throw new NotInitializedError( + name()~": MAC not initialized."); + inner.update(input_); + } + + char[] name() { + return inner.name~"/HMAC"; + } + + void reset() { + ipad[] = 0x36; + opad[] = 0x5c; + + inner.reset(); + outer.reset(); + } + + uint blockSize() { + return inner.blockSize; + } + + uint macSize() { + return inner.digestSize; + } + + ubyte[] finish() { + outer.update(inner.digest()); + ubyte[] r = outer.digest(); + reset(); + return r; + } + + char[] hexFinish() { + return Util.ubytesToHex(finish()); + } + + HMAC copy() { + // Ghetto... oh so ghetto :\ + HMAC h = new HMAC(inner.copy()); + h.inner = inner.copy(); + h.outer = outer.copy(); + h.initialized = true; + return h; + } + + version (UnitTest) { + unittest { + static char[][] test_keys = [ + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "4a656665", // Jefe? + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~ + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ]; + + static char[][] test_inputs = [ + "4869205468657265", + "7768617420646f2079612077616e7420666f72206e6f7468696e673f", + "dd", + "54657374205573696e67204c6172676572205468616e20426c6f63"~ + "6b2d53697a65204b6579202d2048617368204b6579204669727374" + ]; + + static const int[] test_repeat = [ + 1, 1, 50, 1 + ]; + + static const char[][] test_results = [ + "b617318655057264e28bc0b6fb378c8ef146be00", + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", + "125d7342b9ac11cd91a39af48aa17b4f63f175d3", + "aa4ae5e15272d00e95705637ce8a3b55ed402112" + ]; + + HMAC h = new HMAC(new SHA1()); + foreach (uint i, char[] k; test_keys) { + h.init(new SymmetricKey(Util.hexToUbytes(k))); + for (int j = 0; j < test_repeat[i]; j++) + h.update(Util.hexToUbytes(test_inputs[i])); + char[] mac = h.hexFinish(); + assert(mac == test_results[i], + h.name~": ("~mac~") != ("~test_results[i]~")"); + } + } + } +} \ No newline at end of file