AppendData() public method

Append the entire contents of data to the data already processed in the hash or HMAC.
is null. The object has already been disposed.
public AppendData ( byte data ) : void
data byte The data to process.
return void
        private static void Extract(HashAlgorithmName hashAlgorithmName, int hashLength, ReadOnlySpan <byte> ikm, ReadOnlySpan <byte> salt, Span <byte> prk)
        {
            Debug.Assert(HashLength(hashAlgorithmName) == hashLength);

            using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithmName, salt))
            {
                hmac.AppendData(ikm);
                GetHashAndReset(hmac, prk);
            }
        }
        private void Mgf1(IncrementalHash hasher, ReadOnlySpan <byte> mgfSeed, Span <byte> mask)
        {
            Span <byte> writePtr       = mask;
            int         count          = 0;
            Span <byte> bigEndianCount = stackalloc byte[sizeof(int)];

            while (writePtr.Length > 0)
            {
                hasher.AppendData(mgfSeed);
                BinaryPrimitives.WriteInt32BigEndian(bigEndianCount, count);
                hasher.AppendData(bigEndianCount);

                if (writePtr.Length >= _hLen)
                {
                    if (!hasher.TryGetHashAndReset(writePtr, out int bytesWritten))
                    {
                        Debug.Fail($"TryGetHashAndReset failed with sufficient space");
                        throw new CryptographicException();
                    }

                    Debug.Assert(bytesWritten == _hLen);
                    writePtr = writePtr.Slice(bytesWritten);
                }
                else
                {
                    Span <byte> tmp = stackalloc byte[_hLen];

                    if (!hasher.TryGetHashAndReset(tmp, out int bytesWritten))
                    {
                        Debug.Fail($"TryGetHashAndReset failed with sufficient space");
                        throw new CryptographicException();
                    }

                    Debug.Assert(bytesWritten == _hLen);
                    tmp.Slice(0, writePtr.Length).CopyTo(writePtr);
                    break;
                }

                count++;
            }
        }
        internal static byte[] DeriveKeyFromHash(
            ECDiffieHellmanPublicKey otherPartyPublicKey,
            HashAlgorithmName hashAlgorithm,
            ReadOnlySpan <byte> secretPrepend,
            ReadOnlySpan <byte> secretAppend,
            DeriveSecretAgreement deriveSecretAgreement)
        {
            Debug.Assert(otherPartyPublicKey != null);
            Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name));

            using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm))
            {
                hash.AppendData(secretPrepend);

                byte[]? secretAgreement = deriveSecretAgreement(otherPartyPublicKey, hash);
                // We want the side effect, and it should not have returned the answer.
                Debug.Assert(secretAgreement == null);

                hash.AppendData(secretAppend);

                return(hash.GetHashAndReset());
            }
        }
