Blake2xsDigest
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 BlockUpdate(ReadOnlySpan<byte> input)
{
hash.BlockUpdate(input);
}
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");
return Output(output.AsSpan(outOff, outLen));
}
public int DoFinal(Span<byte> output)
{
Check.OutputLength(output, digestLength, "output buffer too short");
return OutputFinal(output.Slice(0, digestLength));
}
public int OutputFinal(Span<byte> output)
{
int result = Output(output);
Reset();
return result;
}
public int Output(Span<byte> output)
{
int length = output.Length;
if (h0 == null) {
h0 = new byte[hash.GetDigestSize()];
hash.DoFinal(h0);
}
if (digestLength != 65535) {
if (digestPos + length > 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 < length; i++) {
if (bufPos >= 32) {
Blake2sDigest blake2sDigest = new Blake2sDigest(ComputeStepLength(), 32, nodeOffset);
blake2sDigest.BlockUpdate(h0);
Arrays.Fill(buf, 0);
blake2sDigest.DoFinal(buf);
bufPos = 0;
nodeOffset++;
blockPos++;
}
output[i] = buf[bufPos];
bufPos++;
digestPos++;
}
return length;
}
private int ComputeStepLength()
{
if (digestLength == 65535)
return 32;
return System.Math.Min(32, digestLength - digestPos);
}
private long ComputeNodeOffset()
{
return digestLength * 4294967296;
}
}
}