Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions System.Security.Cryptography/HMACSHA512.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//

using System.Runtime.CompilerServices;

namespace System.Security.Cryptography
{
/// <summary>
/// Computes a Hash-based Message Authentication Code (HMAC) by using the SHA512 hash function.
/// </summary>
public class HMACSHA512 : IDisposable
{
private const int BlockSize = 128;

private bool _disposed;
private byte[] _keyValue = null;
private byte[] _hashValue;

/// <summary>
/// Gets the value of the computed hash code.
/// </summary>
/// <value>The current value of the computed hash code.</value>
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
public byte[] Hash
{
get
{
if (_disposed)
{
throw new ObjectDisposedException();
}

return (byte[])_hashValue.Clone();
}
}

/// <summary>
/// Gets or sets the key to use in the HMAC calculation.
/// </summary>
/// <value>The key to use in the HMAC calculation.</value>
/// <remarks>
/// <para>
/// This property is the key for the keyed hash algorithm.
/// </para>
/// <para>
/// A Hash-based Message Authentication Code (HMAC) can be used to determine whether a message sent over an insecure channel has been tampered with, provided that the sender and receiver share a secret key. The sender computes the hash value for the original data and sends both the original data and the HMAC as a single message. The receiver recomputes the hash value on the received message and checks that the computed hash value matches the transmitted hash value.
/// </para>
/// </remarks>
public byte[] Key
{
get
{
return (byte[])_keyValue.Clone();
}

set
{
_keyValue = (byte[])value.Clone() ?? throw new ArgumentNullException();
}
}

/// <summary>
/// Initializes a new instance of the HMACSHA512 class with a randomly generated key.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="HMACSHA512"/> is a type of keyed hash algorithm that is constructed from the SHA-512 hash function and used as a Hash-based Message Authentication Code (HMAC). The HMAC process mixes a secret key with the message data, hashes the result with the hash function, mixes that hash value with the secret key again, and then applies the hash function a second time. The output hash is 512 bits in length.
/// </para>
/// <para>
/// This constructor uses a 128-byte, randomly generated key.
/// </para>
/// </remarks>
public HMACSHA512()
{
// Generate a random key with 128 bytes
Random generator = new();
_keyValue = new byte[BlockSize];
generator.NextBytes(_keyValue);
}

/// <summary>
/// Initializes a new instance of the <see cref="HMACSHA512"/> class with the specified key data.
/// </summary>
/// <param name="key">The secret key for HMAC computation. The key can be any length. However, the recommended size is 128 bytes. If the key is more than 128 bytes long, it is hashed (using SHA-512) to derive a 64-byte key.</param>
/// <exception cref="ArgumentNullException">The <paramref name="key"/> parameter is <see langword="null"/>.</exception>
/// <remarks>
/// <see cref="HMACSHA512"/> is a type of keyed hash algorithm that is constructed from the SHA-512 hash function and used as a Hash-based Message Authentication Code (HMAC). The HMAC process mixes a secret key with the message data, hashes the result with the hash function, mixes that hash value with the secret key again, and then applies the hash function a second time. The output hash is 512 bits in length.
/// </remarks>
public HMACSHA512(byte[] key)
{
_keyValue = key ?? throw new ArgumentNullException();
}

/// <summary>
/// Computes the hash value for the specified byte array.
/// </summary>
/// <param name="buffer">The input to compute the hash code for.</param>
/// <returns>The computed hash code.</returns>
/// <exception cref="ObjectDisposedException">The object has already been disposed.</exception>
/// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
public byte[] ComputeHash(byte[] buffer)
{
if (_disposed)
{
throw new ObjectDisposedException();
}

// Developer note: "buffer" parameter is checked for null by HashCore()

_hashValue = HashCore(_keyValue, buffer);

return (byte[])_hashValue.Clone();
}

/// <summary>
/// Computes the HMAC of data using the SHA512 algorithm.
/// </summary>
/// <param name="key">The HMAC key.</param>
/// <param name="source">The data to HMAC.</param>
/// <returns>The HMAC of the data.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="key" /> or <paramref name="source" /> is <see langword="null" />.
/// </exception>
public static byte[] HashData(
byte[] key,
byte[] source)
{
// Developer note: "key" and "source" parameters are checked for null by HashCore()

return HashCore(key, source);
}

/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <inheritdoc/>
protected void Dispose(bool disposing)
{
if (disposing)
{
if (_keyValue != null)
{
Array.Clear(_keyValue, 0, _keyValue.Length);
}

_keyValue = null;

// Although we don't have any resources to dispose at this level,
// we need to continue to throw ObjectDisposedExceptions from CalculateHash
// for compatibility with the .NET Framework.
_disposed = true;
}

return;
}

[MethodImpl(MethodImplOptions.InternalCall)]
extern private static byte[] HashCore(byte[] key, byte[] source);
}
}
2 changes: 1 addition & 1 deletion System.Security.Cryptography/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@

////////////////////////////////////////////////////////////////
// update this whenever the native assembly signature changes //
[assembly: AssemblyNativeVersion("100.0.0.3")]
[assembly: AssemblyNativeVersion("100.0.0.4")]
////////////////////////////////////////////////////////////////
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.props" Condition="Exists('..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.props')" />
<PropertyGroup Label="Globals">
Expand Down Expand Up @@ -46,6 +46,7 @@
<Compile Include="CipherMode.cs" />
<Compile Include="Aes.cs" />
<Compile Include="HMACSHA256.cs" />
<Compile Include="HMACSHA512.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand All @@ -71,4 +72,4 @@
<Error Condition="!Exists('..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.targets'))" />
</Target>
<Import Project="..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\packages\Nerdbank.GitVersioning.3.9.50\build\Nerdbank.GitVersioning.targets')" />
</Project>
</Project>
105 changes: 105 additions & 0 deletions Tests/System.Security.CryptographyTests/HmacSha512Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.
//

