115 lines
4.1 KiB
C#
115 lines
4.1 KiB
C#
|
//------------------------------------------------------------
|
||
|
// 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++];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|