Skip to content

Commit

Permalink
added new method to convert byte array to lower case hex-string
Browse files Browse the repository at this point in the history
added unit test

update ToHex(byte[]) to support mono

added punctuations to unit test summary and parameter description

Replaced with Convert.ToHexString(), public ToHex() + use from Color.ToString()

Adjusted back to a simpler mono compatible version only, with lowered allocation
  • Loading branch information
rudzen committed Sep 22, 2023
1 parent 541d531 commit 765b50e
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 6 deletions.
36 changes: 32 additions & 4 deletions OpenRA.Game/CryptoUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public static class CryptoUtil
// Fixed byte pattern for the OID header
static readonly byte[] OIDHeader = { 0x30, 0xD, 0x6, 0x9, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0 };

static readonly char[] HexUpperAlphabet = "0123456789ABCDEF".ToArray();
static readonly char[] HexLowerAlphabet = "0123456789abcdef".ToArray();

public static string PublicKeyFingerprint(RSAParameters parameters)
{
// Public key fingerprint is defined as the SHA1 of the modulus + exponent bytes
Expand Down Expand Up @@ -249,19 +252,44 @@ public static bool VerifySignature(RSAParameters parameters, byte[] data, string

public static string SHA1Hash(Stream data)
{
using (var csp = SHA1.Create())
return new string(csp.ComputeHash(data).SelectMany(a => a.ToStringInvariant("x2")).ToArray());
using var csp = SHA1.Create();
return ToHex(csp.ComputeHash(data), true);
}

public static string SHA1Hash(byte[] data)
{
using (var csp = SHA1.Create())
return new string(csp.ComputeHash(data).SelectMany(a => a.ToStringInvariant("x2")).ToArray());
using var csp = SHA1.Create();
return ToHex(csp.ComputeHash(data), true);
}

public static string SHA1Hash(string data)
{
return SHA1Hash(Encoding.UTF8.GetBytes(data));
}

public static string ToHex(ReadOnlySpan<byte> source, bool lowerCase = false)
{
if (source.Length == 0)
return string.Empty;

// excessively avoid stack overflow if source is too large (considering that we're allocating a new string)
var buffer = source.Length <= 256 ? stackalloc char[source.Length * 2] : new char[source.Length * 2];
return ToHexInternal(source, buffer, lowerCase);
}

static string ToHexInternal(ReadOnlySpan<byte> source, Span<char> buffer, bool lowerCase)
{
var sourceIndex = 0;
var alphabet = lowerCase ? HexLowerAlphabet : HexUpperAlphabet;

for (var i = 0; i < buffer.Length; i += 2)
{
var b = source[sourceIndex++];
buffer[i] = alphabet[b >> 4];
buffer[i + 1] = alphabet[b & 0xF];
}

return new string(buffer);
}
}
}
4 changes: 2 additions & 2 deletions OpenRA.Game/Primitives/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,9 @@ public override int GetHashCode()
public override string ToString()
{
if (A == 255)
return R.ToStringInvariant("X2") + G.ToStringInvariant("X2") + B.ToStringInvariant("X2");
return CryptoUtil.ToHex(stackalloc byte[3] { R, G, B });

return R.ToStringInvariant("X2") + G.ToStringInvariant("X2") + B.ToStringInvariant("X2") + A.ToStringInvariant("X2");
return CryptoUtil.ToHex(stackalloc byte[4] { R, G, B, A });
}

public static Color Transparent => FromArgb(0x00FFFFFF);
Expand Down
35 changes: 35 additions & 0 deletions OpenRA.Test/OpenRA.Game/Sha1Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using NUnit.Framework;
using OpenRA.Primitives;

namespace OpenRA.Test
{
[TestFixture]
sealed class Sha1Tests
{
/// <summary>
/// https://en.wikipedia.org/wiki/SHA-1#Examples_and_pseudocode.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="expected">The expected hex string of the SHA1.</param>
[TestCase("The quick brown fox jumps over the lazy dog", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")]
[TestCase("The quick brown fox jumps over the lazy cog", "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3")]
[TestCase("", "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
public void Sha1HexConvert(string input, string expected)
{
var actual = CryptoUtil.SHA1Hash(input);

Assert.AreEqual(expected, actual);
}

[TestCase(0xFF0000FF, "0000FF")]
[TestCase(0xFF00FFFF, "00FFFF")]
[TestCase(0xFFFF00FF, "FF00FF")]
[TestCase(0xAAFF00FF, "FF00FFAA")]
public void ColorsToHex(uint value, string expected)
{
var color = Color.FromArgb(value);
var actual = color.ToString();
Assert.AreEqual(expected, actual);
}
}
}

0 comments on commit 765b50e

Please sign in to comment.