Remoting.cs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Security.Cryptography;
  5. namespace OpenSim.Framework
  6. {
  7. /// <summary>
  8. /// NEEDS AUDIT.
  9. /// </summary>
  10. /// <remarks>
  11. /// Suggested implementation
  12. /// <para>Store two digests for each foreign host. A local copy of the local hash using the local challenge (when issued), and a local copy of the remote hash using the remote challenge.</para>
  13. /// <para>When sending data to the foreign host - run 'Sign' on the data and affix the returned byte[] to the message.</para>
  14. /// <para>When recieving data from the foreign host - run 'Authenticate' against the data and the attached byte[].</para>
  15. /// <para>Both hosts should be performing these operations for this to be effective.</para>
  16. /// </remarks>
  17. class RemoteDigest
  18. {
  19. private byte[] currentHash;
  20. private byte[] secret;
  21. private SHA512Managed SHA512;
  22. /// <summary>
  23. /// Initialises a new RemoteDigest authentication mechanism
  24. /// </summary>
  25. /// <remarks>Needs an audit by a cryptographic professional - was not "roll your own"'d by choice but rather a serious lack of decent authentication mechanisms in .NET remoting</remarks>
  26. /// <param name="sharedSecret">The shared secret between systems (for inter-sim, this is provided in encrypted form during connection, for grid this is input manually in setup)</param>
  27. /// <param name="salt">Binary salt - some common value - to be decided what</param>
  28. /// <param name="challenge">The challenge key provided by the third party</param>
  29. public RemoteDigest(string sharedSecret, byte[] salt, string challenge)
  30. {
  31. SHA512 = new SHA512Managed();
  32. Rfc2898DeriveBytes RFC2898 = new Rfc2898DeriveBytes(sharedSecret,salt);
  33. secret = RFC2898.GetBytes(512);
  34. ASCIIEncoding ASCII = new ASCIIEncoding();
  35. currentHash = SHA512.ComputeHash(AppendArrays(secret, ASCII.GetBytes(challenge)));
  36. }
  37. /// <summary>
  38. /// Authenticates a piece of incoming data against the local digest. Upon successful authentication, digest string is incremented.
  39. /// </summary>
  40. /// <param name="data">The incoming data</param>
  41. /// <param name="digest">The remote digest</param>
  42. /// <returns></returns>
  43. public bool Authenticate(byte[] data, byte[] digest)
  44. {
  45. byte[] newHash = SHA512.ComputeHash(AppendArrays(AppendArrays(currentHash, secret), data));
  46. if (digest == newHash)
  47. {
  48. currentHash = newHash;
  49. return true;
  50. }
  51. else
  52. {
  53. throw new Exception("Hash comparison failed. Key resync required.");
  54. }
  55. }
  56. /// <summary>
  57. /// Signs a new bit of data with the current hash. Returns a byte array which should be affixed to the message.
  58. /// Signing a piece of data will automatically increment the hash - if you sign data and do not send it, the
  59. /// hashes will get out of sync and throw an exception when validation is attempted.
  60. /// </summary>
  61. /// <param name="data">The outgoing data</param>
  62. /// <returns>The local digest</returns>
  63. public byte[] Sign(byte[] data)
  64. {
  65. currentHash = SHA512.ComputeHash(AppendArrays(AppendArrays(currentHash, secret), data));
  66. return currentHash;
  67. }
  68. /// <summary>
  69. /// Generates a new challenge string to be issued to a foreign host. Challenges are 1024-bit (effective strength of less than 512-bits) messages generated using the Crytographic Random Number Generator.
  70. /// </summary>
  71. /// <returns>A 128-character hexadecimal string containing the challenge.</returns>
  72. public static string GenerateChallenge()
  73. {
  74. RNGCryptoServiceProvider RNG = new RNGCryptoServiceProvider();
  75. byte[] bytes = new byte[64];
  76. RNG.GetBytes(bytes);
  77. StringBuilder sb = new StringBuilder(bytes.Length * 2);
  78. foreach (byte b in bytes)
  79. {
  80. sb.AppendFormat("{0:x2}", b);
  81. }
  82. return sb.ToString();
  83. }
  84. /// <summary>
  85. /// Helper function, merges two byte arrays
  86. /// </summary>
  87. /// <remarks>Sourced from MSDN Forum</remarks>
  88. /// <param name="a">A</param>
  89. /// <param name="b">B</param>
  90. /// <returns>C</returns>
  91. private byte[] AppendArrays(byte[] a, byte[] b)
  92. {
  93. byte[] c = new byte[a.Length + b.Length];
  94. Buffer.BlockCopy(a, 0, c, 0, a.Length);
  95. Buffer.BlockCopy(b, 0, c, a.Length, b.Length);
  96. return c;
  97. }
  98. }
  99. }