Use a collision-resistant hash algorithm for untrusted data (#5)

* Fix conflict

* Use safer unaligned-read methods

---------

Co-authored-by: Andrew Arnott <andrew.arnott@microsoft.com>
This commit is contained in:
William Godbe 2024-10-21 10:02:20 -07:00 коммит произвёл GitHub
Родитель 95119056ee
Коммит 9aeb12b9bd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
8 изменённых файлов: 552 добавлений и 140 удалений

Просмотреть файл

@ -75,6 +75,9 @@
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\MessagePackSecurity.cs">
<Link>Code\MessagePackSecurity.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\SipHash.cs">
<Link>Code\SipHash.cs</Link>
</Compile>
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\MonoProtection.cs">
<Link>Code\MonoProtection.cs</Link>
</Compile>

Просмотреть файл

@ -6,7 +6,9 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using MessagePack.Formatters;
using MessagePack.Internal;
@ -31,6 +33,8 @@ namespace MessagePack
MaximumObjectGraphDepth = 500,
};
private static readonly SipHash Hash = new();
private readonly ObjectFallbackEqualityComparer objectFallbackEqualityComparer;
private MessagePackSecurity()
@ -61,7 +65,7 @@ namespace MessagePack
/// This can mitigate some denial of service attacks when deserializing untrusted code.
/// </summary>
/// <value>
/// The value is <c>false</c> for <see cref="TrustedData"/> and <c>true</c> for <see cref="UntrustedData"/>.
/// The value is <see langword="false"/> for <see cref="TrustedData"/> and <see langword="true"/> for <see cref="UntrustedData"/>.
/// </value>
public bool HashCollisionResistant { get; private set; }
@ -138,6 +142,51 @@ namespace MessagePack
return this.HashCollisionResistant ? GetHashCollisionResistantEqualityComparer() : EqualityComparer<object>.Default;
}
private class HashResistantCache<T>
{
internal static readonly IEqualityComparer<T>? EqualityComparer;
static HashResistantCache()
{
// We have to specially handle some 32-bit types (e.g. float) where multiple in-memory representations should hash to the same value.
// Any type supported by the PrimitiveObjectFormatter should be added here if supporting it as a key in a collection makes sense.
EqualityComparer =
typeof(T) == typeof(bool) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<bool>.Instance :
typeof(T) == typeof(char) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<char>.Instance :
typeof(T) == typeof(sbyte) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<sbyte>.Instance :
typeof(T) == typeof(byte) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<byte>.Instance :
typeof(T) == typeof(short) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<short>.Instance :
typeof(T) == typeof(ushort) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<ushort>.Instance :
typeof(T) == typeof(int) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<int>.Instance :
typeof(T) == typeof(uint) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<uint>.Instance :
typeof(T) == typeof(long) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<long>.Instance :
typeof(T) == typeof(ulong) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<ulong>.Instance :
typeof(T) == typeof(Guid) ? (IEqualityComparer<T>)CollisionResistantHasherUnmanaged<Guid>.Instance :
// Data types that are managed or have multiple in-memory representations for equivalent values:
typeof(T) == typeof(float) ? (IEqualityComparer<T>)SingleEqualityComparer.Instance :
typeof(T) == typeof(double) ? (IEqualityComparer<T>)DoubleEqualityComparer.Instance :
typeof(T) == typeof(string) ? (IEqualityComparer<T>)StringEqualityComparer.Instance :
typeof(T) == typeof(DateTime) ? (IEqualityComparer<T>)DateTimeEqualityComparer.Instance :
typeof(T) == typeof(DateTimeOffset) ? (IEqualityComparer<T>)DateTimeOffsetEqualityComparer.Instance :
// Call out each primitive behind an enum explicitly to avoid dynamically generating code.
typeof(T).GetTypeInfo().IsEnum && typeof(T).GetTypeInfo().GetEnumUnderlyingType() is Type underlying ? (
underlying == typeof(byte) ? CollisionResistantEnumHasher<T, byte>.Instance :
underlying == typeof(sbyte) ? CollisionResistantEnumHasher<T, sbyte>.Instance :
underlying == typeof(ushort) ? CollisionResistantEnumHasher<T, ushort>.Instance :
underlying == typeof(short) ? CollisionResistantEnumHasher<T, short>.Instance :
underlying == typeof(uint) ? CollisionResistantEnumHasher<T, uint>.Instance :
underlying == typeof(int) ? CollisionResistantEnumHasher<T, int>.Instance :
underlying == typeof(ulong) ? CollisionResistantEnumHasher<T, ulong>.Instance :
underlying == typeof(long) ? CollisionResistantEnumHasher<T, long>.Instance :
null) :
// Failsafe. If we don't recognize the type, don't assume we have a good, secure hash function for it.
null;
}
}
/// <summary>
/// Returns a hash collision resistant equality comparer.
/// </summary>
@ -145,54 +194,20 @@ namespace MessagePack
/// <returns>A hash collision resistant equality comparer.</returns>
protected virtual IEqualityComparer<T> GetHashCollisionResistantEqualityComparer<T>()
{
IEqualityComparer<T> result = null;
if (typeof(T).GetTypeInfo().IsEnum)
if (HashResistantCache<T>.EqualityComparer is { } result)
{
Type underlyingType = typeof(T).GetTypeInfo().GetEnumUnderlyingType();
result =
underlyingType == typeof(sbyte) ? CollisionResistantHasher<T>.Instance :
underlyingType == typeof(byte) ? CollisionResistantHasher<T>.Instance :
underlyingType == typeof(short) ? CollisionResistantHasher<T>.Instance :
underlyingType == typeof(ushort) ? CollisionResistantHasher<T>.Instance :
underlyingType == typeof(int) ? CollisionResistantHasher<T>.Instance :
underlyingType == typeof(uint) ? CollisionResistantHasher<T>.Instance :
null;
return result;
}
else
{
// For anything 32-bits and under, our fallback base secure hasher is usually adequate since it makes the hash unpredictable.
// We should have special implementations for any value that is larger than 32-bits in order to make sure
// that all the data gets hashed securely rather than trivially and predictably compressed into 32-bits before being hashed.
// We also have to specially handle some 32-bit types (e.g. float) where multiple in-memory representations should hash to the same value.
// Any type supported by the PrimitiveObjectFormatter should be added here if supporting it as a key in a collection makes sense.
result =
// 32-bits or smaller:
typeof(T) == typeof(bool) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(char) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(sbyte) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(byte) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(short) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(ushort) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(int) ? CollisionResistantHasher<T>.Instance :
typeof(T) == typeof(uint) ? CollisionResistantHasher<T>.Instance :
// Larger than 32-bits (or otherwise require special handling):
typeof(T) == typeof(long) ? (IEqualityComparer<T>)Int64EqualityComparer.Instance :
typeof(T) == typeof(ulong) ? (IEqualityComparer<T>)UInt64EqualityComparer.Instance :
typeof(T) == typeof(float) ? (IEqualityComparer<T>)SingleEqualityComparer.Instance :
typeof(T) == typeof(double) ? (IEqualityComparer<T>)DoubleEqualityComparer.Instance :
typeof(T) == typeof(string) ? (IEqualityComparer<T>)StringEqualityComparer.Instance :
typeof(T) == typeof(Guid) ? (IEqualityComparer<T>)GuidEqualityComparer.Instance :
typeof(T) == typeof(DateTime) ? (IEqualityComparer<T>)DateTimeEqualityComparer.Instance :
typeof(T) == typeof(DateTimeOffset) ? (IEqualityComparer<T>)DateTimeOffsetEqualityComparer.Instance :
typeof(T) == typeof(object) ? (IEqualityComparer<T>)this.objectFallbackEqualityComparer :
null;
if (typeof(T) == typeof(object))
{
return (IEqualityComparer<T>)this.objectFallbackEqualityComparer;
}
// Any type we don't explicitly whitelist here shouldn't be allowed to use as the key in a hash-based collection since it isn't known to be hash resistant.
// This method can of course be overridden to add more hash collision resistant type support, or the deserializing party can indicate that the data is Trusted
// so that this method doesn't even get called.
return result ?? throw new TypeAccessException($"No hash-resistant equality comparer available for type: {typeof(T)}");
throw new TypeAccessException($"No hash-resistant equality comparer available for type: {typeof(T)}");
}
/// <summary>
@ -232,25 +247,41 @@ namespace MessagePack
/// </remarks>
protected virtual MessagePackSecurity Clone() => new MessagePackSecurity(this);
private static int SecureHash<T>(T value)
where T : unmanaged
{
Span<T> span = stackalloc T[1];
span[0] = value;
return unchecked((int)Hash.Compute(MemoryMarshal.Cast<T, byte>(span)));
}
private static int SecureHash(ReadOnlySpan<byte> data) => unchecked((int)Hash.Compute(data));
/// <summary>
/// A hash collision resistant implementation of <see cref="IEqualityComparer{T}"/>.
/// </summary>
/// <typeparam name="T">The type of key that will be hashed.</typeparam>
private class CollisionResistantHasher<T> : IEqualityComparer<T>, IEqualityComparer
private abstract class CollisionResistantHasher<T> : IEqualityComparer<T>, IEqualityComparer
{
internal static readonly CollisionResistantHasher<T> Instance = new CollisionResistantHasher<T>();
public bool Equals(T? x, T? y) => EqualityComparer<T?>.Default.Equals(x, y);
public bool Equals(T x, T y) => EqualityComparer<T>.Default.Equals(x, y);
bool IEqualityComparer.Equals(object x, object y) => ((IEqualityComparer)EqualityComparer<T>.Default).Equals(x, y);
bool IEqualityComparer.Equals(object? x, object? y) => ((IEqualityComparer)EqualityComparer<T>.Default).Equals(x, y);
public int GetHashCode(object obj) => this.GetHashCode((T)obj);
public virtual int GetHashCode(T value) => HashCode.Combine(value);
public abstract int GetHashCode(T value);
}
private class CollisionResistantHasherUnmanaged<T> : CollisionResistantHasher<T>
where T : unmanaged
{
internal static readonly CollisionResistantHasherUnmanaged<T> Instance = new();
public override int GetHashCode(T value) => SecureHash(value);
}
/// <summary>
/// A special hash-resistent equality comparer that defers picking the actual implementation
/// A special hash-resistant equality comparer that defers picking the actual implementation
/// till it can check the runtime type of each value to be hashed.
/// </summary>
private class ObjectFallbackEqualityComparer : IEqualityComparer<object>, IEqualityComparer
@ -264,9 +295,9 @@ namespace MessagePack
this.security = security ?? throw new ArgumentNullException(nameof(security));
}
bool IEqualityComparer<object>.Equals(object x, object y) => EqualityComparer<object>.Default.Equals(x, y);
bool IEqualityComparer<object>.Equals(object? x, object? y) => EqualityComparer<object?>.Default.Equals(x, y);
bool IEqualityComparer.Equals(object x, object y) => ((IEqualityComparer)EqualityComparer<object>.Default).Equals(x, y);
bool IEqualityComparer.Equals(object? x, object? y) => ((IEqualityComparer)EqualityComparer<object>.Default).Equals(x, y);
public int GetHashCode(object value)
{
@ -284,15 +315,16 @@ namespace MessagePack
return value.GetHashCode();
}
if (!equalityComparerCache.TryGetValue(valueType, out IEqualityComparer equalityComparer))
if (!equalityComparerCache.TryGetValue(valueType, out IEqualityComparer? equalityComparer))
{
try
{
equalityComparer = (IEqualityComparer)GetHashCollisionResistantEqualityComparerOpenGenericMethod.Value.MakeGenericMethod(valueType).Invoke(this.security, Array.Empty<object>());
equalityComparer = (IEqualityComparer)GetHashCollisionResistantEqualityComparerOpenGenericMethod.Value.MakeGenericMethod(valueType).Invoke(this.security, Array.Empty<object>())!;
}
catch (TargetInvocationException ex)
catch (TargetInvocationException ex) when (ex.InnerException is not null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
throw null!; // not reachable
}
equalityComparerCache.TryAdd(valueType, equalityComparer);
@ -302,116 +334,69 @@ namespace MessagePack
}
}
private class UInt64EqualityComparer : CollisionResistantHasher<ulong>
private class SingleEqualityComparer : CollisionResistantHasherUnmanaged<float>
{
internal static new readonly UInt64EqualityComparer Instance = new UInt64EqualityComparer();
public override int GetHashCode(ulong value) => HashCode.Combine((uint)(value >> 32), unchecked((uint)value));
}
private class Int64EqualityComparer : CollisionResistantHasher<long>
{
internal static new readonly Int64EqualityComparer Instance = new Int64EqualityComparer();
public override int GetHashCode(long value) => HashCode.Combine((int)(value >> 32), unchecked((int)value));
}
private class SingleEqualityComparer : CollisionResistantHasher<float>
{
internal static new readonly SingleEqualityComparer Instance = new SingleEqualityComparer();
internal static new readonly SingleEqualityComparer Instance = new();
public override unsafe int GetHashCode(float value)
{
// Special check for 0.0 so that the hash of 0.0 and -0.0 will equal.
if (value == 0.0f)
=> base.GetHashCode(value switch
{
return HashCode.Combine(0);
}
// Standardize on the binary representation of NaN prior to hashing.
if (float.IsNaN(value))
{
value = float.NaN;
}
int l = *(int*)&value;
return l;
}
0.0f => 0, // Special check for 0.0 so that the hash of 0.0 and -0.0 will equal.
float.NaN => float.NaN, // Standardize on the binary representation of NaN prior to hashing.
_ => value,
});
}
private class DoubleEqualityComparer : CollisionResistantHasher<double>
private class DoubleEqualityComparer : CollisionResistantHasherUnmanaged<double>
{
internal static new readonly DoubleEqualityComparer Instance = new DoubleEqualityComparer();
internal static new readonly DoubleEqualityComparer Instance = new();
public override unsafe int GetHashCode(double value)
{
// Special check for 0.0 so that the hash of 0.0 and -0.0 will equal.
if (value == 0.0)
=> base.GetHashCode(value switch
{
return HashCode.Combine(0);
}
// Standardize on the binary representation of NaN prior to hashing.
if (double.IsNaN(value))
{
value = double.NaN;
}
long l = *(long*)&value;
return HashCode.Combine((int)(l >> 32), unchecked((int)l));
}
0.0 => 0, // Special check for 0.0 so that the hash of 0.0 and -0.0 will equal.
double.NaN => double.NaN, // Standardize on the binary representation of NaN prior to hashing.
_ => value,
});
}
private class GuidEqualityComparer : CollisionResistantHasher<Guid>
private class DateTimeEqualityComparer : CollisionResistantHasherUnmanaged<DateTime>
{
internal static new readonly GuidEqualityComparer Instance = new GuidEqualityComparer();
internal static new readonly DateTimeEqualityComparer Instance = new();
public override unsafe int GetHashCode(Guid value)
{
var hash = default(HashCode);
int* pGuid = (int*)&value;
for (int i = 0; i < sizeof(Guid) / sizeof(int); i++)
{
hash.Add(pGuid[i]);
}
public override unsafe int GetHashCode(DateTime value) => SecureHash(value.Ticks);
}
return hash.ToHashCode();
}
private class DateTimeOffsetEqualityComparer : CollisionResistantHasherUnmanaged<DateTimeOffset>
{
internal static new readonly DateTimeOffsetEqualityComparer Instance = new();
public override unsafe int GetHashCode(DateTimeOffset value) => SecureHash(value.UtcDateTime.Ticks);
}
private class StringEqualityComparer : CollisionResistantHasher<string>
{
internal static new readonly StringEqualityComparer Instance = new StringEqualityComparer();
internal static readonly StringEqualityComparer Instance = new();
public override int GetHashCode(string value)
{
#if NETCOREAPP
// .NET Core already has a secure string hashing function. Just use it.
return value?.GetHashCode() ?? 0;
#else
var hash = default(HashCode);
for (int i = 0; i < value.Length; i++)
{
hash.Add(value[i]);
}
return hash.ToHashCode();
#endif
// The Cast call could result in OverflowException at runtime if value is greater than 1bn chars in length.
return SecureHash(MemoryMarshal.Cast<char, byte>(value.AsSpan()));
}
}
private class DateTimeEqualityComparer : CollisionResistantHasher<DateTime>
private class CollisionResistantEnumHasher<TEnum, TUnderlying> : IEqualityComparer<TEnum>, IEqualityComparer
where TUnderlying : unmanaged
{
internal static new readonly DateTimeEqualityComparer Instance = new DateTimeEqualityComparer();
internal static readonly CollisionResistantEnumHasher<TEnum, TUnderlying> Instance = new();
public override unsafe int GetHashCode(DateTime value) => HashCode.Combine((int)(value.Ticks >> 32), unchecked((int)value.Ticks), value.Kind);
}
public bool Equals(TEnum? x, TEnum? y) => EqualityComparer<TEnum?>.Default.Equals(x, y);
private class DateTimeOffsetEqualityComparer : CollisionResistantHasher<DateTimeOffset>
{
internal static new readonly DateTimeOffsetEqualityComparer Instance = new DateTimeOffsetEqualityComparer();
public int GetHashCode(TEnum obj) => SecureHash(Unsafe.As<TEnum, TUnderlying>(ref obj));
public override unsafe int GetHashCode(DateTimeOffset value) => HashCode.Combine((int)(value.UtcTicks >> 32), unchecked((int)value.UtcTicks));
bool IEqualityComparer.Equals(object? x, object? y) => x is TEnum e1 && y is TEnum e2 && Equals(e1, e2);
int IEqualityComparer.GetHashCode(object obj) => GetHashCode((TEnum)obj);
}
}
}
}

