File: System\IdentityModel\Psha1DerivedKeyGenerator.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.IdentityModel
{
    using System;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.ServiceModel.Diagnostics;
 
    sealed class Psha1DerivedKeyGenerator
    {
        byte[] key;
 
        public Psha1DerivedKeyGenerator(byte[] key)
        {
            if (key == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("key");
            }
            this.key = key;
        }
 
        public byte[] GenerateDerivedKey(byte[] label, byte[] nonce, int derivedKeySize, int position)
        {
            if (label == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("label");
            }
            if (nonce == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("nonce");
            }
            ManagedPsha1 dkcp = new ManagedPsha1(key, label, nonce);
            return dkcp.GetDerivedKey(derivedKeySize, position);
        }
 
        // private class to do the real work
        // Note: Though named ManagedPsha1, this works for both fips and non-fips compliance
        sealed class ManagedPsha1
        {
            byte[] aValue;
            byte[] buffer;
            byte[] chunk;
            KeyedHashAlgorithm hmac;
            int index;
            int position;
            byte[] secret;
            byte[] seed;
 
            // assume arguments are already validated
            public ManagedPsha1(byte[] secret, byte[] label, byte[] seed)
            {
                this.secret = secret;
                this.seed = DiagnosticUtility.Utility.AllocateByteArray(checked(label.Length + seed.Length));
                label.CopyTo(this.seed, 0);
                seed.CopyTo(this.seed, label.Length);
 
                this.aValue = this.seed;
                this.chunk = new byte[0];
                this.index = 0;
                this.position = 0;
                this.hmac = CryptoHelper.NewHmacSha1KeyedHashAlgorithm(secret);
 
                this.buffer = DiagnosticUtility.Utility.AllocateByteArray(checked(this.hmac.HashSize / 8 + this.seed.Length));
            }
 
            public byte[] GetDerivedKey(int derivedKeySize, int position)
            {
                if (derivedKeySize < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("derivedKeySize", SR.GetString(SR.ValueMustBeNonNegative)));
                }
                if (this.position > position)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("position", SR.GetString(SR.ValueMustBeInRange, 0, this.position)));
                }
 
                // Seek to the desired position in the pseudo-random stream.
                while (this.position < position)
                {
                    GetByte();
                }
                int sizeInBytes = derivedKeySize / 8;
                byte[] derivedKey = new byte[sizeInBytes];
                for (int i = 0; i < sizeInBytes; i++)
                {
                    derivedKey[i] = GetByte();
                }
                return derivedKey;
            }
 
            byte GetByte()
            {
                if (index >= chunk.Length)
                {
                    // Calculate A(i) = HMAC_SHA1(secret, A(i-1)).
                    hmac.Initialize();
                    this.aValue = hmac.ComputeHash(this.aValue);
                    // Calculate P_SHA1(secret, seed)[j] = HMAC_SHA1(secret, A(j+1) || seed).
                    this.aValue.CopyTo(buffer, 0);
                    this.seed.CopyTo(buffer, this.aValue.Length);
                    hmac.Initialize();
                    this.chunk = hmac.ComputeHash(buffer);
                    index = 0;
                }
                position++;
                return chunk[index++];
            }
        }
    }
}