Example #4
0
            /// <summary>
            /// Get the secret agreement generated between two parties
            /// </summary>
            private byte[] DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash hasher)
            {
                Debug.Assert(otherPartyPublicKey != null);

                // Ensure that this ECDH object contains a private key by attempting a parameter export
                // which will throw an OpenSslCryptoException if no private key is available
                ECParameters thisKeyExplicit = ExportExplicitParameters(true);
                bool thisIsNamed = Interop.Crypto.EcKeyHasCurveName(_key.Value);
                ECDiffieHellmanOpenSslPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanOpenSslPublicKey;
                bool disposeOtherKey = false;

                if (otherKey == null)
                {
                    disposeOtherKey = true;

                    ECParameters otherParameters =
                        thisIsNamed
                            ? otherPartyPublicKey.ExportParameters()
                            : otherPartyPublicKey.ExportExplicitParameters();

                    otherKey = new ECDiffieHellmanOpenSslPublicKey(otherParameters);
                }

                bool otherIsNamed = otherKey.HasCurveName;

                SafeEvpPKeyHandle ourKey = null;
                SafeEvpPKeyHandle theirKey = null;
                byte[] rented = null;
                int secretLength = 0;

                try
                {
                    if (otherKey.KeySize != KeySize)
                    {
                        throw new ArgumentException(SR.Cryptography_ArgECDHKeySizeMismatch, nameof(otherPartyPublicKey));
                    }

                    if (otherIsNamed == thisIsNamed)
                    {
                        ourKey = _key.UpRefKeyHandle();
                        theirKey = otherKey.DuplicateKeyHandle();
                    }
                    else if (otherIsNamed)
                    {
                        ourKey = _key.UpRefKeyHandle();

                        using (ECOpenSsl tmp = new ECOpenSsl(otherKey.ExportExplicitParameters()))
                        {
                            theirKey = tmp.UpRefKeyHandle();
                        }
                    }
                    else
                    {
                        using (ECOpenSsl tmp = new ECOpenSsl(thisKeyExplicit))
                        {
                            ourKey = tmp.UpRefKeyHandle();
                        }

                        theirKey = otherKey.DuplicateKeyHandle();
                    }

                    using (SafeEvpPKeyCtxHandle ctx = Interop.Crypto.EvpPKeyCtxCreate(ourKey, theirKey, out uint secretLengthU))
                    {
                        if (ctx == null || ctx.IsInvalid || secretLengthU == 0 || secretLengthU > int.MaxValue)
                        {
                            throw Interop.Crypto.CreateOpenSslCryptographicException();
                        }

                        secretLength = (int)secretLengthU;

                        // Indicate that secret can hold stackallocs from nested scopes
                        Span<byte> secret = stackalloc byte[0];

                        // Arbitrary limit. But it covers secp521r1, which is the biggest common case.
                        const int StackAllocMax = 66;

                        if (secretLength > StackAllocMax)
                        {
                            rented = ArrayPool<byte>.Shared.Rent(secretLength);
                            secret = new Span<byte>(rented, 0, secretLength);
                        }
                        else
                        {
                            secret = stackalloc byte[secretLength];
                        }

                        Interop.Crypto.EvpPKeyDeriveSecretAgreement(ctx, secret);

                        if (hasher == null)
                        {
                            return secret.ToArray();
                        }
                        else
                        {
                            hasher.AppendData(secret);
                            return null;
                        }
                    }
                }
                finally
                {
                    theirKey?.Dispose();
                    ourKey?.Dispose();

                    if (disposeOtherKey)
                    {
                        otherKey.Dispose();
                    }

                    if (rented != null)
                    {
                        Array.Clear(rented, 0, secretLength);
                        ArrayPool<byte>.Shared.Return(rented);
                    }
                }
            }
 protected override void HashCore(byte[] array, int ibStart, int cbSize)
 {
     _running = true;
     _incrementalHash.AppendData(array, ibStart, cbSize);
 }
        private static unsafe void PHash(
            HashAlgorithmName algorithmName,
            ReadOnlySpan <byte> secret,
            ReadOnlySpan <byte> prfLabel,
            ReadOnlySpan <byte> prfSeed,
            int hashOutputSize,
            Span <byte> ret)
        {
            // https://tools.ietf.org/html/rfc4346#section-5
            //
            // P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
            //                        HMAC_hash(secret, A(2) + seed) +
            //                        HMAC_hash(secret, A(3) + seed) + ...
            //
            // A(0) = seed
            // A(i) = HMAC_hash(secret, A(i-1))
            //
            // This is called via PRF, which turns (label || seed) into seed.

            byte[] secretTmp = new byte[secret.Length];

            // Keep secretTmp pinned the whole time it has a secret in it, so it
            // doesn't get copied around during heap compaction.
            fixed(byte *pinnedSecretTmp = secretTmp)
            {
                secret.CopyTo(secretTmp);

                try
                {
                    Span <byte> retSpan = ret;

                    using (IncrementalHash hasher = IncrementalHash.CreateHMAC(algorithmName, secretTmp))
                    {
                        Span <byte> a = stackalloc byte[hashOutputSize];
                        Span <byte> p = stackalloc byte[hashOutputSize];

                        // A(1)
                        hasher.AppendData(prfLabel);
                        hasher.AppendData(prfSeed);

                        if (!hasher.TryGetHashAndReset(a, out int bytesWritten) || bytesWritten != hashOutputSize)
                        {
                            throw new CryptographicException();
                        }

                        while (true)
                        {
                            // HMAC_hash(secret, A(i) || seed) => p
                            hasher.AppendData(a);
                            hasher.AppendData(prfLabel);
                            hasher.AppendData(prfSeed);

                            if (!hasher.TryGetHashAndReset(p, out bytesWritten) || bytesWritten != hashOutputSize)
                            {
                                throw new CryptographicException();
                            }

                            int len = Math.Min(p.Length, retSpan.Length);

                            p.Slice(0, len).CopyTo(retSpan);
                            retSpan = retSpan.Slice(len);

                            if (retSpan.Length == 0)
                            {
                                return;
                            }

                            // Build the next A(i)
                            hasher.AppendData(a);

                            if (!hasher.TryGetHashAndReset(a, out bytesWritten) || bytesWritten != hashOutputSize)
                            {
                                throw new CryptographicException();
                            }
                        }
                    }
                }
                finally
                {
                    Array.Clear(secretTmp, 0, secretTmp.Length);
                }
            }
        }
            private byte[] DeriveSecretAgreement(ECDiffieHellmanPublicKey otherPartyPublicKey, IncrementalHash hasher)
            {
                if (!(otherPartyPublicKey is ECDiffieHellmanSecurityTransformsPublicKey secTransPubKey))
                {
                    secTransPubKey =
                        new ECDiffieHellmanSecurityTransformsPublicKey(otherPartyPublicKey.ExportParameters());
                }

                try
                {
                    SafeSecKeyRefHandle otherPublic = secTransPubKey.KeyHandle;

                    if (Interop.AppleCrypto.EccGetKeySizeInBits(otherPublic) != KeySize)
                    {
                        throw new ArgumentException(
                                  SR.Cryptography_ArgECDHKeySizeMismatch,
                                  nameof(otherPartyPublicKey));
                    }

                    SafeSecKeyRefHandle thisPrivate = GetKeys().PrivateKey;

                    if (thisPrivate == null)
                    {
                        throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
                    }

                    // Since Apple only supports secp256r1, secp384r1, and secp521r1; and 521 fits in
                    // 66 bytes ((521 + 7) / 8), the Span path will always succeed.
                    Span <byte> secretSpan = stackalloc byte[66];

                    byte[] secret = Interop.AppleCrypto.EcdhKeyAgree(
                        thisPrivate,
                        otherPublic,
                        secretSpan,
                        out int bytesWritten);

                    // Either we wrote to the span or we returned an array, but not both, and not neither.
                    // ("neither" would have thrown)
                    Debug.Assert(
                        (bytesWritten == 0) != (secret == null),
                        $"bytesWritten={bytesWritten}, (secret==null)={secret == null}");

                    if (hasher == null)
                    {
                        return(secret ?? secretSpan.Slice(0, bytesWritten).ToArray());
                    }

                    if (secret == null)
                    {
                        hasher.AppendData(secretSpan.Slice(0, bytesWritten));
                    }
                    else
                    {
                        hasher.AppendData(secret);
                        Array.Clear(secret, 0, secret.Length);
                    }

                    return(null);
                }
                finally
                {
                    if (!ReferenceEquals(otherPartyPublicKey, secTransPubKey))
                    {
                        secTransPubKey.Dispose();
                    }
                }
            }
        internal bool VerifyPss(ReadOnlySpan <byte> mHash, ReadOnlySpan <byte> em, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.2

            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                return(false);
            }

            Debug.Assert(em.Length >= emLen);

            // In this implementation, sLen is restricted to hLen.
            int sLen = _hLen;

            // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
            if (emLen < _hLen + sLen + 2)
            {
                return(false);
            }

            // 4. If the last byte is not 0xBC, output "inconsistent" and stop.
            if (em[em.Length - 1] != 0xBC)
            {
                return(false);
            }

            // 5. maskedDB is the leftmost emLen - hLen -1 bytes, H is the next hLen bytes.
            int dbLen = emLen - _hLen - 1;

            ReadOnlySpan <byte> maskedDb = em.Slice(0, dbLen);
            ReadOnlySpan <byte> h        = em.Slice(dbLen, _hLen);

            // 6. If the unused bits aren't zero, output "inconsistent" and stop.
            int  unusedBits   = 8 * emLen - emBits;
            byte usedBitsMask = (byte)(0xFF >> unusedBits);

            if ((maskedDb[0] & usedBitsMask) != maskedDb[0])
            {
                return(false);
            }

            // 7. dbMask = MGF(H, emLen - hLen - 1)
            byte[]      dbMaskRented = CryptoPool.Rent(maskedDb.Length);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, maskedDb.Length);

            try
            {
                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    Mgf1(hasher, h, dbMask);

                    // 8. DB = maskedDB XOR dbMask
                    Xor(dbMask, maskedDb);

                    // 9. Set the unused bits of DB to 0
                    dbMask[0] &= usedBitsMask;

                    // 10 ("a"): If the emLen - hLen - sLen - 2 leftmost bytes are not 0,
                    // output "inconsistent" and stop.
                    //
                    // Since signature verification is a public key operation there's no need to
                    // use fixed time equality checking here.
                    for (int i = emLen - _hLen - sLen - 2 - 1; i >= 0; --i)
                    {
                        if (dbMask[i] != 0)
                        {
                            return(false);
                        }
                    }

                    // 10 ("b") If the octet at position emLen - hLen - sLen - 1 (under a 1-indexed scheme)
                    // is not 0x01, output "inconsistent" and stop.
                    if (dbMask[emLen - _hLen - sLen - 2] != 0x01)
                    {
                        return(false);
                    }

                    // 11. Let salt be the last sLen octets of DB.
                    ReadOnlySpan <byte> salt = dbMask.Slice(dbMask.Length - sLen);

                    // 12/13. Let H' = Hash(eight zeros || mHash || salt)
                    hasher.AppendData(EightZeros);
                    hasher.AppendData(mHash);
                    hasher.AppendData(salt);

                    Span <byte> hPrime = stackalloc byte[_hLen];

                    if (!hasher.TryGetHashAndReset(hPrime, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 14. If H = H' output "consistent". Otherwise, output "inconsistent"
                    //
                    // Since this is a public key operation, no need to provide fixed time
                    // checking.
                    return(h.SequenceEqual(hPrime));
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(dbMask);
                CryptoPool.Return(dbMaskRented, clearSize: 0);
            }
        }
        internal void EncodePss(ReadOnlySpan <byte> mHash, Span <byte> destination, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.1
            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                throw new CryptographicException(SR.Cryptography_SignHash_WrongSize);
            }

            // In this implementation, sLen is restricted to the length of the input hash.
            int sLen = _hLen;

            // 3.  if emLen < hLen + sLen + 2, encoding error.
            //
            // sLen = hLen in this implementation.

            if (emLen < 2 + _hLen + sLen)
            {
                throw new CryptographicException(SR.Cryptography_KeyTooSmall);
            }

            // Set any leading bytes to zero, since that will be required for the pending
            // RSA operation.
            destination.Slice(0, destination.Length - emLen).Clear();

            // 12. Let EM = maskedDB || H || 0xbc (H has length hLen)
            Span <byte> em = destination.Slice(destination.Length - emLen, emLen);

            int dbLen = emLen - _hLen - 1;

            Span <byte> db    = em.Slice(0, dbLen);
            Span <byte> hDest = em.Slice(dbLen, _hLen);

            em[emLen - 1] = 0xBC;

            byte[]      dbMaskRented = CryptoPool.Rent(dbLen);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, dbLen);

            using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
            {
                // 4. Generate a random salt of length sLen
                Span <byte> salt = stackalloc byte[sLen];
                RandomNumberGenerator.Fill(salt);

                // 5. Let M' = an octet string of 8 zeros concat mHash concat salt
                // 6. Let H = Hash(M')

                hasher.AppendData(EightZeros);
                hasher.AppendData(mHash);
                hasher.AppendData(salt);

                if (!hasher.TryGetHashAndReset(hDest, out int hLen2) || hLen2 != _hLen)
                {
                    Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                    throw new CryptographicException();
                }

                // 7. Generate PS as zero-valued bytes of length emLen - sLen - hLen - 2.
                // 8. Let DB = PS || 0x01 || salt
                int psLen = emLen - sLen - _hLen - 2;
                db.Slice(0, psLen).Clear();
                db[psLen] = 0x01;
                salt.CopyTo(db.Slice(psLen + 1));

                // 9. Let dbMask = MGF(H, emLen - hLen - 1)
                Mgf1(hasher, hDest, dbMask);

                // 10. Let maskedDB = DB XOR dbMask
                Xor(db, dbMask);

                // 11. Set the "unused" bits in the leftmost byte of maskedDB to 0.
                int unusedBits = 8 * emLen - emBits;

                if (unusedBits != 0)
                {
                    byte mask = (byte)(0xFF >> unusedBits);
                    db[0] &= mask;
                }
            }

            CryptographicOperations.ZeroMemory(dbMask);
            CryptoPool.Return(dbMaskRented, clearSize: 0);
        }
