using System; using System.Collections.Generic; using Framework.Cryptography.Ed25519.Internal; using System.Diagnostics.Contracts; namespace Framework.Cryptography.Ed25519 { public class Sha512 { private Array8 _state; private readonly byte[] _buffer; private ulong _totalBytes; public const int BlockSize = 128; private static readonly byte[] _padding = new byte[] { 0x80 }; /// /// Allocation and initialization of the new SHA-512 object. /// public Sha512() { _buffer = new byte[BlockSize];//todo: remove allocation Init(); } /// /// Performs an initialization of internal SHA-512 state. /// public void Init() { Sha512Internal.Sha512Init(out _state); _totalBytes = 0; } /// /// Updates internal state with data from the provided array segment. /// /// Array segment public void Update(ArraySegment data) { Update(data.Array, data.Offset, data.Count); } /// /// Updates internal state with data from the provided array. /// /// Array of bytes /// Offset of byte sequence /// Sequence length public void Update(byte[] data, int index, int length) { Array16 block; int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); _totalBytes += (uint)length; if (_totalBytes >= ulong.MaxValue / 8) throw new InvalidOperationException("Too much data"); // Fill existing buffer if (bytesInBuffer != 0) { var toCopy = Math.Min(BlockSize - bytesInBuffer, length); Buffer.BlockCopy(data, index, _buffer, bytesInBuffer, toCopy); index += toCopy; length -= toCopy; bytesInBuffer += toCopy; if (bytesInBuffer == BlockSize) { ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); Sha512Internal.Core(out _state, ref _state, ref block); CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); bytesInBuffer = 0; } } // Hash complete blocks without copying while (length >= BlockSize) { ByteIntegerConverter.Array16LoadBigEndian64(out block, data, index); Sha512Internal.Core(out _state, ref _state, ref block); index += BlockSize; length -= BlockSize; } // Copy remainder into buffer if (length > 0) { Buffer.BlockCopy(data, index, _buffer, bytesInBuffer, length); } } /// /// Finalizes SHA-512 hashing /// /// Output buffer public void Finalize(ArraySegment output) { Update(_padding, 0, _padding.Length); Array16 block; ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); if (bytesInBuffer > BlockSize - 16) { Sha512Internal.Core(out _state, ref _state, ref block); block = default(Array16); } block.x15 = (_totalBytes - 1) * 8; Sha512Internal.Core(out _state, ref _state, ref block); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 0, _state.x0); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 8, _state.x1); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 16, _state.x2); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 24, _state.x3); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 32, _state.x4); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 40, _state.x5); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 48, _state.x6); ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 56, _state.x7); _state = default(Array8); } /// /// Finalizes SHA-512 hashing. /// /// Hash bytes public byte[] Finalize() { var result = new byte[64]; Finalize(new ArraySegment(result)); return result; } /// /// Calculates SHA-512 hash value for the given bytes array. /// /// Data bytes array /// Hash bytes public static byte[] Hash(byte[] data) { return Hash(data, 0, data.Length); } /// /// Calculates SHA-512 hash value for the given bytes array. /// /// Data bytes array /// Offset of byte sequence /// Sequence length /// Hash bytes public static byte[] Hash(byte[] data, int index, int length) { var hasher = new Sha512(); hasher.Update(data, index, length); return hasher.Finalize(); } } }