view dcrypt/crypto/macs/HMAC.d @ 30:21847420b1ac

Changed Hash's update method to a more optimized variant. Changed the code style for the entire misc package (completely forgot about it). Further changes for D2 compatibility. It appears as if full compatibility won't be possibledue to D2's handling of void[], but the number of changes to obtain compatibility can be minimized in the least.
author Thomas Dixon <reikon@reikon.us>
date Mon, 11 May 2009 15:32:00 -0400
parents b9ba770b8f16
children b9f8aa42a547
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;

debug (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 hash;
        bool initialized;
    }
    
    this (Hash hash, void[] key=null)
    {
        this.hash = hash.copy();
        this.hash.reset();
        
        ipad = new ubyte[blockSize];
        opad = new ubyte[blockSize];
        
        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");
        
        hash.reset();
        
        if (keyParams.key.length > blockSize)
        {
            hash.update(keyParams.key);
            key = hash.digest();
        } else
            key = keyParams.key;
        
        ipad[] = 0x36;
        opad[] = 0x5c;
        
        foreach (uint i, ubyte j; key)
        {
            ipad[i] ^= j;
            opad[i] ^= j;
        }
        
        reset();
        
        initialized = true;
    }
    
    void update(void[] input_)
    {
        if (!initialized)
            throw new NotInitializedError(name()~": MAC not initialized.");
            
        hash.update(input_);
    }
    
    string name()
    {
        return "HMAC-"~hash.name;
    }
    
    void reset()
    {    
        hash.reset();
        hash.update(ipad);
    }
    
    uint blockSize()
    {
        return hash.blockSize;
    }
    
    uint macSize()
    {
        return hash.digestSize;
    }
    
    ubyte[] digest()
    {
        ubyte[] t = hash.digest();
        hash.update(opad);
        hash.update(t);
        ubyte[] r = hash.digest();
        
        reset();
        
        return r;
    }
    
    char[] hexDigest()
    {
        return ByteConverter.hexEncode(digest());
    }
    
    HMAC copy()
    {
        // Ghetto... oh so ghetto :\
        HMAC h = new HMAC(hash.copy());
        h.hash = hash.copy();
        h.initialized = true;
        return h;
    }
    
    debug (UnitTest)
    {
        unittest
        {
            static string[] test_keys = [
                "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
                "4a656665", // Jefe?
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"~
                "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
            ];
            
            static string[] test_inputs = [
                "4869205468657265",
                "7768617420646f2079612077616e7420666f72206e6f7468696e673f",
                "dd",
                "54657374205573696e67204c6172676572205468616e20426c6f63"~
                "6b2d53697a65204b6579202d2048617368204b6579204669727374"
            ];
            
            static int[] test_repeat = [
                1, 1, 50, 1
            ];
            
            static string[] test_results = [
                "b617318655057264e28bc0b6fb378c8ef146be00",
                "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79",
                "125d7342b9ac11cd91a39af48aa17b4f63f175d3",
                "aa4ae5e15272d00e95705637ce8a3b55ed402112"
            ];
            
            HMAC h = new HMAC(new SHA1());
            foreach (uint i, string k; test_keys)
            {
                h.init(new SymmetricKey(ByteConverter.hexDecode(k)));
                for (int j = 0; j < test_repeat[i]; j++)
                    h.update(ByteConverter.hexDecode(test_inputs[i]));
                string mac = h.hexDigest();
                assert(mac == test_results[i], 
                        h.name~": ("~mac~") != ("~test_results[i]~")");
            }
        }
    }
}