Example #10
0
        private static unsafe void PHash(
            HashAlgorithmName algorithmName,
            ReadOnlySpan <byte> secret,
            ReadOnlySpan <byte> prfLabel,
            ReadOnlySpan <byte> prfSeed,
            int hashOutputSize,
            Span <byte> ret)
        {
            // https://tools.ietf.org/html/rfc4346#section-5
            //
            // P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
            //                        HMAC_hash(secret, A(2) + seed) +
            //                        HMAC_hash(secret, A(3) + seed) + ...
            //
            // A(0) = seed
            // A(i) = HMAC_hash(secret, A(i-1))
            //
            // This is called via PRF, which turns (label || seed) into seed.
            Span <byte> retSpan = ret;

            using (IncrementalHash hasher = IncrementalHash.CreateHMAC(algorithmName, secret))
            {
                Span <byte> a = stackalloc byte[hashOutputSize];
                Span <byte> p = stackalloc byte[hashOutputSize];

                // A(1)
                hasher.AppendData(prfLabel);
                hasher.AppendData(prfSeed);

                if (!hasher.TryGetHashAndReset(a, out int bytesWritten) || bytesWritten != hashOutputSize)
                {
                    throw new CryptographicException();
                }

                while (true)
                {
                    // HMAC_hash(secret, A(i) || seed) => p
                    hasher.AppendData(a);
                    hasher.AppendData(prfLabel);
                    hasher.AppendData(prfSeed);

                    if (!hasher.TryGetHashAndReset(p, out bytesWritten) || bytesWritten != hashOutputSize)
                    {
                        throw new CryptographicException();
                    }

                    int len = Math.Min(p.Length, retSpan.Length);

                    p.Slice(0, len).CopyTo(retSpan);
                    retSpan = retSpan.Slice(len);

                    if (retSpan.Length == 0)
                    {
                        return;
                    }

                    // Build the next A(i)
                    hasher.AppendData(a);

                    if (!hasher.TryGetHashAndReset(a, out bytesWritten) || bytesWritten != hashOutputSize)
                    {
                        throw new CryptographicException();
                    }
                }
            }
        }