using nanoFramework.TestFramework;
using System.Collections;
using System.Security.Cryptography;

namespace System.Security.CryptographyTests
{
[TestClass]
public class HmacSha512Tests
{
// RFC 4231 test vectors for HMAC-SHA-512
static ArrayList keys = new ArrayList()
{
// Test case 1: 20-byte key
new byte[] { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b },
// Test case 2: "Jefe"
new byte[] { 0x4a, 0x65, 0x66, 0x65 },
// Test case 3: 20-byte key of 0xaa
new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
// Test case 4: 25-byte key
new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 },
// Test case 6: 131-byte key (larger than block size)
new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }
};

static ArrayList sources = new ArrayList()
{
// Test case 1: "Hi There"
new byte[] { 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 },
// Test case 2: "what do ya want for nothing?"
new byte[] { 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x3f },
// Test case 3: 50 bytes of 0xdd
new byte[] { 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd },
// Test case 4: 50 bytes of 0xcd
new byte[] { 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd },
// Test case 6: "Test Using Larger Than Block-Size Key - Hash Key First"
new byte[] { 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x72, 0x20, 0x54, 0x68, 0x61, 0x6e, 0x20, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2d, 0x53, 0x69, 0x7a, 0x65, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x2d, 0x20, 0x48, 0x61, 0x73, 0x68, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x46, 0x69, 0x72, 0x73, 0x74 },
};

// RFC 4231 expected HMAC-SHA-512 outputs
static ArrayList expectedHashes = new ArrayList()
{
// Test case 1
new byte[] { 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0, 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, 0x02, 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, 0xbe, 0x9d, 0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70, 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54 },
// Test case 2
new byte[] { 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99, 0x4a, 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd, 0xca, 0xea, 0xb1, 0xa3, 0x4d, 0x4a, 0x6b, 0x4b, 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37 },
// Test case 3
new byte[] { 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37, 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb },
// Test case 4
new byte[] { 0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7, 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, 0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, 0xa9, 0x1c, 0xa5, 0xc1, 0x1a, 0xa2, 0x5e, 0xb4, 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63, 0xa5, 0xf1, 0x97, 0x41, 0x12, 0x0c, 0x4f, 0x2d, 0xe2, 0xad, 0xeb, 0xeb, 0x10, 0xa2, 0x98, 0xdd },
// Test case 6
new byte[] { 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1, 0xdd, 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 0x12, 0x1b, 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25, 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 0x95, 0xe6, 0x4f, 0x73, 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98 },
};

[DataRow("RFC4231 Test case 1", 0, 0, 0)]
[DataRow("RFC4231 Test case 2", 1, 1, 1)]
[DataRow("RFC4231 Test case 3", 2, 2, 2)]
[DataRow("RFC4231 Test case 4", 3, 3, 3)]
[DataRow("RFC4231 Test case 6", 4, 4, 4)]
[TestMethod]
public void TestHMACSHA512Static(
string testCase,
int keyIndex,
int sourceIndex,
int hashIndex)
{
OutputHelper.WriteLine($"Test Case: {testCase}");

var hash = HMACSHA512.HashData(
(byte[])keys[keyIndex],
(byte[])sources[sourceIndex]);

CollectionAssert.AreEqual(
(byte[])expectedHashes[hashIndex],
hash);
}

[DataRow("RFC4231 Test case 1", 0, 0, 0)]
[DataRow("RFC4231 Test case 2", 1, 1, 1)]
[DataRow("RFC4231 Test case 3", 2, 2, 2)]
[DataRow("RFC4231 Test case 4", 3, 3, 3)]
[DataRow("RFC4231 Test case 6", 4, 4, 4)]
[TestMethod]
public void TestHMACSHA512Compute(
string testCase,
int keyIndex,
int sourceIndex,
int hashIndex)
{
OutputHelper.WriteLine($"Test Case: {testCase}");

HMACSHA512 hasher = new HMACSHA512((byte[])keys[keyIndex]);
hasher.ComputeHash((byte[])sources[sourceIndex]);

CollectionAssert.AreEqual(
(byte[])expectedHashes[hashIndex],
hasher.Hash);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<Compile Include="AesTests_CBC.cs" />
<Compile Include="AesTests_ECB.cs" />
<Compile Include="HmacSha256Tests.cs" />
<Compile Include="HmacSha512Tests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
10 changes: 4 additions & 6 deletions nanoFramework.System.Security.Cryptography.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@
This package requires a target with nanoFramework.System.Security.Cryptography v$nativeVersion$ (checksum $checksum$).</description>
<tags>nanoFramework C# csharp netmf netnf </tags>
<dependencies>
<dependency id="nanoFramework.CoreLibrary" version="1.17.11" />
<group targetFramework=".NETnanoFramework1.0">
<dependency id="nanoFramework.CoreLibrary" version="2.0.0-preview.35" />
</group>
</dependencies>
</metadata>
<files>
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.dll" target="lib\nanoFramework.System.Security.Cryptography.dll" />
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.pdb" target="lib\nanoFramework.System.Security.Cryptography.pdb" />
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.pdbx" target="lib\nanoFramework.System.Security.Cryptography.pdbx" />
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.pe" target="lib\nanoFramework.System.Security.Cryptography.pe" />
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.xml" target="lib\nanoFramework.System.Security.Cryptography.xml" />
<file src="System.Security.Cryptography\bin\Release\nanoFramework.System.Security.Cryptography.*" target="lib\netnano1.0" />
<!-- readme -->
<file src="assets\readme.txt" target="" />
<file src="README.md" target="docs\" />
Expand Down
Loading