123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- #pragma warning disable
- using System;
- using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
- namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests
- {
- /*
- The BLAKE2 cryptographic hash function was designed by Jean-
- Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn, and Christian
- Winnerlein.
- Reference Implementation and Description can be found at: https://blake2.net/blake2x.pdf
- */
- /**
- * Implementation of the eXtendable Output Function (XOF) BLAKE2xs.
- * <p/>
- * BLAKE2xs offers a built-in keying mechanism to be used directly
- * for authentication ("Prefix-MAC") rather than a HMAC construction.
- * <p/>
- * BLAKE2xs offers a built-in support for a salt for randomized hashing
- * and a personal string for defining a unique hash function for each application.
- * <p/>
- * BLAKE2xs is optimized for 32-bit platforms and produces digests of any size
- * between 1 and 2^16-2 bytes. The length can also be unknown and then the maximum
- * length will be 2^32 blocks of 32 bytes.
- */
- public sealed class Blake2xsDigest
- : IXof
- {
- /**
- * Magic number to indicate an unknown length of digest
- */
- public const int UnknownDigestLength = 65535;
- private const int DigestLength = 32;
- private const long MaxNumberBlocks = 1L << 32;
- /**
- * Expected digest length for the xof. It can be unknown.
- */
- private int digestLength;
- /**
- * Root hash that will take the updates
- */
- private Blake2sDigest hash;
- /**
- * Digest of the root hash
- */
- private byte[] h0 = null;
- /**
- * Digest of each round of the XOF
- */
- private byte[] buf = new byte[32];
- /**
- * Current position for a round
- */
- private int bufPos = 32;
- /**
- * Overall position of the digest. It is useful when the length is known
- * in advance to get last block length.
- */
- private int digestPos = 0;
- /**
- * Keep track of the round number to detect the end of the digest after
- * 2^32 blocks of 32 bytes.
- */
- private long blockPos = 0;
- /**
- * Current node offset incremented by 1 every round.
- */
- private long nodeOffset;
- /**
- * BLAKE2xs for hashing with unknown digest length
- */
- public Blake2xsDigest()
- : this(UnknownDigestLength)
- {
- }
- /**
- * BLAKE2xs for hashing
- *
- * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
- */
- public Blake2xsDigest(int digestBytes)
- : this(digestBytes, null, null, null)
- {
- }
- /**
- * BLAKE2xs with key
- *
- * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
- * @param key A key up to 32 bytes or null
- */
- public Blake2xsDigest(int digestBytes, byte[] key)
- : this(digestBytes, key, null, null)
- {
- }
- /**
- * BLAKE2xs with key, salt and personalization
- *
- * @param digestBytes The desired digest length in bytes. Must be above 1 and less than 2^16-1
- * @param key A key up to 32 bytes or null
- * @param salt 8 bytes or null
- * @param personalization 8 bytes or null
- */
- public Blake2xsDigest(int digestBytes, byte[] key, byte[] salt, byte[] personalization)
- {
- if (digestBytes < 1 || digestBytes > UnknownDigestLength)
- throw new ArgumentException("BLAKE2xs digest length must be between 1 and 2^16-1");
- digestLength = digestBytes;
- nodeOffset = ComputeNodeOffset();
- hash = new Blake2sDigest(DigestLength, 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;
- }
- /**
- * Return the algorithm name.
- *
- * @return the algorithm name
- */
- public string AlgorithmName => "BLAKE2xs";
- /**
- * Return the size in bytes of the digest produced by this message digest.
- *
- * @return the size in bytes of the digest produced by this message digest.
- */
- public int GetDigestSize() => digestLength;
- /**
- * Return the size in bytes of the internal buffer the digest applies its
- * compression function to.
- *
- * @return byte length of the digest's internal buffer.
- */
- public int GetByteLength() => hash.GetByteLength();
- /**
- * Return the maximum size in bytes the digest can produce when the length
- * is unknown
- *
- * @return byte length of the largest digest with unknown length
- */
- public long GetUnknownMaxLength()
- {
- return MaxNumberBlocks * DigestLength;
- }
- /**
- * Update the message digest with a single byte.
- *
- * @param in the input byte to be entered.
- */
- public void Update(byte b)
- {
- hash.Update(b);
- }
- /**
- * Update the message digest with a block of bytes.
- *
- * @param in the byte array containing the data.
- * @param inOff the offset into the byte array where the data starts.
- * @param len the length of the data.
- */
- public void BlockUpdate(byte[] input, int inOff, int inLen)
- {
- hash.BlockUpdate(input, inOff, inLen);
- }
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- public void BlockUpdate(ReadOnlySpan<byte> input)
- {
- hash.BlockUpdate(input);
- }
- #endif
- /**
- * Reset the digest back to its initial state. The key, the salt and the
- * personal string will remain for further computations.
- */
- public void Reset()
- {
- hash.Reset();
- h0 = null;
- bufPos = DigestLength;
- digestPos = 0;
- blockPos = 0;
- nodeOffset = ComputeNodeOffset();
- }
- /**
- * Close the digest, producing the final digest value. The doFinal() call
- * leaves the digest reset. Key, salt and personal string remain.
- *
- * @param out the array the digest is to be copied into.
- * @param outOffset the offset into the out array the digest is to start at.
- */
- public int DoFinal(byte[] output, int outOff)
- {
- return OutputFinal(output, outOff, digestLength);
- }
- /**
- * Close the digest, producing the final digest value. The doFinal() call
- * leaves the digest reset. Key, salt, personal string remain.
- *
- * @param out output array to write the output bytes to.
- * @param outOff offset to start writing the bytes at.
- * @param outLen the number of output bytes requested.
- */
- public int OutputFinal(byte[] output, int outOff, int outLen)
- {
- int ret = Output(output, outOff, outLen);
- Reset();
- return ret;
- }
- /**
- * Start outputting the results of the final calculation for this digest. Unlike doFinal, this method
- * will continue producing output until the Xof is explicitly reset, or signals otherwise.
- *
- * @param out output array to write the output bytes to.
- * @param outOff offset to start writing the bytes at.
- * @param outLen the number of output bytes requested.
- * @return the number of bytes written
- */
- public int Output(byte[] output, int outOff, int outLen)
- {
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- return Output(output.AsSpan(outOff, outLen));
- #else
- if (h0 == null)
- {
- h0 = new byte[hash.GetDigestSize()];
- hash.DoFinal(h0, 0);
- }
- if (digestLength != UnknownDigestLength)
- {
- 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 >= DigestLength)
- {
- Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
- h.BlockUpdate(h0, 0, h0.Length);
- Arrays.Fill(buf, 0);
- h.DoFinal(buf, 0);
- bufPos = 0;
- nodeOffset++;
- blockPos++;
- }
- output[outOff + i] = buf[bufPos];
- bufPos++;
- digestPos++;
- }
- return outLen;
- #endif
- }
- #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
- public int DoFinal(Span<byte> output)
- {
- return OutputFinal(output[..digestLength]);
- }
- public int OutputFinal(Span<byte> output)
- {
- int ret = Output(output);
- Reset();
- return ret;
- }
- public int Output(Span<byte> output)
- {
- int outLen = output.Length;
- if (h0 == null)
- {
- h0 = new byte[hash.GetDigestSize()];
- hash.DoFinal(h0);
- }
- if (digestLength != UnknownDigestLength)
- {
- 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 >= DigestLength)
- {
- Blake2sDigest h = new Blake2sDigest(ComputeStepLength(), DigestLength, nodeOffset);
- h.BlockUpdate(h0);
- Arrays.Fill(buf, 0);
- h.DoFinal(buf);
- bufPos = 0;
- nodeOffset++;
- blockPos++;
- }
- output[i] = buf[bufPos];
- bufPos++;
- digestPos++;
- }
- return outLen;
- }
- #endif
- // get the next round length. If the length is unknown, the digest length is always the maximum.
- private int ComputeStepLength()
- {
- if (digestLength == UnknownDigestLength)
- return DigestLength;
- return System.Math.Min(DigestLength, digestLength - digestPos);
- }
- private long ComputeNodeOffset()
- {
- return digestLength * 0x100000000L;
- }
- }
- }
- #pragma warning restore
- #endif
|