162 lines
6.0 KiB
C#
162 lines
6.0 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Security
|
||
|
{
|
||
|
using System.Collections;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Runtime;
|
||
|
|
||
|
/// <summary>
|
||
|
/// This is the in-memory nonce-cache used for turnkey replay detection.
|
||
|
/// The nonce cache is based on a hashtable implementation for fast lookups.
|
||
|
/// The hashcode is computed based on the nonce byte array.
|
||
|
/// The nonce cache periodically purges stale nonce entries.
|
||
|
/// </summary>
|
||
|
sealed class InMemoryNonceCache : NonceCache
|
||
|
{
|
||
|
NonceCacheImpl cacheImpl;
|
||
|
|
||
|
public InMemoryNonceCache(TimeSpan cachingTime, int maxCachedNonces)
|
||
|
{
|
||
|
this.CacheSize = maxCachedNonces;
|
||
|
this.CachingTimeSpan = cachingTime;
|
||
|
this.cacheImpl = new NonceCacheImpl(cachingTime, maxCachedNonces);
|
||
|
}
|
||
|
|
||
|
public override bool CheckNonce(byte[] nonce)
|
||
|
{
|
||
|
return this.cacheImpl.CheckNonce(nonce);
|
||
|
}
|
||
|
|
||
|
public override bool TryAddNonce(byte[] nonce)
|
||
|
{
|
||
|
return this.cacheImpl.TryAddNonce(nonce);
|
||
|
}
|
||
|
|
||
|
public override string ToString()
|
||
|
{
|
||
|
StringWriter writer = new StringWriter(CultureInfo.InvariantCulture);
|
||
|
writer.WriteLine("NonceCache:");
|
||
|
writer.WriteLine(" Caching Timespan: {0}", this.CachingTimeSpan);
|
||
|
writer.WriteLine(" Capacity: {0}", this.CacheSize);
|
||
|
return writer.ToString();
|
||
|
}
|
||
|
|
||
|
internal sealed class NonceCacheImpl : TimeBoundedCache
|
||
|
{
|
||
|
static NonceKeyComparer comparer = new NonceKeyComparer();
|
||
|
static object dummyItem = new Object();
|
||
|
// if there are less than lowWaterMark entries, no purging is done
|
||
|
static int lowWaterMark = 50;
|
||
|
// We created a key for the nonce using the first 4 bytes, and hence the minimum length of nonce
|
||
|
// that can be added to the cache.
|
||
|
static int minimumNonceLength = 4;
|
||
|
TimeSpan cachingTimeSpan;
|
||
|
|
||
|
public NonceCacheImpl(TimeSpan cachingTimeSpan, int maxCachedNonces)
|
||
|
: base(lowWaterMark, maxCachedNonces, comparer, PurgingMode.AccessBasedPurge, TimeSpan.FromTicks(cachingTimeSpan.Ticks >> 2), false)
|
||
|
{
|
||
|
this.cachingTimeSpan = cachingTimeSpan;
|
||
|
}
|
||
|
|
||
|
public bool TryAddNonce(byte[] nonce)
|
||
|
{
|
||
|
if (nonce == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("nonce");
|
||
|
if (nonce.Length < minimumNonceLength)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.NonceLengthTooShort));
|
||
|
DateTime expirationTime = TimeoutHelper.Add(DateTime.UtcNow, this.cachingTimeSpan);
|
||
|
return base.TryAddItem(nonce, dummyItem, expirationTime, false);
|
||
|
}
|
||
|
|
||
|
public bool CheckNonce(byte[] nonce)
|
||
|
{
|
||
|
if (nonce == null)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("nonce");
|
||
|
if (nonce.Length < minimumNonceLength)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.NonceLengthTooShort));
|
||
|
if (base.GetItem(nonce) != null)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// This class provides the hash-code value for the key (nonce) of the nonce cache.
|
||
|
/// The hash code is obtained from the nonce byte array by making an int of
|
||
|
/// the first 4 bytes
|
||
|
/// </summary>
|
||
|
internal sealed class NonceKeyComparer : IEqualityComparer, System.Collections.Generic.IEqualityComparer<byte[]>
|
||
|
{
|
||
|
public int GetHashCode(object o)
|
||
|
{
|
||
|
return GetHashCode((byte[])o);
|
||
|
}
|
||
|
public int GetHashCode(byte[] o)
|
||
|
{
|
||
|
byte[] nonce = (byte[])o;
|
||
|
|
||
|
return (((int)nonce[0]) | (((int)nonce[1]) << 8) | (((int)nonce[2]) << 16) | (((int)nonce[3]) << 24));
|
||
|
}
|
||
|
|
||
|
public int Compare(object x, object y)
|
||
|
{
|
||
|
return Compare((byte[])x, (byte[])y);
|
||
|
}
|
||
|
|
||
|
public int Compare(byte[] x, byte[] y)
|
||
|
{
|
||
|
if (Object.ReferenceEquals(x, y))
|
||
|
return 0;
|
||
|
|
||
|
if (x == null)
|
||
|
return -1;
|
||
|
else if (y == null)
|
||
|
return 1;
|
||
|
|
||
|
byte[] nonce1 = (byte[])x;
|
||
|
int length1 = nonce1.Length;
|
||
|
byte[] nonce2 = (byte[])y;
|
||
|
int length2 = nonce2.Length;
|
||
|
|
||
|
if (length1 == length2)
|
||
|
{
|
||
|
for (int i = 0; i < length1; ++i)
|
||
|
{
|
||
|
int diff = ((int)nonce1[i] - (int)nonce2[i]);
|
||
|
|
||
|
if (diff != 0)
|
||
|
{
|
||
|
return diff;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
else if (length1 > length2)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public new bool Equals(object x, object y)
|
||
|
{
|
||
|
return (Compare(x, y) == 0);
|
||
|
}
|
||
|
|
||
|
public bool Equals(byte[] x, byte[] y)
|
||
|
{
|
||
|
return (Compare(x, y) == 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|