diff --git a/OpenSim.Framework/Remoting.cs b/OpenSim.Framework/Remoting.cs new file mode 100644 index 0000000000..88f559862c --- /dev/null +++ b/OpenSim.Framework/Remoting.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Security.Cryptography; + +namespace OpenSim.Framework +{ + /// + /// NEEDS AUDIT. + /// + /// + /// Suggested implementation + /// 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. + /// When sending data to the foreign host - run 'Sign' on the data and affix the returned byte[] to the message. + /// When recieving data from the foreign host - run 'Authenticate' against the data and the attached byte[]. + /// Both hosts should be performing these operations for this to be effective. + /// + class RemoteDigest + { + private byte[] currentHash; + private byte[] secret; + + private SHA512Managed SHA512; + + /// + /// Initialises a new RemoteDigest authentication mechanism + /// + /// 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 + /// The shared secret between systems (for inter-sim, this is provided in encrypted form during connection, for grid this is input manually in setup) + /// Binary salt - some common value - to be decided what + /// The challenge key provided by the third party + public RemoteDigest(string sharedSecret, byte[] salt, string challenge) + { + SHA512 = new SHA512Managed(); + Rfc2898DeriveBytes RFC2898 = new Rfc2898DeriveBytes(sharedSecret,salt); + secret = RFC2898.GetBytes(512); + ASCIIEncoding ASCII = new ASCIIEncoding(); + + currentHash = SHA512.ComputeHash(AppendArrays(secret, ASCII.GetBytes(challenge))); + } + + /// + /// Authenticates a piece of incoming data against the local digest. Upon successful authentication, digest string is incremented. + /// + /// The incoming data + /// The remote digest + /// + public bool Authenticate(byte[] data, byte[] digest) + { + byte[] newHash = SHA512.ComputeHash(AppendArrays(AppendArrays(currentHash, secret), data)); + if (digest == newHash) + { + currentHash = newHash; + return true; + } + else + { + throw new Exception("Hash comparison failed. Key resync required."); + } + } + + /// + /// Signs a new bit of data with the current hash. Returns a byte array which should be affixed to the message. + /// Signing a piece of data will automatically increment the hash - if you sign data and do not send it, the + /// hashes will get out of sync and throw an exception when validation is attempted. + /// + /// The outgoing data + /// The local digest + public byte[] Sign(byte[] data) + { + currentHash = SHA512.ComputeHash(AppendArrays(AppendArrays(currentHash, secret), data)); + return currentHash; + } + + /// + /// Helper function, merges two byte arrays + /// + /// A + /// B + /// C + private byte[] AppendArrays(byte[] a, byte[] b) + { + byte[] c = new byte[a.Length + b.Length]; + Buffer.BlockCopy(a, 0, c, 0, a.Length); + Buffer.BlockCopy(b, 0, c, a.Length, b.Length); + return c; + } + + } +}