Example #1
0
        private static bool TryDecrypt(
            SafeRsaHandle key,
            ReadOnlySpan <byte> data,
            Span <byte> destination,
            Interop.Crypto.RsaPadding rsaPadding,
            RsaPaddingProcessor rsaPaddingProcessor,
            out int bytesWritten)
        {
            // If rsaPadding is PKCS1 or OAEP-SHA1 then no depadding method should be present.
            // If rsaPadding is NoPadding then a depadding method should be present.
            Debug.Assert(
                (rsaPadding == Interop.Crypto.RsaPadding.NoPadding) ==
                (rsaPaddingProcessor != null));

            // Caller should have already checked this.
            Debug.Assert(!key.IsInvalid);

            int rsaSize = Interop.Crypto.RsaSize(key);

            if (data.Length > rsaSize)
            {
                throw new CryptographicException(
                          SR.Format(SR.Cryptography_Padding_DecDataTooBig, rsaSize));
            }

            if (destination.Length < rsaSize)
            {
                bytesWritten = 0;
                return(false);
            }

            Span <byte> decryptBuf = destination;

            byte[] paddingBuf = null;

            if (rsaPaddingProcessor != null)
            {
                paddingBuf = ArrayPool <byte> .Shared.Rent(rsaSize);

                decryptBuf = paddingBuf;
            }

            try
            {
                int returnValue = Interop.Crypto.RsaPrivateDecrypt(data.Length, data, decryptBuf, key, rsaPadding);
                CheckReturn(returnValue);

                if (rsaPaddingProcessor != null)
                {
                    return(rsaPaddingProcessor.DepadOaep(paddingBuf, destination, out bytesWritten));
                }
                else
                {
                    // If the padding mode is RSA_NO_PADDING then the size of the decrypted block
                    // will be RSA_size. If any padding was used, then some amount (determined by the padding algorithm)
                    // will have been reduced, and only returnValue bytes were part of the decrypted
                    // body.  Either way, we can just use returnValue, but some additional bytes may have been overwritten
                    // in the destination span.
                    bytesWritten = returnValue;
                }

                return(true);
            }
            finally
            {
                if (paddingBuf != null)
                {
                    // DecryptBuf is paddingBuf if paddingBuf is not null, erase it before returning it.
                    // If paddingBuf IS null then decryptBuf was destination, and shouldn't be cleared.
                    CryptographicOperations.ZeroMemory(decryptBuf);
                    ArrayPool <byte> .Shared.Return(paddingBuf);
                }
            }
        }
Example #2
0
        public override bool TryDecrypt(
            ReadOnlySpan <byte> data,
            Span <byte> destination,
            RSAEncryptionPadding padding,
            out int bytesWritten)
        {
            if (padding == null)
            {
                throw new ArgumentNullException(nameof(padding));
            }

            Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor);
            SafeRsaHandle             key        = GetKey();

            int keySizeBytes = Interop.Crypto.RsaSize(key);

            // OpenSSL does not take a length value for the destination, so it can write out of bounds.
            // To prevent the OOB write, decrypt into a temporary buffer.
            if (destination.Length < keySizeBytes)
            {
                Span <byte> tmp = stackalloc byte[0];
                byte[]? rent = null;

                // RSA up through 4096 stackalloc
                if (keySizeBytes <= 512)
                {
                    tmp = stackalloc byte[keySizeBytes];
                }
                else
                {
                    rent = ArrayPool <byte> .Shared.Rent(keySizeBytes);

                    tmp = rent;
                }

                bool ret = TryDecrypt(key, data, tmp, rsaPadding, oaepProcessor, out bytesWritten);

                if (ret)
                {
                    tmp = tmp.Slice(0, bytesWritten);

                    if (bytesWritten > destination.Length)
                    {
                        ret          = false;
                        bytesWritten = 0;
                    }
                    else
                    {
                        tmp.CopyTo(destination);
                    }

                    CryptographicOperations.ZeroMemory(tmp);
                }

                if (rent != null)
                {
                    // Already cleared
                    ArrayPool <byte> .Shared.Return(rent);
                }

                return(ret);
            }

            return(TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten));
        }