-
Notifications
You must be signed in to change notification settings - Fork 53
/
Sha512.cs
94 lines (84 loc) · 3.25 KB
/
Sha512.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using static Interop.Libsodium;
namespace NSec.Cryptography
{
//
// SHA-512
//
// FIPS Secure Hash Algorithm (SHA) with a 512-bit message digest
//
// References:
//
// RFC 6234 - US Secure Hash Algorithms (SHA and SHA-based HMAC and
// HKDF)
//
// RFC 5754 - Using SHA2 Algorithms with Cryptographic Message Syntax
//
// Parameters:
//
// Input Size - Between 0 and 2^125-1 bytes. (A Span<byte> can hold
// only up to 2^31-1 bytes.)
//
// Hash Size - 64 bytes (256 bits of security). The output can be
// truncated to 32 bytes (128 bits of security). Note that SHA-512
// truncated to 32 bytes/256 bits is not the same as SHA-512/256,
// which uses a different initial hash value.
//
public sealed class Sha512 : HashAlgorithm
{
private static readonly Oid s_oid = new Oid(2, 16, 840, 1, 101, 3, 4, 2, 3);
private static readonly Lazy<bool> s_selfTest = new Lazy<bool>(new Func<bool>(SelfTest));
public Sha512() : base(
minHashSize: crypto_hash_sha512_BYTES / 2,
defaultHashSize: crypto_hash_sha512_BYTES,
maxHashSize: crypto_hash_sha512_BYTES)
{
if (!s_selfTest.Value)
throw Error.Cryptographic_InitializationFailed();
}
internal override void HashCore(
ReadOnlySpan<byte> data,
Span<byte> hash)
{
Debug.Assert(hash.Length >= crypto_hash_sha512_BYTES / 2);
Debug.Assert(hash.Length <= crypto_hash_sha512_BYTES);
crypto_hash_sha512_init(out crypto_hash_sha512_state state);
if (!data.IsEmpty)
{
crypto_hash_sha512_update(ref state, ref data.DangerousGetPinnableReference(), (ulong)data.Length);
}
// crypto_hash_sha512_final expects an output buffer with a
// size of exactly crypto_hash_sha512_BYTES, so we need to
// copy when a truncated output is requested.
if (hash.Length == crypto_hash_sha512_BYTES)
{
crypto_hash_sha512_final(ref state, ref hash.DangerousGetPinnableReference());
}
else
{
Span<byte> temp;
try
{
unsafe
{
byte* pointer = stackalloc byte[crypto_hash_sha512_BYTES];
temp = new Span<byte>(pointer, crypto_hash_sha512_BYTES);
}
crypto_hash_sha512_final(ref state, ref temp.DangerousGetPinnableReference());
temp.Slice(0, hash.Length).CopyTo(hash);
}
finally
{
sodium_memzero(ref temp.DangerousGetPinnableReference(), (UIntPtr)temp.Length);
}
}
}
private static bool SelfTest()
{
return (crypto_hash_sha512_bytes() == (UIntPtr)crypto_hash_sha512_BYTES)
&& (crypto_hash_sha512_statebytes() == (UIntPtr)Unsafe.SizeOf<crypto_hash_sha512_state>());
}
}
}