using System; using Org.BouncyCastle.Utilities; namespace 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. *
* BLAKE2xs offers a built-in keying mechanism to be used directly * for authentication ("Prefix-MAC") rather than a HMAC construction. * * 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. * * 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 public void BlockUpdate(ReadOnlySpan