view dcrypt/crypto/macs/HMAC.d @ 6:5cb17e09d685

Minor edits to the unittests of hash functions and ciphers. Added AES and test vectors.
author Thomas Dixon <reikon@reikon.us>
date Sat, 16 Aug 2008 22:43:22 -0400
parents 3de3a2de13a0
children 5ce3012f1def
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.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;
}

/** 
 * Implementation of Keyed-Hash Message Authentication Code (HMAC)
 * 
 * Conforms: RFC 2104 
 * References: http://www.faqs.org/rfcs/rfc2104.html
 */
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]~")");
            }
        }
    }
}