InMemoryNonceCache.cs 6.0 KB

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