Exemple #1
0
            public override bool VerifyHash(ReadOnlySpan <byte> hash, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
            {
                if (string.IsNullOrEmpty(hashAlgorithm.Name))
                {
                    throw HashAlgorithmNameNullOrEmpty();
                }
                if (padding == null)
                {
                    throw new ArgumentNullException(nameof(padding));
                }
                if (padding == RSASignaturePadding.Pkcs1 && padding == RSASignaturePadding.Pss)
                {
                    throw PaddingModeNotSupported();
                }

                RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
                SafeRsaHandle       rsa       = GetKey();

                int requiredBytes = Interop.AndroidCrypto.RsaSize(rsa);

                if (signature.Length != requiredBytes)
                {
                    return(false);
                }

                if (hash.Length != processor.HashLength)
                {
                    return(false);
                }

                byte[]      rented    = CryptoPool.Rent(requiredBytes);
                Span <byte> unwrapped = new Span <byte>(rented, 0, requiredBytes);

                try
                {
                    int ret = Interop.AndroidCrypto.RsaVerificationPrimitive(signature, unwrapped, rsa);

                    CheckReturn(ret);
                    if (ret == 0)
                    {
                        // Return value of 0 from RsaVerificationPrimitive indicates the signature could not be decrypted.
                        return(false);
                    }

                    Debug.Assert(
                        ret == requiredBytes,
                        $"RsaVerificationPrimitive returned {ret} when {requiredBytes} was expected");

                    if (padding == RSASignaturePadding.Pkcs1)
                    {
                        byte[]      repadRent = CryptoPool.Rent(unwrapped.Length);
                        Span <byte> repadded  = repadRent.AsSpan(0, requiredBytes);
                        processor.PadPkcs1Signature(hash, repadded);
                        bool valid = CryptographicOperations.FixedTimeEquals(repadded, unwrapped);
                        CryptoPool.Return(repadRent, requiredBytes);
                        return(valid);
                    }
                    else if (padding == RSASignaturePadding.Pss)
                    {
                        return(processor.VerifyPss(hash, unwrapped, KeySize));
                    }
                    else
                    {
                        Debug.Fail("Padding mode should be checked prior to this point.");
                        throw PaddingModeNotSupported();
                    }
                }
                finally
                {
                    CryptoPool.Return(rented, requiredBytes);
                }

                throw PaddingModeNotSupported();
            }
        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);
                }
            }
        }