<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />

Blake2xsDigest

public sealed class Blake2xsDigest : IXof, IDigest
using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Digests { public sealed class Blake2xsDigest : IXof, IDigest { public const int UnknownDigestLength = 65535; private const int DigestLength = 32; private const long MaxNumberBlocks = 4294967296; private int digestLength; private Blake2sDigest hash; private byte[] h0; private byte[] buf = new byte[32]; private int bufPos = 32; private int digestPos; private long blockPos; private long nodeOffset; public string AlgorithmName => "BLAKE2xs"; public Blake2xsDigest() : this(65535) { } public Blake2xsDigest(int digestBytes) : this(digestBytes, null, null, null) { } public Blake2xsDigest(int digestBytes, byte[] key) : this(digestBytes, key, null, null) { } public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization) { if (digestBytes < 1 || digestBytes > 65535) throw new ArgumentException("BLAKE2xs digest length must be between 1 and 2^16-1"); digestLength = digestBytes; nodeOffset = ComputeNodeOffset(); hash = new Blake2sDigest(32, key, salt, personalization, nodeOffset); } public Blake2xsDigest(Blake2xsDigest digest) { digestLength = digest.digestLength; hash = new Blake2sDigest(digest.hash); h0 = Arrays.Clone(digest.h0); buf = Arrays.Clone(digest.buf); bufPos = digest.bufPos; digestPos = digest.digestPos; blockPos = digest.blockPos; nodeOffset = digest.nodeOffset; } public int GetDigestSize() { return digestLength; } public int GetByteLength() { return hash.GetByteLength(); } public long GetUnknownMaxLength() { return 137438953472; } public void Update(byte b) { hash.Update(b); } public void BlockUpdate(byte[] input, int inOff, int inLen) { hash.BlockUpdate(input, inOff, inLen); } public void Reset() { hash.Reset(); h0 = null; bufPos = 32; digestPos = 0; blockPos = 0; nodeOffset = ComputeNodeOffset(); } public int DoFinal(byte[] output, int outOff) { return OutputFinal(output, outOff, digestLength); } public int OutputFinal(byte[] output, int outOff, int outLen) { int result = Output(output, outOff, outLen); Reset(); return result; } public int Output(byte[] output, int outOff, int outLen) { Check.OutputLength(output, outOff, outLen, "output buffer too short"); if (h0 == null) { h0 = new byte[hash.GetDigestSize()]; hash.DoFinal(h0, 0); } if (digestLength != 65535) { if (digestPos + outLen > digestLength) throw new ArgumentException("Output length is above the digest length"); } else if (blockPos << 5 >= GetUnknownMaxLength()) { throw new ArgumentException("Maximum length is 2^32 blocks of 32 bytes"); } for (int i = 0; i < outLen; i++) { if (bufPos >= 32) { Blake2sDigest blake2sDigest = new Blake2sDigest(ComputeStepLength(), 32, nodeOffset); blake2sDigest.BlockUpdate(h0, 0, h0.Length); Arrays.Fill(buf, 0); blake2sDigest.DoFinal(buf, 0); bufPos = 0; nodeOffset++; blockPos++; } output[outOff + i] = buf[bufPos]; bufPos++; digestPos++; } return outLen; } private int ComputeStepLength() { if (digestLength == 65535) return 32; return System.Math.Min(32, digestLength - digestPos); } private long ComputeNodeOffset() { return digestLength * 4294967296; } } }