1 //----------------------------------------------------------------------------- 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //----------------------------------------------------------------------------- 4 5 namespace System.ServiceModel.Security 6 { 7 using System.Collections; 8 using System.Globalization; 9 using System.IO; 10 using System.Runtime; 11 12 /// <summary> 13 /// This is the in-memory nonce-cache used for turnkey replay detection. 14 /// The nonce cache is based on a hashtable implementation for fast lookups. 15 /// The hashcode is computed based on the nonce byte array. 16 /// The nonce cache periodically purges stale nonce entries. 17 /// </summary> 18 sealed class InMemoryNonceCache : NonceCache 19 { 20 NonceCacheImpl cacheImpl; 21 InMemoryNonceCache(TimeSpan cachingTime, int maxCachedNonces)22 public InMemoryNonceCache(TimeSpan cachingTime, int maxCachedNonces) 23 { 24 this.CacheSize = maxCachedNonces; 25 this.CachingTimeSpan = cachingTime; 26 this.cacheImpl = new NonceCacheImpl(cachingTime, maxCachedNonces); 27 } 28 CheckNonce(byte[] nonce)29 public override bool CheckNonce(byte[] nonce) 30 { 31 return this.cacheImpl.CheckNonce(nonce); 32 } 33 TryAddNonce(byte[] nonce)34 public override bool TryAddNonce(byte[] nonce) 35 { 36 return this.cacheImpl.TryAddNonce(nonce); 37 } 38 ToString()39 public override string ToString() 40 { 41 StringWriter writer = new StringWriter(CultureInfo.InvariantCulture); 42 writer.WriteLine("NonceCache:"); 43 writer.WriteLine(" Caching Timespan: {0}", this.CachingTimeSpan); 44 writer.WriteLine(" Capacity: {0}", this.CacheSize); 45 return writer.ToString(); 46 } 47 48 internal sealed class NonceCacheImpl : TimeBoundedCache 49 { 50 static NonceKeyComparer comparer = new NonceKeyComparer(); 51 static object dummyItem = new Object(); 52 // if there are less than lowWaterMark entries, no purging is done 53 static int lowWaterMark = 50; 54 // We created a key for the nonce using the first 4 bytes, and hence the minimum length of nonce 55 // that can be added to the cache. 56 static int minimumNonceLength = 4; 57 TimeSpan cachingTimeSpan; 58 NonceCacheImpl(TimeSpan cachingTimeSpan, int maxCachedNonces)59 public NonceCacheImpl(TimeSpan cachingTimeSpan, int maxCachedNonces) 60 : base(lowWaterMark, maxCachedNonces, comparer, PurgingMode.AccessBasedPurge, TimeSpan.FromTicks(cachingTimeSpan.Ticks >> 2), false) 61 { 62 this.cachingTimeSpan = cachingTimeSpan; 63 } 64 TryAddNonce(byte[] nonce)65 public bool TryAddNonce(byte[] nonce) 66 { 67 if (nonce == null) 68 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("nonce"); 69 if (nonce.Length < minimumNonceLength) 70 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.NonceLengthTooShort)); 71 DateTime expirationTime = TimeoutHelper.Add(DateTime.UtcNow, this.cachingTimeSpan); 72 return base.TryAddItem(nonce, dummyItem, expirationTime, false); 73 } 74 CheckNonce(byte[] nonce)75 public bool CheckNonce(byte[] nonce) 76 { 77 if (nonce == null) 78 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("nonce"); 79 if (nonce.Length < minimumNonceLength) 80 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.NonceLengthTooShort)); 81 if (base.GetItem(nonce) != null) 82 return true; 83 else 84 return false; 85 } 86 87 /// <summary> 88 /// This class provides the hash-code value for the key (nonce) of the nonce cache. 89 /// The hash code is obtained from the nonce byte array by making an int of 90 /// the first 4 bytes 91 /// </summary> 92 internal sealed class NonceKeyComparer : IEqualityComparer, System.Collections.Generic.IEqualityComparer<byte[]> 93 { GetHashCode(object o)94 public int GetHashCode(object o) 95 { 96 return GetHashCode((byte[])o); 97 } GetHashCode(byte[] o)98 public int GetHashCode(byte[] o) 99 { 100 byte[] nonce = (byte[])o; 101 102 return (((int)nonce[0]) | (((int)nonce[1]) << 8) | (((int)nonce[2]) << 16) | (((int)nonce[3]) << 24)); 103 } 104 Compare(object x, object y)105 public int Compare(object x, object y) 106 { 107 return Compare((byte[])x, (byte[])y); 108 } 109 Compare(byte[] x, byte[] y)110 public int Compare(byte[] x, byte[] y) 111 { 112 if (Object.ReferenceEquals(x, y)) 113 return 0; 114 115 if (x == null) 116 return -1; 117 else if (y == null) 118 return 1; 119 120 byte[] nonce1 = (byte[])x; 121 int length1 = nonce1.Length; 122 byte[] nonce2 = (byte[])y; 123 int length2 = nonce2.Length; 124 125 if (length1 == length2) 126 { 127 for (int i = 0; i < length1; ++i) 128 { 129 int diff = ((int)nonce1[i] - (int)nonce2[i]); 130 131 if (diff != 0) 132 { 133 return diff; 134 } 135 } 136 137 return 0; 138 } 139 else if (length1 > length2) 140 { 141 return 1; 142 } 143 else 144 { 145 return -1; 146 } 147 } 148 Equals(object x, object y)149 public new bool Equals(object x, object y) 150 { 151 return (Compare(x, y) == 0); 152 } 153 Equals(byte[] x, byte[] y)154 public bool Equals(byte[] x, byte[] y) 155 { 156 return (Compare(x, y) == 0); 157 } 158 } 159 } 160 } 161 } 162