Skip to content

Commit f13d853

Browse files
feature/v2
- Added HMAC implementation.
1 parent 924aa26 commit f13d853

16 files changed

+824
-15
lines changed

OnixLabs.Playground/Program.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ internal static class Program
2323
{
2424
private static void Main(string[] args)
2525
{
26-
byte[] key = Encoding.ASCII.GetBytes("Test");
27-
byte[] data = Encoding.ASCII.GetBytes("Hello, World!");
26+
Hmac hmac = Hmac.ComputeSha2Hmac256("Hello, World!", "Test key");
27+
Console.WriteLine(hmac.ToStringWithAlgorithmType());
2828

29-
using HMAC hmac = new HMACSHA256(key);
30-
byte[] result = hmac.ComputeHash(data);
29+
Hmac hmac2 = Hmac.Parse(hmac.ToStringWithAlgorithmType());
3130

32-
Console.WriteLine(Hash.FromByteArray(result));
31+
Console.WriteLine(hmac2.ToStringWithAlgorithmType());
32+
33+
Console.WriteLine(hmac == hmac2);
3334
}
3435
}
3536
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2020-2021 ONIXLabs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using Xunit;
16+
17+
namespace OnixLabs.Security.Cryptography.UnitTests
18+
{
19+
public sealed class HmacTests
20+
{
21+
[Fact(DisplayName = "Identical HMACs should be considered equal")]
22+
public void IdenticalHashesShouldBeConsideredEqual()
23+
{
24+
// Arrange
25+
Hmac a = Hmac.ComputeSha2Hmac256("abc", "key");
26+
Hmac b = Hmac.ComputeSha2Hmac256("abc", "key");
27+
28+
// Assert
29+
Assert.Equal(a, b);
30+
}
31+
32+
[Fact(DisplayName = "Different HMACs should not be considered equal (different data)")]
33+
public void DifferentHashesShouldNotBeConsideredEqualWithDifferentData()
34+
{
35+
// Arrange
36+
Hmac a = Hmac.ComputeSha2Hmac256("abc", "key");
37+
Hmac b = Hmac.ComputeSha2Hmac256("xyz", "key");
38+
39+
// Assert
40+
Assert.NotEqual(a, b);
41+
}
42+
43+
[Fact(DisplayName = "Different HMACs should not be considered equal (different keys)")]
44+
public void DifferentHashesShouldNotBeConsideredEqualWithDifferentKeys()
45+
{
46+
// Arrange
47+
Hmac a = Hmac.ComputeSha2Hmac256("abc", "key");
48+
Hmac b = Hmac.ComputeSha2Hmac256("abc", "123");
49+
50+
// Assert
51+
Assert.NotEqual(a, b);
52+
}
53+
54+
[Fact(DisplayName = "Parse should be able to parse a known hash")]
55+
public void ParseShouldBeAbleToParseAKnownHash()
56+
{
57+
// Arrange
58+
const string expected =
59+
"Sha2Hmac256:73ac6fa8599f4bde8dfee594c7f5f6ff03023b2d99ca71a7eccf729a8fc5c324:48656c6c6f2c20576f726c6421";
60+
61+
// Act
62+
Hmac hash = Hmac.Parse(expected);
63+
string actual = hash.ToStringWithAlgorithmType();
64+
65+
// Assert
66+
Assert.Equal(expected, actual);
67+
}
68+
}
69+
}

OnixLabs.Security.Cryptography/HashAlgorithmType.Enumerations.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,26 @@ public sealed partial class HashAlgorithmType
8282
/// <summary>
8383
/// The MD5 HMAC keyed hash algorithm.
8484
/// </summary>
85-
public static readonly HashAlgorithmType Md5HmacHash = new(12, nameof(Md5HmacHash), 16, true);
85+
public static readonly HashAlgorithmType Md5Hmac = new(12, nameof(Md5Hmac), 16, true);
8686

8787
/// <summary>
8888
/// The SHA-1 HMAC keyed hash algorithm.
8989
/// </summary>
90-
public static readonly HashAlgorithmType Sha1HmacHash = new(13, nameof(Sha1HmacHash), 20, true);
90+
public static readonly HashAlgorithmType Sha1Hmac = new(13, nameof(Sha1Hmac), 20, true);
9191

9292
/// <summary>
9393
/// The SHA-2 256-bit HMAC keyed hash algorithm.
9494
/// </summary>
95-
public static readonly HashAlgorithmType Sha2HmacHash256 = new(14, nameof(Sha2HmacHash256), 32, true);
95+
public static readonly HashAlgorithmType Sha2Hmac256 = new(14, nameof(Sha2Hmac256), 32, true);
9696

9797
/// <summary>
9898
/// The SHA-2 384-bit HMAC keyed hash algorithm.
9999
/// </summary>
100-
public static readonly HashAlgorithmType Sha2HmacHash384 = new(15, nameof(Sha2HmacHash384), 48, true);
100+
public static readonly HashAlgorithmType Sha2Hmac384 = new(15, nameof(Sha2Hmac384), 48, true);
101101

