Inheritance: IDisposable
Example #1
0
        internal static RsaPaddingProcessor OpenProcessor(HashAlgorithmName hashAlgorithmName)
        {
            return(s_lookup.GetOrAdd(
                       hashAlgorithmName,
                       static hashAlgorithmName =>
            {
                using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName))
                {
                    // SHA-2-512 is the biggest we expect
                    Span <byte> stackDest = stackalloc byte[512 / 8];
                    ReadOnlyMemory <byte> digestInfoPrefix;

                    if (hashAlgorithmName == HashAlgorithmName.MD5)
                    {
                        digestInfoPrefix = s_digestInfoMD5;
                    }
                    else if (hashAlgorithmName == HashAlgorithmName.SHA1)
                    {
                        digestInfoPrefix = s_digestInfoSha1;
                    }
                    else if (hashAlgorithmName == HashAlgorithmName.SHA256)
                    {
                        digestInfoPrefix = s_digestInfoSha256;
                    }
                    else if (hashAlgorithmName == HashAlgorithmName.SHA384)
                    {
                        digestInfoPrefix = s_digestInfoSha384;
                    }
                    else if (hashAlgorithmName == HashAlgorithmName.SHA512)
                    {
                        digestInfoPrefix = s_digestInfoSha512;
                    }
                    else
                    {
                        Debug.Fail("Unknown digest algorithm");
                        throw new CryptographicException();
                    }

                    if (hasher.TryGetHashAndReset(stackDest, out int bytesWritten))
                    {
                        return new RsaPaddingProcessor(hashAlgorithmName, bytesWritten, digestInfoPrefix);
                    }

                    byte[] big = hasher.GetHashAndReset();
                    return new RsaPaddingProcessor(hashAlgorithmName, big.Length, digestInfoPrefix);
                }
            }));
        }
        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 RsaPaddingProcessor OpenProcessor(HashAlgorithmName hashAlgorithmName)
        {
            return(s_lookup.GetOrAdd(
                       hashAlgorithmName,
                       alg =>
            {
                using (IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName))
                {
                    // SHA-2-512 is the biggest we expect
                    Span <byte> stackDest = stackalloc byte[512 / 8];

                    if (hasher.TryGetHashAndReset(stackDest, out int bytesWritten))
                    {
                        return new RsaPaddingProcessor(hashAlgorithmName, bytesWritten);
                    }

                    byte[] big = hasher.GetHashAndReset();
                    return new RsaPaddingProcessor(hashAlgorithmName, big.Length);
                }
            }));
        }
        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 #5
0
 public IncrementalMD5NetStandard16()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
 }
        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);
        }
        internal bool DepadOaep(
            ReadOnlySpan <byte> source,
            Span <byte> destination,
            out int bytesWritten)
        {
            // https://tools.ietf.org/html/rfc3447#section-7.1.2
            using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
            {
                Span <byte> lHash = stackalloc byte[_hLen];

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

                int y = source[0];
                ReadOnlySpan <byte> maskedSeed = source.Slice(1, _hLen);
                ReadOnlySpan <byte> maskedDB   = source.Slice(1 + _hLen);

                Span <byte> seed = stackalloc byte[_hLen];
                // seedMask = MGF(maskedDB, hLen)
                Mgf1(hasher, maskedDB, seed);

                // seed = seedMask XOR maskedSeed
                Xor(seed, maskedSeed);

                byte[] tmp = CryptoPool.Rent(source.Length);

                try
                {
                    Span <byte> dbMask = new Span <byte>(tmp, 0, maskedDB.Length);
                    // dbMask = MGF(seed, k - hLen - 1)
                    Mgf1(hasher, seed, dbMask);

                    // DB = dbMask XOR maskedDB
                    Xor(dbMask, maskedDB);

                    ReadOnlySpan <byte> lHashPrime = dbMask.Slice(0, _hLen);

                    int separatorPos = int.MaxValue;

                    for (int i = dbMask.Length - 1; i >= _hLen; i--)
                    {
                        // if dbMask[i] is 1, val is 0. otherwise val is [01,FF]
                        byte dbMinus1 = (byte)(dbMask[i] - 1);
                        int  val      = dbMinus1;

                        // if val is 0: FFFFFFFF & FFFFFFFF => FFFFFFFF
                        // if val is any other byte value, val-1 will be in the range 00000000 to 000000FE,
                        // and so the high bit will not be set.
                        val = (~val & (val - 1)) >> 31;

                        // if val is 0: separator = (0 & i) | (~0 & separator) => separator
                        // else: separator = (~0 & i) | (0 & separator) => i
                        //
                        // Net result: non-branching "if (dbMask[i] == 1) separatorPos = i;"
                        separatorPos = (val & i) | (~val & separatorPos);
                    }

                    bool lHashMatches       = CryptographicOperations.FixedTimeEquals(lHash, lHashPrime);
                    bool yIsZero            = y == 0;
                    bool separatorMadeSense = separatorPos < dbMask.Length;

                    // This intentionally uses non-short-circuiting operations to hide the timing
                    // differential between the three failure cases
                    bool shouldContinue = lHashMatches & yIsZero & separatorMadeSense;

                    if (!shouldContinue)
                    {
                        throw new CryptographicException(SR.Cryptography_OAEP_Decryption_Failed);
                    }

                    Span <byte> message = dbMask.Slice(separatorPos + 1);

                    if (message.Length <= destination.Length)
                    {
                        message.CopyTo(destination);
                        bytesWritten = message.Length;
                        return(true);
                    }
                    else
                    {
                        bytesWritten = 0;
                        return(false);
                    }
                }
                finally
                {
                    CryptoPool.Return(tmp, source.Length);
                }
            }
        }
 public SHA1CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
 }
