public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(padding); Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); SafeRsaHandle key = GetKey(); int rsaSize = Interop.AndroidCrypto.RsaSize(key); Span <byte> destination = default; byte[] buf = CryptoPool.Rent(rsaSize); try { destination = new Span <byte>(buf, 0, rsaSize); if (!TryDecrypt(key, data, destination, rsaPadding, oaepProcessor, out int bytesWritten)) { Debug.Fail($"{nameof(TryDecrypt)} should not return false for RSA_size buffer"); throw new CryptographicException(); } return(destination.Slice(0, bytesWritten).ToArray()); } finally { CryptographicOperations.ZeroMemory(destination); CryptoPool.Return(buf, clearSize: 0); } }
public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) { ArgumentNullException.ThrowIfNull(data); ArgumentNullException.ThrowIfNull(padding); Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); SafeRsaHandle key = GetKey(); byte[] buf = new byte[Interop.AndroidCrypto.RsaSize(key)]; bool encrypted = TryEncrypt( key, data, buf, rsaPadding, oaepProcessor, out int bytesWritten); if (!encrypted || bytesWritten != buf.Length) { Debug.Fail($"TryEncrypt behaved unexpectedly: {nameof(encrypted)}=={encrypted}, {nameof(bytesWritten)}=={bytesWritten}, {nameof(buf.Length)}=={buf.Length}"); throw new CryptographicException(); } return(buf); }
private static bool TryDecrypt( SafeRsaHandle key, ReadOnlySpan <byte> data, Span <byte> destination, Interop.AndroidCrypto.RsaPadding rsaPadding, out int bytesWritten) { // Caller should have already checked this. Debug.Assert(!key.IsInvalid); int rsaSize = Interop.AndroidCrypto.RsaSize(key); if (data.Length != rsaSize) { throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize); } if (destination.Length < rsaSize) { bytesWritten = 0; return(false); } int returnValue = Interop.AndroidCrypto.RsaPrivateDecrypt(data.Length, data, destination, key, rsaPadding); CheckReturn(returnValue); bytesWritten = returnValue; return(true); }
public override bool TryEncrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { ArgumentNullException.ThrowIfNull(padding); Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); SafeRsaHandle key = GetKey(); return(TryEncrypt(key, data, destination, rsaPadding, oaepProcessor, out bytesWritten)); }
private static bool TryEncrypt( SafeRsaHandle key, ReadOnlySpan <byte> data, Span <byte> destination, Interop.AndroidCrypto.RsaPadding rsaPadding, RsaPaddingProcessor?rsaPaddingProcessor, out int bytesWritten) { int rsaSize = Interop.AndroidCrypto.RsaSize(key); if (destination.Length < rsaSize) { bytesWritten = 0; return(false); } int returnValue; if (rsaPaddingProcessor != null) { Debug.Assert(rsaPadding == Interop.AndroidCrypto.RsaPadding.NoPadding); byte[] rented = CryptoPool.Rent(rsaSize); Span <byte> tmp = new Span <byte>(rented, 0, rsaSize); try { rsaPaddingProcessor.PadOaep(data, tmp); returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(tmp.Length, tmp, destination, key, rsaPadding); } finally { CryptographicOperations.ZeroMemory(tmp); CryptoPool.Return(rented, clearSize: 0); } } else { Debug.Assert(rsaPadding != Interop.AndroidCrypto.RsaPadding.NoPadding); returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(data.Length, data, destination, key, rsaPadding); } CheckReturn(returnValue); bytesWritten = returnValue; Debug.Assert(returnValue == rsaSize, $"{returnValue} != {rsaSize}"); return(true); }
private static bool TryEncrypt( SafeRsaHandle key, ReadOnlySpan <byte> data, Span <byte> destination, Interop.AndroidCrypto.RsaPadding rsaPadding, out int bytesWritten) { int rsaSize = Interop.AndroidCrypto.RsaSize(key); if (destination.Length < rsaSize) { bytesWritten = 0; return(false); } int returnValue = Interop.AndroidCrypto.RsaPublicEncrypt(data.Length, data, destination, key, rsaPadding); CheckReturn(returnValue); bytesWritten = returnValue; Debug.Assert(returnValue == rsaSize, $"{returnValue} != {rsaSize}"); return(true); }
private static bool TryDecrypt( SafeRsaHandle key, ReadOnlySpan <byte> data, Span <byte> destination, Interop.AndroidCrypto.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.AndroidCrypto.RsaPadding.NoPadding) == (rsaPaddingProcessor != null)); // Caller should have already checked this. Debug.Assert(!key.IsInvalid); int rsaSize = Interop.AndroidCrypto.RsaSize(key); if (data.Length != rsaSize) { throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize); } if (destination.Length < rsaSize) { bytesWritten = 0; return(false); } Span <byte> decryptBuf = destination; byte[]? paddingBuf = null; if (rsaPaddingProcessor != null) { paddingBuf = CryptoPool.Rent(rsaSize); decryptBuf = paddingBuf; } try { int returnValue = Interop.AndroidCrypto.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); CryptoPool.Return(paddingBuf, clearSize: 0); } } }
public override bool TryDecrypt( ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { ArgumentNullException.ThrowIfNull(padding); Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor? oaepProcessor); SafeRsaHandle key = GetKey(); int keySizeBytes = Interop.AndroidCrypto.RsaSize(key); // Android 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)); }