| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using Renci.SshNet.Security.Chaos.NaCl.Internal; |
| | | 4 | | |
| | | 5 | | namespace Renci.SshNet.Security.Chaos.NaCl |
| | | 6 | | { |
| | | 7 | | internal class Sha512 |
| | | 8 | | { |
| | | 9 | | private Array8<UInt64> _state; |
| | | 10 | | private readonly byte[] _buffer; |
| | | 11 | | private ulong _totalBytes; |
| | | 12 | | public const int BlockSize = 128; |
| | 4 | 13 | | private static readonly byte[] _padding = new byte[] { 0x80 }; |
| | | 14 | | |
| | 11 | 15 | | public Sha512() |
| | 11 | 16 | | { |
| | 11 | 17 | | _buffer = new byte[BlockSize];//todo: remove allocation |
| | 11 | 18 | | Init(); |
| | 11 | 19 | | } |
| | | 20 | | |
| | | 21 | | public void Init() |
| | 13 | 22 | | { |
| | 13 | 23 | | Sha512Internal.Sha512Init(out _state); |
| | 13 | 24 | | _totalBytes = 0; |
| | 13 | 25 | | } |
| | | 26 | | |
| | | 27 | | public void Update(ArraySegment<byte> data) |
| | 0 | 28 | | { |
| | 0 | 29 | | if (data.Array == null) |
| | 0 | 30 | | throw new ArgumentNullException("data.Array"); |
| | 0 | 31 | | Update(data.Array, data.Offset, data.Count); |
| | 0 | 32 | | } |
| | | 33 | | |
| | | 34 | | public void Update(byte[] data, int offset, int count) |
| | 35 | 35 | | { |
| | 35 | 36 | | if (data == null) |
| | 0 | 37 | | throw new ArgumentNullException("data"); |
| | 35 | 38 | | if (offset < 0) |
| | 0 | 39 | | throw new ArgumentOutOfRangeException("offset"); |
| | 35 | 40 | | if (count < 0) |
| | 0 | 41 | | throw new ArgumentOutOfRangeException("count"); |
| | 35 | 42 | | if (data.Length - offset < count) |
| | 0 | 43 | | throw new ArgumentException("Requires offset + count <= data.Length"); |
| | | 44 | | |
| | | 45 | | Array16<ulong> block; |
| | 35 | 46 | | int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); |
| | 35 | 47 | | _totalBytes += (uint)count; |
| | | 48 | | |
| | 35 | 49 | | if (_totalBytes >= ulong.MaxValue / 8) |
| | 0 | 50 | | throw new InvalidOperationException("Too much data"); |
| | | 51 | | // Fill existing buffer |
| | 35 | 52 | | if (bytesInBuffer != 0) |
| | 22 | 53 | | { |
| | 22 | 54 | | var toCopy = Math.Min(BlockSize - bytesInBuffer, count); |
| | 22 | 55 | | Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, toCopy); |
| | 22 | 56 | | offset += toCopy; |
| | 22 | 57 | | count -= toCopy; |
| | 22 | 58 | | bytesInBuffer += toCopy; |
| | 22 | 59 | | if (bytesInBuffer == BlockSize) |
| | 2 | 60 | | { |
| | 2 | 61 | | ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); |
| | 2 | 62 | | Sha512Internal.Core(out _state, ref _state, ref block); |
| | 2 | 63 | | CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); |
| | 2 | 64 | | bytesInBuffer = 0; |
| | 2 | 65 | | } |
| | 22 | 66 | | } |
| | | 67 | | // Hash complete blocks without copying |
| | 35 | 68 | | while (count >= BlockSize) |
| | 0 | 69 | | { |
| | 0 | 70 | | ByteIntegerConverter.Array16LoadBigEndian64(out block, data, offset); |
| | 0 | 71 | | Sha512Internal.Core(out _state, ref _state, ref block); |
| | 0 | 72 | | offset += BlockSize; |
| | 0 | 73 | | count -= BlockSize; |
| | 0 | 74 | | } |
| | | 75 | | // Copy remainder into buffer |
| | 35 | 76 | | if (count > 0) |
| | 15 | 77 | | { |
| | 15 | 78 | | Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, count); |
| | 15 | 79 | | } |
| | 35 | 80 | | } |
| | | 81 | | |
| | | 82 | | public void Finish(ArraySegment<byte> output) |
| | 13 | 83 | | { |
| | 13 | 84 | | if (output.Array == null) |
| | 0 | 85 | | throw new ArgumentNullException("output.Array"); |
| | 13 | 86 | | if (output.Count != 64) |
| | 0 | 87 | | throw new ArgumentException("output.Count must be 64"); |
| | | 88 | | |
| | 13 | 89 | | Update(_padding, 0, _padding.Length); |
| | | 90 | | Array16<ulong> block; |
| | 13 | 91 | | ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); |
| | 13 | 92 | | CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); |
| | 13 | 93 | | int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); |
| | 13 | 94 | | if (bytesInBuffer > BlockSize - 16) |
| | 0 | 95 | | { |
| | 0 | 96 | | Sha512Internal.Core(out _state, ref _state, ref block); |
| | 0 | 97 | | block = default(Array16<ulong>); |
| | 0 | 98 | | } |
| | 13 | 99 | | block.x15 = (_totalBytes - 1) * 8; |
| | 13 | 100 | | Sha512Internal.Core(out _state, ref _state, ref block); |
| | | 101 | | |
| | 13 | 102 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 0, _state.x0); |
| | 13 | 103 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 8, _state.x1); |
| | 13 | 104 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 16, _state.x2); |
| | 13 | 105 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 24, _state.x3); |
| | 13 | 106 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 32, _state.x4); |
| | 13 | 107 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 40, _state.x5); |
| | 13 | 108 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 48, _state.x6); |
| | 13 | 109 | | ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 56, _state.x7); |
| | 13 | 110 | | _state = default(Array8<ulong>); |
| | 13 | 111 | | } |
| | | 112 | | |
| | | 113 | | public byte[] Finish() |
| | 13 | 114 | | { |
| | 13 | 115 | | var result = new byte[64]; |
| | 13 | 116 | | Finish(new ArraySegment<byte>(result)); |
| | 13 | 117 | | return result; |
| | 13 | 118 | | } |
| | | 119 | | |
| | | 120 | | internal static byte[] Hash(byte[] data) |
| | 0 | 121 | | { |
| | 0 | 122 | | return Hash(data, 0, data.Length); |
| | 0 | 123 | | } |
| | | 124 | | |
| | | 125 | | internal static byte[] Hash(byte[] data, int offset, int count) |
| | 7 | 126 | | { |
| | 7 | 127 | | var hasher = new Sha512(); |
| | 7 | 128 | | hasher.Update(data, offset, count); |
| | 7 | 129 | | return hasher.Finish(); |
| | 7 | 130 | | } |
| | | 131 | | } |
| | | 132 | | } |