Example #11
0
        private static void Expand(HashAlgorithmName hashAlgorithmName, int hashLength, ReadOnlySpan <byte> prk, Span <byte> output, ReadOnlySpan <byte> info)
        {
            Debug.Assert(HashLength(hashAlgorithmName) == hashLength);

            if (prk.Length < hashLength)
            {
                throw new ArgumentException(SR.Format(SR.Cryptography_Prk_TooSmall, hashLength), nameof(prk));
            }

            byte        counter         = 0;
            var         counterSpan     = new Span <byte>(ref counter);
            Span <byte> t               = Span <byte> .Empty;
            Span <byte> remainingOutput = output;

            const int   MaxStackInfoBuffer = 64;
            Span <byte> tempInfoBuffer     = stackalloc byte[MaxStackInfoBuffer];
            scoped ReadOnlySpan <byte> infoBuffer;

            byte[]? rentedTempInfoBuffer = null;

            if (output.Overlaps(info))
            {
                if (info.Length > MaxStackInfoBuffer)
                {
                    rentedTempInfoBuffer = CryptoPool.Rent(info.Length);
                    tempInfoBuffer       = rentedTempInfoBuffer;
                }

                tempInfoBuffer = tempInfoBuffer.Slice(0, info.Length);
                info.CopyTo(tempInfoBuffer);
                infoBuffer = tempInfoBuffer;
            }
            else
            {
                infoBuffer = info;
            }

            using (IncrementalHash hmac = IncrementalHash.CreateHMAC(hashAlgorithmName, prk))
            {
                for (int i = 1; ; i++)
                {
                    hmac.AppendData(t);
                    hmac.AppendData(infoBuffer);
                    counter = (byte)i;
                    hmac.AppendData(counterSpan);

                    if (remainingOutput.Length >= hashLength)
                    {
                        t = remainingOutput.Slice(0, hashLength);
                        remainingOutput = remainingOutput.Slice(hashLength);
                        GetHashAndReset(hmac, t);
                    }
                    else
                    {
                        if (remainingOutput.Length > 0)
                        {
                            Debug.Assert(hashLength <= 512 / 8, "hashLength is larger than expected, consider increasing this value or using regular allocation");
                            Span <byte> lastChunk = stackalloc byte[hashLength];
                            GetHashAndReset(hmac, lastChunk);
                            lastChunk.Slice(0, remainingOutput.Length).CopyTo(remainingOutput);
                        }

                        break;
                    }
                }
            }

            if (rentedTempInfoBuffer is not null)
            {
                CryptoPool.Return(rentedTempInfoBuffer, clearSize: info.Length);
            }
        }