Просмотреть файл

@ -0,0 +1,287 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//// Originally from: https://github.com/paya-cz/siphash
//// Author: Pavel Werl
//// License: Public Domain
//// SipHash website: https://131002.net/siphash/
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace MessagePack
{
/// <summary>
/// Implements the <see href="https://en.wikipedia.org/wiki/SipHash">SipHash pseudo-random function</see>.
/// </summary>
/// <remarks>
/// This class is immutable and thread-safe.
/// </remarks>
internal class SipHash
{
/// <summary>
/// Part of the initial 256-bit internal state.
/// </summary>
private readonly ulong initialState0;
/// <summary>
/// Part of the initial 256-bit internal state.
/// </summary>
private readonly ulong initialState1;
/// <summary>Initializes a new instance of the <see cref="SipHash"/> class using a random key.</summary>
public SipHash()
{
using RandomNumberGenerator rng = RandomNumberGenerator.Create();
#if SPAN_BUILTIN
Span<byte> key = stackalloc byte[16];
rng.GetBytes(key);
#else
byte[] buffer = ArrayPool<byte>.Shared.Rent(16);
rng.GetBytes(buffer, 0, 16);
Span<byte> key = buffer;
#endif
this.initialState0 = 0x736f6d6570736575UL ^ BinaryPrimitives.ReadUInt64LittleEndian(key);
this.initialState1 = 0x646f72616e646f6dUL ^ BinaryPrimitives.ReadUInt64LittleEndian(key.Slice(sizeof(ulong)));
#if !SPAN_BUILTIN
ArrayPool<byte>.Shared.Return(buffer);
#endif
}
/// <summary>Initializes a new instance of the <see cref="SipHash"/> class using the specified 128-bit key.</summary>
/// <param name="key">Key for the SipHash pseudo-random function. Must be exactly 16 bytes long.</param>
/// <exception cref="ArgumentException">Thrown when <paramref name="key"/> is not exactly 16 bytes long (128 bits).</exception>
public SipHash(ReadOnlySpan<byte> key)
{
if (key.Length != 16)
{
throw new ArgumentException("SipHash key must be exactly 128-bit long (16 bytes).", nameof(key));
}
this.initialState0 = 0x736f6d6570736575UL ^ BinaryPrimitives.ReadUInt64LittleEndian(key);
this.initialState1 = 0x646f72616e646f6dUL ^ BinaryPrimitives.ReadUInt64LittleEndian(key.Slice(sizeof(ulong)));
}
/// <summary>
/// Gets a 128-bit SipHash key.
/// </summary>
/// <param name="key">The 16-byte buffer that receives the key originally provided to the constructor.</param>
public void GetKey(Span<byte> key)
{
if (key.Length != 16)
{
throw new ArgumentException("SipHash key must be exactly 128-bit long (16 bytes).", nameof(key));
}
BinaryPrimitives.WriteUInt64LittleEndian(key, this.initialState0 ^ 0x736f6d6570736575UL);
BinaryPrimitives.WriteUInt64LittleEndian(key.Slice(sizeof(ulong)), this.initialState1 ^ 0x646f72616e646f6dUL);
}
/// <summary>Computes 64-bit SipHash tag for the specified message.</summary>
/// <param name="data">The byte array for which to computer SipHash tag.</param>
/// <returns>Returns 64-bit (8 bytes) SipHash tag.</returns>
public long Compute(ReadOnlySpan<byte> data)
{
unchecked
{
// SipHash internal state
ulong v0 = this.initialState0;
ulong v1 = this.initialState1;
// It is faster to load the initialStateX fields from memory again than to reference v0 and v1:
ulong v2 = 0x1F160A001E161714UL ^ this.initialState0;
ulong v3 = 0x100A160317100A1EUL ^ this.initialState1;
// We process data in 64-bit blocks
ulong block;
// The last 64-bit block of data
int finalBlockPosition = data.Length & ~7;
// Process the input data in blocks of 64 bits
for (int blockPosition = 0; blockPosition < finalBlockPosition; blockPosition += sizeof(ulong))
{
block = MemoryMarshal.Read<ulong>(data.Slice(blockPosition));
v3 ^= block;
// Round 1
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
// Round 2
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
v0 ^= block;
}
// Load the remaining bytes
block = (ulong)data.Length << 56;
switch (data.Length & 7)
{
case 7:
block |= MemoryMarshal.Read<uint>(data.Slice(finalBlockPosition)) | (ulong)MemoryMarshal.Read<ushort>(data.Slice(finalBlockPosition + 4)) << 32 | (ulong)data[finalBlockPosition + 6] << 48;
break;
case 6:
block |= MemoryMarshal.Read<uint>(data.Slice(finalBlockPosition)) | (ulong)MemoryMarshal.Read<ushort>(data.Slice(finalBlockPosition + 4)) << 32;
break;
case 5:
block |= MemoryMarshal.Read<uint>(data.Slice(finalBlockPosition)) | (ulong)data[finalBlockPosition + 4] << 32;
break;
case 4:
block |= MemoryMarshal.Read<uint>(data.Slice(finalBlockPosition));
break;
case 3:
block |= MemoryMarshal.Read<ushort>(data.Slice(finalBlockPosition)) | (ulong)data[finalBlockPosition + 2] << 16;
break;
case 2:
block |= MemoryMarshal.Read<ushort>(data.Slice(finalBlockPosition));
break;
case 1:
block |= data[finalBlockPosition];
break;
}
// Process the final block
{
v3 ^= block;
// Round 1
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
// Round 2
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
v0 ^= block;
v2 ^= 0xff;
}
// 4 finalization rounds
{
// Round 1
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
// Round 2
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
// Round 3
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
// Round 4
v0 += v1;
v2 += v3;
v1 = v1 << 13 | v1 >> 51;
v3 = v3 << 16 | v3 >> 48;
v1 ^= v0;
v3 ^= v2;
v0 = v0 << 32 | v0 >> 32;
v2 += v1;
v0 += v3;
v1 = v1 << 17 | v1 >> 47;
v3 = v3 << 21 | v3 >> 43;
v1 ^= v2;
v3 ^= v0;
v2 = v2 << 32 | v2 >> 32;
}
return (long)((v0 ^ v1) ^ (v2 ^ v3));
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c78b7a26485c4131ae8bcfae61e01435
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -125,11 +125,8 @@ public class MessagePackSecurityTests
Assert.NotNull(MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeUInt16Enum>());
Assert.NotNull(MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeInt32Enum>());
Assert.NotNull(MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeUInt32Enum>());
// Supporting enums with backing integers that exceed 32-bits would likely require Ref.Emit of new types
// since C# doesn't let us cast T to the underlying int type.
Assert.Throws<TypeAccessException>(() => MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeInt64Enum>());
Assert.Throws<TypeAccessException>(() => MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeUInt64Enum>());
Assert.NotNull(MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeInt64Enum>());
Assert.NotNull(MessagePackSecurity.UntrustedData.GetEqualityComparer<SomeUInt64Enum>());
}
[Fact]

Просмотреть файл

@ -0,0 +1,117 @@
// Copyright (c) All contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Linq;
using MessagePack;
using Xunit;
public class SipHashTests
{
// Test battery taken from: https://github.com/veorq/SipHash/blob/master/main.c
private static readonly uint[][] Vectors = new[]
{
new uint[] { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72 },
new uint[] { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74 },
new uint[] { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d },
new uint[] { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85 },
new uint[] { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf },
new uint[] { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18 },
new uint[] { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb },
new uint[] { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab },
new uint[] { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93 },
new uint[] { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e },
new uint[] { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a },
new uint[] { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4 },
new uint[] { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75 },
new uint[] { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14 },
new uint[] { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7 },
new uint[] { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1 },
new uint[] { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f },
new uint[] { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69 },
new uint[] { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b },
new uint[] { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb },
new uint[] { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe },
new uint[] { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0 },
new uint[] { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93 },
new uint[] { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8 },
new uint[] { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8 },
new uint[] { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc },
new uint[] { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17 },
new uint[] { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f },
new uint[] { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde },
new uint[] { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6 },
new uint[] { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad },
new uint[] { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32 },
new uint[] { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71 },
new uint[] { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7 },
new uint[] { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12 },
new uint[] { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15 },
new uint[] { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31 },
new uint[] { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02 },
new uint[] { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca },
new uint[] { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a },
new uint[] { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e },
new uint[] { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad },
new uint[] { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18 },
new uint[] { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4 },
new uint[] { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9 },
new uint[] { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9 },
new uint[] { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb },
new uint[] { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0 },
new uint[] { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6 },
new uint[] { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7 },
new uint[] { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee },
new uint[] { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1 },
new uint[] { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a },
new uint[] { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81 },
new uint[] { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f },
new uint[] { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24 },
new uint[] { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7 },
new uint[] { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea },
new uint[] { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60 },
new uint[] { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66 },
new uint[] { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c },
new uint[] { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f },
new uint[] { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5 },
new uint[] { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95 },
};
[Fact]
public void TestBattery_Aligned() => TestHelper(false);
[Fact]
public void TestBattery_Unaligned() => TestHelper(true);
private void TestHelper(bool unaligned)
{
// 128-bit key
byte[] key = Enumerable.Range(0, 16).Select(x => (byte)x).ToArray();
// SipHash initialized with the key
SipHash prf = new SipHash(key);
// Perform the test battery
Span<byte> message = new byte[Vectors.Length + 1];
if (unaligned)
{
message = message.Slice(1);
}
for (int i = 0; i < Vectors.Length; i++)
{
message[i] = (byte)i;
// Compute the tag
long actual = prf.Compute(message.Slice(0, i));
// Get the target tag
long expected = BitConverter.ToInt64(Vectors[i].Select(x => (byte)x).ToArray(), 0);
if (expected != actual)
{
throw new Exception($"Test vector failed for {i:N}-byte message!");
}
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c71df5af28304fe2991b6aaba359db90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -11,6 +11,7 @@
<ItemGroup>
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\Internal\FarmHash.cs" Link="Link\FarmHash.cs" />
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\Internal\ILGeneratorExtensions.cs" Link="Link\ILGeneratorExtensions.cs" />
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\SipHash.cs" Link="SipHash.cs" />
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\StringEncoding.cs" Link="Link\StringEncoding.cs" />
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\MessagePack\ThisLibraryExtensionTypeCodes.cs" Link="ExtensionTests\ThisLibraryExtensionTypeCodes.cs" />
<Compile Include="..\..\src\MessagePack.UnityClient\Assets\Scripts\Tests\ShareTests\**\*.cs" Exclude="..\..\src\MessagePack.UnityClient\Assets\Scripts\Tests\ShareTests\T4\**\*.cs" />