/* * Mentalis.org Security Library * * Copyright © 2002-2005, The KPD-Team * All rights reserved. * http://www.mentalis.org/ * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Neither the name of the KPD-Team, nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Security.Cryptography; using System.Runtime.InteropServices; using Org.Mentalis.Security; namespace Org.Mentalis.Security.Cryptography { // http://www.ietf.org/rfc/rfc2104.txt /// /// Implements the HMAC keyed message authentication code algorithm. /// public sealed class HMAC : KeyedHashAlgorithm { /// /// Initializes a new instance of the class. This class cannot be inherited. /// /// The underlying hash algorithm to use. /// A random key will be generated and used by the HMAC. /// is a null reference (Nothing in Visual Basic). public HMAC(HashAlgorithm hash) : this(hash, null) {} /// /// Initializes a new instance of the class. /// /// The underlying hash algorithm to use. /// The key to use for the HMAC -or- a null reference (Nothing in Visual Basic). /// If is a null reference, the HMAC class will generate a random key. /// is a null reference (Nothing in Visual Basic). public HMAC(HashAlgorithm hash, byte[] rgbKey) { if (hash == null) throw new ArgumentNullException(); if (rgbKey == null) { rgbKey = new byte[hash.HashSize / 8]; new RNGCryptoServiceProvider().GetBytes(rgbKey); } m_HashAlgorithm = hash; this.Key = (byte[])rgbKey.Clone(); m_IsDisposed = false; m_KeyBuffer = new byte[64]; m_Padded = new byte[64]; Initialize(); } /// /// Initializes the HMAC. /// /// The HMAC instance has been disposed. public override void Initialize() { if (m_IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); m_HashAlgorithm.Initialize(); m_IsHashing = false; this.State = 0; Array.Clear(m_KeyBuffer, 0, m_KeyBuffer.Length); } /// /// Routes data written to the object into the hash algorithm for computing the hash. /// /// The input for which to compute the hash code. /// The offset into the byte array from which to begin using data. /// The number of bytes in the byte array to use as data. /// The HMAC instance has been disposed. protected override void HashCore(byte[] rgb, int ib, int cb) { if (m_IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); if (!m_IsHashing) { byte[] key; if (this.Key.Length > 64) key = m_HashAlgorithm.ComputeHash(this.Key); else key = this.Key; Array.Copy(key, 0, m_KeyBuffer, 0, key.Length); for(int i = 0; i < 64; i++) m_Padded[i] = (byte)(m_KeyBuffer[i] ^ 0x36); m_HashAlgorithm.TransformBlock(m_Padded, 0, m_Padded.Length, m_Padded, 0); m_IsHashing = true; } m_HashAlgorithm.TransformBlock(rgb, ib, cb, rgb, ib); } /// /// Finalizes the hash computation after the last data is processed by the cryptographic stream object. /// /// The computed hash code. /// The HMAC instance has been disposed. protected override byte[] HashFinal() { if (m_IsDisposed) throw new ObjectDisposedException(this.GetType().FullName); m_HashAlgorithm.TransformFinalBlock(new byte[0], 0, 0); byte[] dataHash = m_HashAlgorithm.Hash; for(int i = 0; i < 64; i++) m_Padded[i] = (byte)(m_KeyBuffer[i] ^ 0x5C); m_HashAlgorithm.Initialize(); m_HashAlgorithm.TransformBlock(m_Padded, 0, m_Padded.Length, m_Padded, 0); m_HashAlgorithm.TransformFinalBlock(dataHash, 0, dataHash.Length); dataHash = m_HashAlgorithm.Hash; Array.Clear(m_KeyBuffer, 0, m_KeyBuffer.Length); m_IsHashing = false; return dataHash; } /// /// Gets the size of the computed hash code in bits. /// /// The size of the computed hash code in bits. public override int HashSize { get { return m_HashAlgorithm.HashSize; } } /// /// Releases the resources used by the HMAC. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { m_IsDisposed = true; base.Dispose(true); m_HashAlgorithm.Clear(); try { GC.SuppressFinalize(this); } catch {} } /// /// Finalizes the HMAC. /// ~HMAC() { m_HashAlgorithm.Clear(); } /// /// Holds the internal hash algorithm /// private HashAlgorithm m_HashAlgorithm; /// /// Holds the key buffer. /// private byte[] m_KeyBuffer; /// /// true if a hash operation is in prograss, false otherwise. /// private bool m_IsHashing; /// /// true if the object has been disposed, false otherwise. /// private bool m_IsDisposed; private byte[] m_Padded; } }