429 lines
16 KiB
C#
429 lines
16 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the OpenSimulator Project nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace osWebRtcVoice
|
|
{
|
|
|
|
// There are several different hashing systems ranging from int's to SHA versions.
|
|
// The model here is to create a hasher of the desired type, do Add's of things to
|
|
// hash, and complete with a Finish() to return a BHash that contains the hash value.
|
|
// Since some hash functions are incremental (doing Add's) while some are buffer
|
|
// oriented (create a hash of a byte buffer), the interface is built to cover both.
|
|
// Some optimizations are implemented internally (like not copying the buffer
|
|
// for buffer based hashers if Finish(bytes) is used).
|
|
//
|
|
// var hasher = new BHashSHA256();
|
|
// BHash bHash = hasher.Finish(buffer);
|
|
// byte[] theHash = bHash.ToByte();
|
|
//
|
|
// Note that BHash has IEquatable and IComparible so it can be used in Dictionaries
|
|
// and sorted Lists.
|
|
//
|
|
// The C# GetHashCode() method returns an int that is usually based on location.
|
|
// Signatures should really be at least 64 bits so these routines generate
|
|
// ulong's for hashes and fold them to make the int for GetHashCode().
|
|
|
|
// Create a BHasher, do a bunch of 'Add's, then Finish().
|
|
public interface IBHasher {
|
|
// Create a new object implementing BHasher, then Add values to be hashed
|
|
void Add(byte c);
|
|
void Add(ushort c);
|
|
void Add(uint c);
|
|
void Add(ulong c);
|
|
void Add(float c);
|
|
void Add(byte[] c, int offset, int len);
|
|
void Add(string c);
|
|
void Add(BHash c);
|
|
|
|
BHash Finish();
|
|
// Finish and add byte array.
|
|
// If no Add's before, can do the hashing without copying the byte array
|
|
BHash Finish(byte[] c);
|
|
BHash Finish(byte[] c, int offset, int len);
|
|
// Get the hash code after doing a Finish()
|
|
BHash Hash();
|
|
}
|
|
|
|
// ======================================================================
|
|
// BHasher computes a BHash which holds the hash value after Finish() is called.
|
|
public abstract class BHash : IEquatable<BHash>, IComparable<BHash> {
|
|
public abstract override string ToString();
|
|
public abstract byte[] ToBytes();
|
|
public abstract ulong ToULong(); // returns the hash of the hash if not int based hash
|
|
public abstract bool Equals(BHash other);
|
|
public abstract int CompareTo(BHash obj);
|
|
// public abstract int Compare(BHash x, BHash y); // TODO: do we need this function?
|
|
public abstract override int GetHashCode(); // to match the C# standard hash function
|
|
}
|
|
|
|
// A hash that is an UInt64
|
|
public class BHashULong : BHash {
|
|
protected ulong _hash;
|
|
|
|
public BHashULong() {
|
|
_hash = 5131;
|
|
}
|
|
public BHashULong(ulong initialHash) {
|
|
_hash = initialHash;
|
|
}
|
|
// the .NET GetHashCode uses an int. Make conversion easy.
|
|
public BHashULong(int initialHash) {
|
|
_hash = (ulong)initialHash;
|
|
}
|
|
public override string ToString() {
|
|
return _hash.ToString();
|
|
}
|
|
public override byte[] ToBytes() {
|
|
return BitConverter.GetBytes(_hash);
|
|
}
|
|
public override ulong ToULong() {
|
|
return _hash;
|
|
}
|
|
public override bool Equals(BHash other) {
|
|
bool ret = false;
|
|
if (other != null) {
|
|
BHash bh = other as BHashULong;
|
|
if (bh != null) {
|
|
ret = _hash.Equals(bh.ToULong());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
public override int CompareTo(BHash other) {
|
|
int ret = 1;
|
|
if (other != null) {
|
|
if (other is BHashULong bh) {
|
|
ret = _hash.CompareTo(bh.ToULong());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
public override int GetHashCode() {
|
|
ulong upper = (_hash >> 32) & 0xffffffff;
|
|
ulong lower = _hash & 0xffffffff;
|
|
return (int)(upper ^ lower);
|
|
}
|
|
}
|
|
|
|
// A hash that is an array of bytes
|
|
public class BHashBytes : BHash {
|
|
private readonly byte[] _hash;
|
|
|
|
public BHashBytes() {
|
|
_hash = new byte[0];
|
|
}
|
|
public BHashBytes(byte[] initialHash) {
|
|
_hash = initialHash;
|
|
}
|
|
public override string ToString() {
|
|
// BitConverter puts a hyphen between each byte. Remove them
|
|
return BitConverter.ToString(_hash).Replace("-", String.Empty);
|
|
}
|
|
public override byte[] ToBytes() {
|
|
return _hash;
|
|
}
|
|
public override ulong ToULong() {
|
|
return this.MakeHashCode();
|
|
}
|
|
public override bool Equals(BHash other) {
|
|
bool ret = false;
|
|
if (other != null) {
|
|
BHash bh = other as BHashBytes;
|
|
if (bh != null) {
|
|
ret = _hash.Equals(bh.ToBytes());
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
public override int CompareTo(BHash other) {
|
|
int ret = 1;
|
|
if (other != null) {
|
|
BHash bh = other as BHashBytes;
|
|
if (bh != null) {
|
|
byte[] otherb = bh.ToBytes();
|
|
if (_hash.Length != otherb.Length) {
|
|
ret = _hash.Length.CompareTo(otherb.Length);
|
|
}
|
|
else {
|
|
ret = 0; // start off assuming they are equal
|
|
for (int ii = 0; ii < _hash.Length; ii++) {
|
|
ret = _hash[ii].CompareTo(otherb[ii]);
|
|
if (ret != 0) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
public override int GetHashCode()
|
|
{
|
|
ulong hashhash = this.MakeHashCode();
|
|
ulong upper = (hashhash >> 32 )& 0xffffffff;
|
|
ulong lower = hashhash & 0xffffffff;
|
|
return (int)(upper ^ lower);
|
|
}
|
|
public ulong MakeHashCode() {
|
|
ulong h = 5381;
|
|
for (int ii = 0; ii < _hash.Length; ii++) {
|
|
h = ((h << 5) + h) + (ulong)(_hash[ii]);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
}
|
|
|
|
// ======================================================================
|
|
// ======================================================================
|
|
public abstract class BHasher : IBHasher
|
|
{
|
|
public BHasher() {
|
|
}
|
|
|
|
public abstract void Add(byte c);
|
|
public abstract void Add(ushort c);
|
|
public abstract void Add(uint c);
|
|
public abstract void Add(ulong c);
|
|
public abstract void Add(float c);
|
|
public abstract void Add(byte[] c, int offset, int len);
|
|
public abstract void Add(string c);
|
|
public abstract void Add(BHash c);
|
|
public abstract BHash Finish();
|
|
public abstract BHash Finish(byte[] c);
|
|
public abstract BHash Finish(byte[] c, int offset, int len);
|
|
public abstract BHash Hash();
|
|
}
|
|
|
|
// A hasher that builds up a buffer of bytes ('building') and then hashes over same
|
|
public abstract class BHasherBytes : BHasher {
|
|
protected byte[] building;
|
|
protected int buildingLoc;
|
|
protected int allocStep = 1024;
|
|
|
|
public BHasherBytes() : base() {
|
|
building = new byte[allocStep];
|
|
buildingLoc = 0;
|
|
}
|
|
|
|
private byte[] oneByte = new byte[1];
|
|
public override void Add(byte c) {
|
|
oneByte[0] = c;
|
|
AddBytes(oneByte, 0, 1);
|
|
}
|
|
|
|
public override void Add(ushort c) {
|
|
byte[] bytes = BitConverter.GetBytes(c);
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
public override void Add(uint c) {
|
|
byte[] bytes = BitConverter.GetBytes(c);
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
public override void Add(ulong c) {
|
|
byte[] bytes = BitConverter.GetBytes(c);
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
public override void Add(float c) {
|
|
byte[] bytes = BitConverter.GetBytes(c);
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
public override void Add(byte[] c, int offset, int len) {
|
|
AddBytes(c, offset, len);
|
|
}
|
|
|
|
public override void Add(string c)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes(c);
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
public override void Add(BHash c) {
|
|
byte[] bytes = c.ToBytes();
|
|
AddBytes(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
// Implemented by derived class
|
|
// public abstract BHash Finish();
|
|
|
|
// Helper function for simple byte array
|
|
public override BHash Finish(byte[] c) {
|
|
return this.Finish(c, 0, c.Length);
|
|
}
|
|
|
|
// Implemented by derived class
|
|
// public abstract BHash Finish(byte[] c, int offset, int len);
|
|
|
|
// Implemented by derived class
|
|
// public abstract BHash Hash();
|
|
|
|
// Add the given number of bytes to the byte array being built
|
|
protected void AddBytes(byte[] addition, int offset, int len) {
|
|
// byte[] tempBytes = new byte[len]; // DEBUG DEBUG
|
|
// Array.Copy(addition, offset, tempBytes, 0, len); // DEBUG DEBUG
|
|
// System.Console.WriteLine(String.Format("AddBytes: offset={0}, len={1}, bytes={2}", // DEBUG DEBUG
|
|
// offset, len, BitConverter.ToString(tempBytes).Replace("-", String.Empty))); // DEBUG DEBUG
|
|
if (len < 0 || offset < 0 || addition == null) {
|
|
throw new ArgumentException(String.Format("BHasherBytes.AddBytes: Bad parameters. offset={0}, len={1}",
|
|
offset, len));
|
|
}
|
|
if (offset + len > addition.Length) {
|
|
throw new ArgumentException(String.Format("BHasherBytes.AddBytes: addition parameters off end of array. addition.len={0}, offset={1}, len={2}",
|
|
addition.Length, offset, len));
|
|
}
|
|
if (len > 0) {
|
|
if (buildingLoc + len > building.Length) {
|
|
// New data requires expanding the data buffer
|
|
byte[] newBuilding = new byte[buildingLoc + len + allocStep];
|
|
Buffer.BlockCopy(building, 0, newBuilding, 0, buildingLoc);
|
|
building = newBuilding;
|
|
}
|
|
Buffer.BlockCopy(addition, offset, building, buildingLoc, len);
|
|
buildingLoc += len;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|
|
// ULong hash code taken from Meshmerizer
|
|
public class BHasherMdjb2 : BHasherBytes, IBHasher {
|
|
BHashULong hash = new BHashULong();
|
|
|
|
public BHasherMdjb2() : base() {
|
|
}
|
|
|
|
public override BHash Finish() {
|
|
hash = new BHashULong(ComputeMdjb2Hash(building, 0, buildingLoc));
|
|
return hash;
|
|
}
|
|
|
|
public override BHash Finish(byte[] c, int offset, int len) {
|
|
if (building.Length > 0) {
|
|
AddBytes(c, offset, len);
|
|
hash = new BHashULong(ComputeMdjb2Hash(building, 0, buildingLoc));
|
|
}
|
|
else {
|
|
// if no 'Add's were done, don't copy the input data
|
|
hash = new BHashULong(ComputeMdjb2Hash(c, offset, len));
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
private ulong ComputeMdjb2Hash(byte[] c, int offset, int len) {
|
|
ulong h = 5381;
|
|
for (int ii = offset; ii < offset+len; ii++) {
|
|
h = ((h << 5) + h) + (ulong)(c[ii]);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
public override BHash Hash() {
|
|
return hash;
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|
|
public class BHasherMD5 : BHasherBytes, IBHasher {
|
|
BHashBytes hash = new BHashBytes();
|
|
|
|
public BHasherMD5() : base() {
|
|
}
|
|
|
|
public override BHash Finish() {
|
|
MD5 md5 = MD5.Create();
|
|
hash = new BHashBytes(md5.ComputeHash(building, 0, buildingLoc));
|
|
return hash;
|
|
}
|
|
|
|
public override BHash Finish(byte[] c) {
|
|
return this.Finish(c, 0, c.Length);
|
|
}
|
|
|
|
public override BHash Finish(byte[] c, int offset, int len) {
|
|
MD5 md5 = MD5.Create();
|
|
if (building.Length > 0) {
|
|
AddBytes(c, offset, len);
|
|
hash = new BHashBytes(md5.ComputeHash(building, 0, buildingLoc));
|
|
}
|
|
else {
|
|
// if no 'Add's were done, don't copy the input data
|
|
hash = new BHashBytes(md5.ComputeHash(c, offset, len));
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
public override BHash Hash() {
|
|
return hash;
|
|
}
|
|
}
|
|
|
|
// ======================================================================
|
|
public class BHasherSHA256 : BHasherBytes, IBHasher {
|
|
BHashBytes hash = new BHashBytes();
|
|
|
|
public BHasherSHA256() : base() {
|
|
}
|
|
|
|
public override BHash Finish() {
|
|
using (SHA256 SHA256 = SHA256.Create()) {
|
|
hash = new BHashBytes(SHA256.ComputeHash(building, 0, buildingLoc));
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
public override BHash Finish(byte[] c) {
|
|
return this.Finish(c, 0, c.Length);
|
|
}
|
|
|
|
public override BHash Finish(byte[] c, int offset, int len) {
|
|
using (SHA256 SHA256 = SHA256.Create()) {
|
|
if (buildingLoc > 0) {
|
|
AddBytes(c, offset, len);
|
|
hash = new BHashBytes(SHA256.ComputeHash(building, 0, buildingLoc));
|
|
}
|
|
else {
|
|
// if no 'Add's were done, don't copy the input data
|
|
hash = new BHashBytes(SHA256.ComputeHash(c, offset, len));
|
|
}
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
public override BHash Hash() {
|
|
return hash;
|
|
}
|
|
}
|
|
}
|