async public Task HkdfExpand_Success(string expectedKey, HkdfAlgorithm algorithm, string prkString, int outputByteSize, string info, PclCryptoFunctionService sut)
        {
            var prk = Convert.FromBase64String(prkString);

            var key = await sut.HkdfExpandAsync(prk, info, outputByteSize, algorithm);

            Assert.Equal(expectedKey, Convert.ToBase64String(key));

            var keyFromByteArray = await sut.HkdfExpandAsync(prk, Encoding.UTF8.GetBytes(info), outputByteSize, algorithm);

            Assert.Equal(key, keyFromByteArray);
        }
        async public Task Hkdf_Success(string expectedKey, HkdfAlgorithm algorithm, string ikmString, string salt, string info, PclCryptoFunctionService sut)
        {
            byte[] ikm = Convert.FromBase64String(ikmString);

            var key = await sut.HkdfAsync(ikm, salt, info, 32, algorithm);

            Assert.Equal(expectedKey, Convert.ToBase64String(key));

            var keyFromByteArray = await sut.HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), Encoding.UTF8.GetBytes(info), 32, algorithm);

            Assert.Equal(key, keyFromByteArray);
        }
        private CryptoHashAlgorithm HkdfAlgorithmToCryptoHashAlgorithm(HkdfAlgorithm hkdfAlgorithm)
        {
            switch (hkdfAlgorithm)
            {
            case HkdfAlgorithm.Sha256:
                return(CryptoHashAlgorithm.Sha256);

            case HkdfAlgorithm.Sha512:
                return(CryptoHashAlgorithm.Sha512);

            default:
                throw new ArgumentException($"Invalid hkdf algorithm type, {hkdfAlgorithm}");
            }
        }
        // ref: https://tools.ietf.org/html/rfc5869
        public async Task <byte[]> HkdfExpandAsync(byte[] prk, byte[] info, int outputByteSize, HkdfAlgorithm algorithm)
        {
            var hashLen = algorithm == HkdfAlgorithm.Sha256 ? 32 : 64;

            var maxOutputByteSize = 255 * hashLen;

            if (outputByteSize > maxOutputByteSize)
            {
                throw new ArgumentException($"{nameof(outputByteSize)} is too large. Max is {maxOutputByteSize}, received {outputByteSize}");
            }
            if (prk.Length < hashLen)
            {
                throw new ArgumentException($"{nameof(prk)} length is too small. Must be at least {hashLen} for {algorithm}");
            }

            var cryptoHashAlgorithm = HkdfAlgorithmToCryptoHashAlgorithm(algorithm);
            var previousT           = new byte[0];
            var runningOkmLength    = 0;
            var n   = (int)Math.Ceiling((double)outputByteSize / hashLen);
            var okm = new byte[n * hashLen];

            for (var i = 0; i < n; i++)
            {
                var t = new byte[previousT.Length + info.Length + 1];
                previousT.CopyTo(t, 0);
                info.CopyTo(t, previousT.Length);
                t[t.Length - 1] = (byte)(i + 1);
                previousT       = await HmacAsync(t, prk, cryptoHashAlgorithm);

                previousT.CopyTo(okm, runningOkmLength);
                runningOkmLength = previousT.Length;
                if (runningOkmLength >= outputByteSize)
                {
                    break;
                }
            }
            return(okm.Take(outputByteSize).ToArray());
        }
 public async Task <byte[]> HkdfExpandAsync(byte[] prk, string info, int outputByteSize, HkdfAlgorithm algorithm) =>
 await HkdfExpandAsync(prk, Encoding.UTF8.GetBytes(info), outputByteSize, algorithm);
        public async Task <byte[]> HkdfAsync(byte[] ikm, byte[] salt, byte[] info, int outputByteSize, HkdfAlgorithm algorithm)
        {
            var prk = await HmacAsync(ikm, salt, HkdfAlgorithmToCryptoHashAlgorithm(algorithm));

            return(await HkdfExpandAsync(prk, info, outputByteSize, algorithm));
        }
 public async Task <byte[]> HkdfAsync(byte[] ikm, string salt, byte[] info, int outputByteSize, HkdfAlgorithm algorithm) =>
 await HkdfAsync(ikm, Encoding.UTF8.GetBytes(salt), info, outputByteSize, algorithm);