102102
/// <summary>
103103
/// The SHA-2 512-bit HMAC keyed hash algorithm.
104104
/// </summary>
105-
public static readonly HashAlgorithmType Sha2HmacHash512 = new(16, nameof(Sha2HmacHash512), 64, true);
105+
public static readonly HashAlgorithmType Sha2Hmac512 = new(16, nameof(Sha2Hmac512), 64, true);
106106
}
107107
}

OnixLabs.Security.Cryptography/HashAlgorithmType.Get.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,18 @@ public HashAlgorithm GetHashAlgorithm(int length = UnknownLength)
6161
/// <exception cref="ArgumentException">If the hash algorithm is unknown.</exception>
6262
public KeyedHashAlgorithm GetKeyedHashAlgorithm(byte[]? key = null)
6363
{
64+
if (!Keyed)
65+
{
66+
throw new ArgumentException($"Hash algorithm type '{Name}' is not a keyed hash algorithm.");
67+
}
68+
6469
return Name switch
6570
{
66-
nameof(Md5HmacHash) => key is null ? new HMACMD5() : new HMACMD5(key),
67-
nameof(Sha1HmacHash) => key is null ? new HMACSHA1() : new HMACSHA1(key),
68-
nameof(Sha2HmacHash256) => key is null ? new HMACSHA256() : new HMACSHA256(key),
69-
nameof(Sha2HmacHash384) => key is null ? new HMACSHA384() : new HMACSHA384(key),
70-
nameof(Sha2HmacHash512) => key is null ? new HMACSHA512() : new HMACSHA512(key),
71+
nameof(Md5Hmac) => key is null ? new HMACMD5() : new HMACMD5(key),
72+
nameof(Sha1Hmac) => key is null ? new HMACSHA1() : new HMACSHA1(key),
73+
nameof(Sha2Hmac256) => key is null ? new HMACSHA256() : new HMACSHA256(key),
74+
nameof(Sha2Hmac384) => key is null ? new HMACSHA384() : new HMACSHA384(key),
75+
nameof(Sha2Hmac512) => key is null ? new HMACSHA512() : new HMACSHA512(key),
7176
_ => throw new ArgumentException($"Hash algorithm '{Name}' is unknown.")
7277
};
7378
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2020-2021 ONIXLabs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Text;
16+
17+
namespace OnixLabs.Security.Cryptography
18+
{
19+
/// <summary>
20+
/// Represents a hashed message authentication code (HMAC).
21+
/// </summary>
22+
public readonly partial struct Hmac
23+
{
24+
/// <summary>
25+
/// Computes an MD5 HMAC from the specified value and key.
26+
/// This will use the default encoding to convert the input value and key into a byte array.
27+
/// </summary>
28+
/// <param name="value">The value for which to compute a HMAC.</param>
29+
/// <param name="key">The key for which to compute a HMAC.</param>
30+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
31+
public static Hmac ComputeMd5Hmac(string value, string key)
32+
{
33+
return ComputeMd5Hmac(value, key, Encoding.Default);
34+
}
35+
36+
/// <summary>
37+
/// Computes an MD5 HMAC from the specified value and key.
38+
/// </summary>
39+
/// <param name="value">The value for which to compute a HMAC.</param>
40+
/// <param name="key">The key for which to compute a HMAC.</param>
41+
/// <param name="encoding">The encoding which will be used to convert the value and key into a byte array.</param>
42+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
43+
public static Hmac ComputeMd5Hmac(string value, string key, Encoding encoding)
44+
{
45+
byte[] valueBytes = encoding.GetBytes(value);
46+
byte[] keyBytes = encoding.GetBytes(key);
47+
48+
return ComputeMd5Hmac(valueBytes, keyBytes);
49+
}
50+
51+
/// <summary>
52+
/// Computes an MD5 HMAC from the specified value and key.
53+
/// </summary>
54+
/// <param name="value">The value for which to compute a HMAC.</param>
55+
/// <param name="key">The key for which to compute a HMAC.</param>
56+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
57+
public static Hmac ComputeMd5Hmac(byte[] value, byte[] key)
58+
{
59+
return ComputeHmac(value, key, HashAlgorithmType.Md5Hmac);
60+
}
61+
}
62+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2020-2021 ONIXLabs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Text;
16+
17+
namespace OnixLabs.Security.Cryptography
18+
{
19+
/// <summary>
20+
/// Represents a hashed message authentication code (HMAC).
21+
/// </summary>
22+
public readonly partial struct Hmac
23+
{
24+
/// <summary>
25+
/// Computes a SHA-1 HMAC from the specified value and key.
26+
/// This will use the default encoding to convert the input value and key into a byte array.
27+
/// </summary>
28+
/// <param name="value">The value for which to compute a HMAC.</param>
29+
/// <param name="key">The key for which to compute a HMAC.</param>
30+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
31+
public static Hmac ComputeSha1Hmac(string value, string key)
32+
{
33+
return ComputeSha1Hmac(value, key, Encoding.Default);
34+
}
35+
36+
/// <summary>
37+
/// Computes a SHA-1 HMAC from the specified value and key.
38+
/// </summary>
39+
/// <param name="value">The value for which to compute a HMAC.</param>
40+
/// <param name="key">The key for which to compute a HMAC.</param>
41+
/// <param name="encoding">The encoding which will be used to convert the value and key into a byte array.</param>
42+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
43+
public static Hmac ComputeSha1Hmac(string value, string key, Encoding encoding)
44+
{
45+
byte[] valueBytes = encoding.GetBytes(value);
46+
byte[] keyBytes = encoding.GetBytes(key);
47+
48+
return ComputeSha1Hmac(valueBytes, keyBytes);
49+
}
50+
51+
/// <summary>
52+
/// Computes a SHA-1 HMAC from the specified value and key.
53+
/// </summary>
54+
/// <param name="value">The value for which to compute a HMAC.</param>
55+
/// <param name="key">The key for which to compute a HMAC.</param>
56+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
57+
public static Hmac ComputeSha1Hmac(byte[] value, byte[] key)
58+
{
59+
return ComputeHmac(value, key, HashAlgorithmType.Sha1Hmac);
60+
}
61+
}
62+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2020-2021 ONIXLabs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Text;
16+
17+
namespace OnixLabs.Security.Cryptography
18+
{
19+
/// <summary>
20+
/// Represents a hashed message authentication code (HMAC).
21+
/// </summary>
22+
public readonly partial struct Hmac
23+
{
24+
/// <summary>
25+
/// Computes a SHA-2 256-bit HMAC from the specified value and key.
26+
/// This will use the default encoding to convert the input value and key into a byte array.
27+
/// </summary>
28+
/// <param name="value">The value for which to compute a HMAC.</param>
29+
/// <param name="key">The key for which to compute a HMAC.</param>
30+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
31+
public static Hmac ComputeSha2Hmac256(string value, string key)
32+
{
33+
return ComputeSha2Hmac256(value, key, Encoding.Default);
34+
}
35+
36+
/// <summary>
37+
/// Computes a SHA-2 256-bit HMAC from the specified value and key.
38+
/// </summary>
39+
/// <param name="value">The value for which to compute a HMAC.</param>
40+
/// <param name="key">The key for which to compute a HMAC.</param>
41+
/// <param name="encoding">The encoding which will be used to convert the value and key into a byte array.</param>
42+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
43+
public static Hmac ComputeSha2Hmac256(string value, string key, Encoding encoding)
44+
{
45+
byte[] valueBytes = encoding.GetBytes(value);
46+
byte[] keyBytes = encoding.GetBytes(key);
47+
48+
return ComputeSha2Hmac256(valueBytes, keyBytes);
49+
}
50+
51+
/// <summary>
52+
/// Computes a SHA-2 256-bit HMAC from the specified value and key.
53+
/// </summary>
54+
/// <param name="value">The value for which to compute a HMAC.</param>
55+
/// <param name="key">The key for which to compute a HMAC.</param>
56+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
57+
public static Hmac ComputeSha2Hmac256(byte[] value, byte[] key)
58+
{
59+
return ComputeHmac(value, key, HashAlgorithmType.Sha2Hmac256);
60+
}
61+
}
62+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2020-2021 ONIXLabs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Text;
16+
17+
namespace OnixLabs.Security.Cryptography
18+
{
19+
/// <summary>
20+
/// Represents a hashed message authentication code (HMAC).
21+
/// </summary>
22+
public readonly partial struct Hmac
23+
{
24+
/// <summary>
25+
/// Computes a SHA-2 384-bit HMAC from the specified value and key.
26+
/// This will use the default encoding to convert the input value and key into a byte array.
27+
/// </summary>
28+
/// <param name="value">The value for which to compute a HMAC.</param>
29+
/// <param name="key">The key for which to compute a HMAC.</param>
30+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
31+
public static Hmac ComputeSha2Hmac384(string value, string key)
32+
{
33+
return ComputeSha2Hmac384(value, key, Encoding.Default);
34+
}
35+
36+
/// <summary>
37+
/// Computes a SHA-2 384-bit HMAC from the specified value and key.
38+
/// </summary>
39+
/// <param name="value">The value for which to compute a HMAC.</param>
40+
/// <param name="key">The key for which to compute a HMAC.</param>
41+
/// <param name="encoding">The encoding which will be used to convert the value and key into a byte array.</param>
42+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
43+
public static Hmac ComputeSha2Hmac384(string value, string key, Encoding encoding)
44+
{
45+
byte[] valueBytes = encoding.GetBytes(value);
46+
byte[] keyBytes = encoding.GetBytes(key);
47+
48+
return ComputeSha2Hmac384(valueBytes, keyBytes);
49+
}
50+
51+
/// <summary>
52+
/// Computes a SHA-2 384-bit HMAC from the specified value and key.
53+
/// </summary>
54+
/// <param name="value">The value for which to compute a HMAC.</param>
55+
/// <param name="key">The key for which to compute a HMAC.</param>
56+
/// <returns>Returns a <see cref="Hmac"/> of the input value and key.</returns>
57+
public static Hmac ComputeSha2Hmac384(byte[] value, byte[] key)
58+
{
59+
return ComputeHmac(value, key, HashAlgorithmType.Sha2Hmac384);
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)