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 (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); } } }
private bool TrySignHash( ReadOnlySpan <byte> hash, Span <byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, bool allocateSignature, out int bytesWritten, out byte[] signature) { Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); Debug.Assert(padding != null); signature = null; // Do not factor out getting _key.Value, since the key creation should not happen on // invalid padding modes. if (padding.Mode == RSASignaturePaddingMode.Pkcs1) { int algorithmNid = GetAlgorithmNid(hashAlgorithm); SafeRsaHandle rsa = _key.Value; int bytesRequired = Interop.Crypto.RsaSize(rsa); if (allocateSignature) { Debug.Assert(destination.Length == 0); signature = new byte[bytesRequired]; destination = signature; } if (destination.Length < bytesRequired) { bytesWritten = 0; return(false); } if (!Interop.Crypto.RsaSign(algorithmNid, hash, hash.Length, destination, out int signatureSize, rsa)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Debug.Assert( signatureSize == bytesRequired, $"RSA_sign reported signatureSize was {signatureSize}, when {bytesRequired} was expected"); bytesWritten = signatureSize; return(true); } else if (padding.Mode == RSASignaturePaddingMode.Pss) { RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm); SafeRsaHandle rsa = _key.Value; int bytesRequired = Interop.Crypto.RsaSize(rsa); if (allocateSignature) { Debug.Assert(destination.Length == 0); signature = new byte[bytesRequired]; destination = signature; } if (destination.Length < bytesRequired) { bytesWritten = 0; return(false); } byte[] pssRented = ArrayPool <byte> .Shared.Rent(bytesRequired); Span <byte> pssBytes = new Span <byte>(pssRented, 0, bytesRequired); processor.EncodePss(hash, pssBytes, KeySize); int ret = Interop.Crypto.RsaSignPrimitive(pssBytes, destination, rsa); pssBytes.Clear(); ArrayPool <byte> .Shared.Return(pssRented); CheckReturn(ret); Debug.Assert( ret == bytesRequired, $"RSA_private_encrypt returned {ret} when {bytesRequired} was expected"); bytesWritten = ret; return(true); } throw PaddingModeNotSupported(); }
internal RSAAndroid(SafeRsaHandle key) { _key = new Lazy <SafeRsaHandle>(key.DuplicateHandle()); SetKeySizeFromHandle(key); }
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)); }
public override void ImportParameters(RSAParameters parameters) { ValidateParameters(ref parameters); ThrowIfDisposed(); if (parameters.Exponent == null || parameters.Modulus == null) { throw new CryptographicException(SR.Cryptography_InvalidRsaParameters); } // Check that either all parameters are not null or all are null, if a subset were set, then the parameters are invalid. // If the parameters are all not null, verify the integrity of their lengths. if (parameters.D == null) { if (parameters.P != null || parameters.DP != null || parameters.Q != null || parameters.DQ != null || parameters.InverseQ != null) { throw new CryptographicException(SR.Cryptography_InvalidRsaParameters); } } else { if (parameters.P == null || parameters.DP == null || parameters.Q == null || parameters.DQ == null || parameters.InverseQ == null) { throw new CryptographicException(SR.Cryptography_InvalidRsaParameters); } // Half, rounded up. int halfModulusLength = (parameters.Modulus.Length + 1) / 2; // Matching the .NET Framework RSACryptoServiceProvider behavior, as that's the .NET de facto standard if (parameters.D.Length != parameters.Modulus.Length || parameters.P.Length != halfModulusLength || parameters.Q.Length != halfModulusLength || parameters.DP.Length != halfModulusLength || parameters.DQ.Length != halfModulusLength || parameters.InverseQ.Length != halfModulusLength) { throw new CryptographicException(SR.Cryptography_InvalidRsaParameters); } } SafeRsaHandle key = Interop.AndroidCrypto.RsaCreate(); bool imported = false; if (key is null || key.IsInvalid) { throw new CryptographicException(); } try { if (!Interop.AndroidCrypto.SetRsaParameters( key, parameters.Modulus, parameters.Modulus != null ? parameters.Modulus.Length : 0, parameters.Exponent, parameters.Exponent != null ? parameters.Exponent.Length : 0, parameters.D, parameters.D != null ? parameters.D.Length : 0, parameters.P, parameters.P != null ? parameters.P.Length : 0, parameters.DP, parameters.DP != null ? parameters.DP.Length : 0, parameters.Q, parameters.Q != null ? parameters.Q.Length : 0, parameters.DQ, parameters.DQ != null ? parameters.DQ.Length : 0, parameters.InverseQ, parameters.InverseQ != null ? parameters.InverseQ.Length : 0)) { throw new CryptographicException(); } imported = true; } finally { if (!imported) { key.Dispose(); } } FreeKey(); _key = new Lazy <SafeRsaHandle>(key); SetKeySizeFromHandle(key); }
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(); }
private bool TrySignHash( ReadOnlySpan <byte> hash, Span <byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, bool allocateSignature, out int bytesWritten, out byte[]?signature) { Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name)); Debug.Assert(padding != null); signature = null; if (padding != RSASignaturePadding.Pkcs1 && padding != RSASignaturePadding.Pss) { throw PaddingModeNotSupported(); } RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm); SafeRsaHandle rsa = GetKey(); int bytesRequired = Interop.AndroidCrypto.RsaSize(rsa); if (allocateSignature) { Debug.Assert(destination.Length == 0); signature = new byte[bytesRequired]; destination = signature; } if (destination.Length < bytesRequired) { bytesWritten = 0; return(false); } byte[] encodedRented = CryptoPool.Rent(bytesRequired); Span <byte> encodedBytes = new Span <byte>(encodedRented, 0, bytesRequired); if (padding.Mode == RSASignaturePaddingMode.Pkcs1) { processor.PadPkcs1Signature(hash, encodedBytes); } else if (padding.Mode == RSASignaturePaddingMode.Pss) { processor.EncodePss(hash, encodedBytes, KeySize); } else { Debug.Fail("Padding mode should be checked prior to this point."); throw PaddingModeNotSupported(); } int ret = Interop.AndroidCrypto.RsaSignPrimitive(encodedBytes, destination, rsa); CryptoPool.Return(encodedRented, bytesRequired); CheckReturn(ret); Debug.Assert( ret == bytesRequired, $"RsaSignPrimitive returned {ret} when {bytesRequired} was expected"); bytesWritten = ret; return(true); }