Example #9
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);
            }
        }
 public SHA256CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
     HashSizeValue    = HashSizeBits;
 }
        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 PadOaep(
            ReadOnlySpan <byte> source,
            Span <byte> destination)
        {
            // https://tools.ietf.org/html/rfc3447#section-7.1.1

            byte[]? dbMask = null;
            Span <byte> dbMaskSpan = Span <byte> .Empty;

            try
            {
                // Since the biggest known _hLen is 512/8 (64) and destination.Length is 0 or more,
                // this shouldn't underflow without something having severely gone wrong.
                int maxInput = checked (destination.Length - _hLen - _hLen - 2);

                // 1(a) does not apply, we do not allow custom label values.

                // 1(b)
                if (source.Length > maxInput)
                {
                    throw new CryptographicException(
                              SR.Format(SR.Cryptography_Encryption_MessageTooLong, maxInput));
                }

                // The final message (step 2(i)) will be
                // 0x00 || maskedSeed (hLen long) || maskedDB (rest of the buffer)
                Span <byte> seed = destination.Slice(1, _hLen);
                Span <byte> db   = destination.Slice(1 + _hLen);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    // DB = lHash || PS || 0x01 || M
                    Span <byte> lHash = db.Slice(0, _hLen);
                    Span <byte> mDest = db.Slice(db.Length - source.Length);
                    Span <byte> ps    = db.Slice(_hLen, db.Length - _hLen - 1 - mDest.Length);
                    Span <byte> psEnd = db.Slice(_hLen + ps.Length, 1);

                    // 2(a) lHash = Hash(L), where L is the empty string.
                    if (!hasher.TryGetHashAndReset(lHash, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 2(b) generate a padding string of all zeros equal to the amount of unused space.
                    ps.Clear();

                    // 2(c)
                    psEnd[0] = 0x01;

                    // still 2(c)
                    source.CopyTo(mDest);

                    // 2(d)
                    RandomNumberGenerator.Fill(seed);

                    // 2(e)
                    dbMask     = CryptoPool.Rent(db.Length);
                    dbMaskSpan = new Span <byte>(dbMask, 0, db.Length);
                    Mgf1(hasher, seed, dbMaskSpan);

                    // 2(f)
                    Xor(db, dbMaskSpan);

                    // 2(g)
                    Span <byte> seedMask = stackalloc byte[_hLen];
                    Mgf1(hasher, db, seedMask);

                    // 2(h)
                    Xor(seed, seedMask);

                    // 2(i)
                    destination[0] = 0;
                }
            }
            catch (Exception e) when(!(e is CryptographicException))
            {
                Debug.Fail("Bad exception produced from OAEP padding: " + e);
                throw new CryptographicException();
            }
            finally
            {
                if (dbMask != null)
                {
                    CryptographicOperations.ZeroMemory(dbMaskSpan);
                    CryptoPool.Return(dbMask, clearSize: 0);
                }
            }
        }
Example #13
0
 public SHA1CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
 }
Example #14
0
 public MD5CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
 }
Example #15
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="key">Password string</param>
        /// <param name="saltBytes">Random bytes, length depends on encryption strength.
        /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.</param>
        /// <param name="blockSize">The encryption strength, in bytes eg 16 for 128 bits.</param>
        /// <param name="writeMode">True when creating a zip, false when reading. For the AuthCode.</param>
        ///
        public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) {
            if (blockSize!=16&&blockSize!=32) // 24 valid for AES but not supported by Winzip
                throw new Exception("Invalid blocksize "+blockSize+". Must be 16 or 32.");
            if (saltBytes.Length!=blockSize/2)
                throw new Exception("Invalid salt len. Must be "+blockSize/2+" for blocksize "+blockSize);
            // initialise the encryption buffer and buffer pos
            _blockSize=blockSize;
            _encryptBuffer=new byte[_blockSize];
            _encrPos=ENCRYPT_BLOCK;

            // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c
            var pdb=new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS);
            var rm=new RijndaelImplementation();
            rm.Mode=CipherMode.ECB; // No feedback from cipher for CTR mode
            _counterNonce=new byte[_blockSize];
            byte[] byteKey1=pdb.GetBytes(_blockSize);
            byte[] byteKey2=pdb.GetBytes(_blockSize);
            _encryptor=rm.CreateEncryptor(byteKey1, byteKey2);
            _pwdVerifier=pdb.GetBytes(PWD_VER_LENGTH);
            //
            
#if !OS_WINDOWS
           incrementalHash =  IncrementalHash.CreateHMAC(HashAlgorithmName.SHA1, byteKey2);
#else
            _hmacsha1=new HMACSHA1(byteKey2);
#endif
            _writeMode=writeMode;
        }
Example #16
0
 public MD5CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
     HashSizeValue    = HashSizeInBits;
 }
Example #17
0
 public Gost3411CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.Gost3411);
     HashSizeValue    = HashSizeBits;
 }
 public SHA1CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
     HashSizeValue = HashSizeBits;
 }
Example #19
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();
                    }
                }
            }
        }
            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();
                    }
                }
            }
Example #21
0
 internal MD5SHA1IncrementalHasher()
 {
     _md5 = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
     _sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
 }
        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);
                }
            }
        }
Example #23
0
 internal IncrementalHashHasher(HashAlgorithmName hashAlgorithmName)
 {
     _incrementalHash = IncrementalHash.CreateHash(hashAlgorithmName);
 }
Example #24
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);
                    }
                }
            }
 public MD5CryptoServiceProvider()
 {
     _incrementalHash = IncrementalHash.CreateHash(HashAlgorithmName.MD5);
 }