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) { int algorithmNid = GetAlgorithmNid(hashAlgorithm); SafeRsaHandle rsa = GetKey(); return(Interop.Crypto.RsaVerify(algorithmNid, hash, signature, rsa)); } else if (padding == RSASignaturePadding.Pss) { RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm); SafeRsaHandle rsa = GetKey(); int requiredBytes = Interop.Crypto.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.Crypto.RsaVerificationPrimitive(signature, unwrapped, rsa); CheckReturn(ret); Debug.Assert( ret == requiredBytes, $"RSA_private_encrypt returned {ret} when {requiredBytes} was expected"); return(processor.VerifyPss(hash, unwrapped, KeySize)); } finally { CryptoPool.Return(rented, requiredBytes); } } throw PaddingModeNotSupported(); }
public override bool TryEncrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { ArgumentNullException.ThrowIfNull(padding); ThrowIfDisposed(); int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); if (destination.Length < rsaSize) { bytesWritten = 0; return(false); } if (data.Length == 0) { if (padding.Mode != RSAEncryptionPaddingMode.Pkcs1 && padding.Mode != RSAEncryptionPaddingMode.Oaep) { throw new CryptographicException(SR.Cryptography_InvalidPaddingMode); } byte[] rented = CryptoPool.Rent(rsaSize); Span <byte> tmp = new Span <byte>(rented, 0, rsaSize); try { if (padding.Mode == RSAEncryptionPaddingMode.Oaep) { RsaPaddingProcessor.PadOaep(padding.OaepHashAlgorithm, data, tmp); } else { Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Pkcs1); RsaPaddingProcessor.PadPkcs1Encryption(data, tmp); } return(Interop.AppleCrypto.TryRsaEncryptionPrimitive( GetKeys().PublicKey, tmp, destination, out bytesWritten)); } finally { CryptographicOperations.ZeroMemory(tmp); CryptoPool.Return(rented, clearSize: 0); } } return(Interop.AppleCrypto.TryRsaEncrypt( GetKeys().PublicKey, data, destination, padding, out bytesWritten)); }
private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters) { AsnWriter keyWriter; bool hasPrivateKey; if (parameters.X != null) { // DSAPrivateKey ::= SEQUENCE( // version INTEGER, // p INTEGER, // q INTEGER, // g INTEGER, // y INTEGER, // x INTEGER, // ) keyWriter = new AsnWriter(AsnEncodingRules.DER); using (keyWriter.PushSequence()) { keyWriter.WriteInteger(0); keyWriter.WriteKeyParameterInteger(parameters.P); keyWriter.WriteKeyParameterInteger(parameters.Q); keyWriter.WriteKeyParameterInteger(parameters.G); keyWriter.WriteKeyParameterInteger(parameters.Y); keyWriter.WriteKeyParameterInteger(parameters.X); } hasPrivateKey = true; } else { keyWriter = DSAKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); hasPrivateKey = false; } byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); if (!keyWriter.TryEncode(rented, out int written)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new InvalidOperationException(); } // Explicitly clear the inner buffer keyWriter.Reset(); try { return(Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey)); } finally { CryptoPool.Return(rented, written); } }
public int TransformFinal(ReadOnlySpan <byte> input, Span <byte> output) { #if DEBUG if (_isFinalized) { Debug.Fail("Cipher was reused without being reset."); throw new CryptographicException(); } _isFinalized = true; #endif // We just use CCCryptorUpdate instead of CCCryptorFinal. From the // Apple documentation: // In the following cases, the CCCryptorFinal() is superfluous as // it will not yield any data nor return an error: // 1. Encrypting or decrypting with a block cipher with padding // disabled, when the total amount of data provided to // CCCryptorUpdate() is an integral multiple of the block size. // 2. Encrypting or decrypting with a stream cipher. // For case 1, we do all of our padding manually and the cipher is opened with // PAL_PaddingMode.None. So that condition is met. For the second part, we always // submit data as a multiple of the block size, and is asserted below. So this condition // is met. Debug.Assert((input.Length % PaddingSizeInBytes) == 0); Debug.Assert(input.Length <= output.Length); int written = 0; if (input.Overlaps(output, out int offset) && offset != 0) { byte[] rented = CryptoPool.Rent(output.Length); try { written = CipherUpdate(input, rented); rented.AsSpan(0, written).CopyTo(output); } finally { CryptoPool.Return(rented, clearSize: written); } } else { written = CipherUpdate(input, output); } return(written); }
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { // inputCount != InputBlockSize is allowed ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); if (_inputBuffer == null) { ThrowHelper.ThrowObjectDisposed(); } if (inputCount == 0) { return(Array.Empty <byte>()); } // The common case is inputCount <= Base64InputBlockSize byte[]? tmpBufferArray = null; Span <byte> tmpBuffer = stackalloc byte[StackAllocSize]; if (inputCount > StackAllocSize) { tmpBuffer = tmpBufferArray = CryptoPool.Rent(inputCount); } tmpBuffer = GetTempBuffer(inputBuffer.AsSpan(inputOffset, inputCount), tmpBuffer); int bytesToTransform = _inputIndex + tmpBuffer.Length; // Too little data to decode if (bytesToTransform < InputBlockSize) { // reinitialize the transform Reset(); ReturnToCryptoPool(tmpBufferArray, tmpBuffer.Length); return(Array.Empty <byte>()); } int outputSize = GetOutputSize(bytesToTransform, tmpBuffer); byte[] output = new byte[outputSize]; ConvertFromBase64(tmpBuffer, output, out int consumed, out int written); Debug.Assert(written == outputSize); ReturnToCryptoPool(tmpBufferArray, tmpBuffer.Length); // reinitialize the transform Reset(); return(output); }
private bool TryDecrypt( SafeSecKeyRefHandle privateKey, ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { Debug.Assert(privateKey != null); if (padding.Mode != RSAEncryptionPaddingMode.Pkcs1 && padding.Mode != RSAEncryptionPaddingMode.Oaep) { throw new CryptographicException(SR.Cryptography_InvalidPaddingMode); } int modulusSizeInBytes = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); if (data.Length != modulusSizeInBytes) { throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize); } if (padding.Mode == RSAEncryptionPaddingMode.Pkcs1 || padding == RSAEncryptionPadding.OaepSHA1) { return(Interop.AppleCrypto.TryRsaDecrypt(privateKey, data, destination, padding, out bytesWritten)); } Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep); RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm); byte[] rented = CryptoPool.Rent(modulusSizeInBytes); Span <byte> unpaddedData = Span <byte> .Empty; try { if (!Interop.AppleCrypto.TryRsaDecryptionPrimitive(privateKey, data, rented, out int paddedSize)) { Debug.Fail($"Raw decryption failed with KeySize={KeySize} and a buffer length {rented.Length}"); throw new CryptographicException(); } Debug.Assert(modulusSizeInBytes == paddedSize); unpaddedData = new Span <byte>(rented, 0, paddedSize); return(processor.DepadOaep(unpaddedData, destination, out bytesWritten)); } finally { CryptographicOperations.ZeroMemory(unpaddedData); CryptoPool.Return(rented, clearSize: 0); } }
public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } SecKeyPair keys = GetKeys(); if (keys.PrivateKey == null) { throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } int modulusSizeInBytes = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); if (data.Length != modulusSizeInBytes) { throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize); } if (padding.Mode == RSAEncryptionPaddingMode.Pkcs1) { return(Interop.AppleCrypto.RsaDecrypt(keys.PrivateKey, data, padding)); } int maxOutputSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); byte[] rented = CryptoPool.Rent(maxOutputSize); int bytesWritten = 0; try { if (!TryDecrypt(keys.PrivateKey, data, rented, padding, out bytesWritten)) { Debug.Fail($"TryDecrypt returned false with a modulus-sized destination"); throw new CryptographicException(); } Span <byte> contentsSpan = new Span <byte>(rented, 0, bytesWritten); return(contentsSpan.ToArray()); } finally { CryptoPool.Return(rented, bytesWritten); } }
private static void ReadEncryptedPkcs8 <TRet>( string[] validOids, ReadOnlyMemory <byte> source, ReadOnlySpan <char> password, ReadOnlySpan <byte> passwordBytes, KeyReader <TRet> keyReader, out int bytesRead, out TRet ret) { int read; EncryptedPrivateKeyInfoAsn epki; try { AsnValueReader reader = new AsnValueReader(source.Span, AsnEncodingRules.BER); read = reader.PeekEncodedValue().Length; EncryptedPrivateKeyInfoAsn.Decode(ref reader, source, out epki); } catch (AsnContentException e) { throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e); } // No supported encryption algorithms produce more bytes of decryption output than there // were of decryption input. byte[] decrypted = CryptoPool.Rent(epki.EncryptedData.Length); Memory <byte> decryptedMemory = decrypted; try { int decryptedBytes = PasswordBasedEncryption.Decrypt( epki.EncryptionAlgorithm, password, passwordBytes, epki.EncryptedData.Span, decrypted); decryptedMemory = decryptedMemory.Slice(0, decryptedBytes); ReadPkcs8( validOids, decryptedMemory, keyReader, out int innerRead, out ret); if (innerRead != decryptedMemory.Length) { ret = default !;
private static Pkcs8Response ImportPkcs8(AsnWriter pkcs8Writer) { byte[] tmp = CryptoPool.Rent(pkcs8Writer.GetEncodedLength()); if (!pkcs8Writer.TryEncode(tmp, out int written)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new CryptographicException(); } Pkcs8Response ret = ImportPkcs8(tmp.AsSpan(0, written)); CryptoPool.Return(tmp, written); return(ret); }
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); }
public int Transform(ReadOnlySpan <byte> input, Span <byte> output) { Debug.Assert(input.Length > 0); Debug.Assert((input.Length % PaddingSizeInBytes) == 0); int numBytesWritten = 0; // BCryptEncrypt and BCryptDecrypt can do in place encryption, but if the buffers overlap // the offset must be zero. In that case, we need to copy to a temporary location. if (input.Overlaps(output, out int offset) && offset != 0) { byte[] rented = CryptoPool.Rent(output.Length); try { numBytesWritten = BCryptTransform(input, rented); rented.AsSpan(0, numBytesWritten).CopyTo(output); } finally { CryptoPool.Return(rented, clearSize: numBytesWritten); } } else { numBytesWritten = BCryptTransform(input, output); } if (numBytesWritten != input.Length) { // CNG gives us no way to tell BCryptDecrypt() that we're decrypting the final block, nor is it performing any // padding /depadding for us. So there's no excuse for a provider to hold back output for "future calls." Though // this isn't technically our problem to detect, we might as well detect it now for easier diagnosis. throw new CryptographicException(SR.Cryptography_UnexpectedTransformTruncation); } return(numBytesWritten); int BCryptTransform(ReadOnlySpan <byte> input, Span <byte> output) { return(_encrypting ? Interop.BCrypt.BCryptEncrypt(_hKey, input, _currentIv, output) : Interop.BCrypt.BCryptDecrypt(_hKey, input, _currentIv, output)); } }
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { // inputCount != InputBlockSize is allowed ThrowHelper.ValidateTransformBlock(inputBuffer, inputOffset, inputCount); if (_inputBuffer == null) { ThrowHelper.ThrowObjectDisposed(); } if (outputBuffer == null) { ThrowHelper.ThrowArgumentNull(ThrowHelper.ExceptionArgument.outputBuffer); } // The common case is inputCount = InputBlockSize byte[]? tmpBufferArray = null; Span <byte> tmpBuffer = stackalloc byte[StackAllocSize]; if (inputCount > StackAllocSize) { tmpBuffer = tmpBufferArray = CryptoPool.Rent(inputCount); } tmpBuffer = GetTempBuffer(inputBuffer.AsSpan(inputOffset, inputCount), tmpBuffer); int bytesToTransform = _inputIndex + tmpBuffer.Length; // Too little data to decode: save data to _inputBuffer, so it can be transformed later if (bytesToTransform < InputBlockSize) { tmpBuffer.CopyTo(_inputBuffer.AsSpan(_inputIndex)); _inputIndex = bytesToTransform; ReturnToCryptoPool(tmpBufferArray, tmpBuffer.Length); return(0); } ConvertFromBase64(tmpBuffer, outputBuffer.AsSpan(outputOffset), out _, out int written); ReturnToCryptoPool(tmpBufferArray, tmpBuffer.Length); return(written); }
public int TransformFinal(ReadOnlySpan <byte> input, Span <byte> output) { #if DEBUG if (_isFinalized) { Debug.Fail("Cipher was reused without being reset."); throw new CryptographicException(); } _isFinalized = true; #endif // If input and output overlap but are not the same, we need to use a // temp buffer since openssl doesn't seem to like partial overlaps. if (input.Overlaps(output, out int offset) && offset != 0) { byte[] rented = CryptoPool.Rent(input.Length); int written = 0; try { written = CipherUpdate(input, rented); Span <byte> outputSpan = rented.AsSpan(written); CheckBoolReturn(Interop.Crypto.EvpCipherFinalEx(_ctx, outputSpan, out int finalWritten)); written += finalWritten; rented.AsSpan(0, written).CopyTo(output); return(written); } finally { CryptoPool.Return(rented, clearSize: written); } } else { int written = CipherUpdate(input, output); Span <byte> outputSpan = output.Slice(written); CheckBoolReturn(Interop.Crypto.EvpCipherFinalEx(_ctx, outputSpan, out int finalWritten)); written += finalWritten; return(written); } }
public override bool TryCreateSignature(ReadOnlySpan <byte> hash, Span <byte> destination, out int bytesWritten) { byte[] converted; SafeDsaHandle key = _key.Value; int signatureSize = Interop.Crypto.DsaEncodedSignatureSize(key); byte[] signature = CryptoPool.Rent(signatureSize); try { bool success = Interop.Crypto.DsaSign(key, hash, new Span <byte>(signature, 0, signatureSize), out signatureSize); if (!success) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Debug.Assert( signatureSize <= signature.Length, "DSA_sign reported an unexpected signature size", "DSA_sign reported signatureSize was {0}, when <= {1} was expected", signatureSize, signature.Length); int signatureFieldSize = Interop.Crypto.DsaSignatureFieldSize(key) * BitsPerByte; converted = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(signature, 0, signatureSize, signatureFieldSize); } finally { CryptoPool.Return(signature, signatureSize); } if (converted.Length <= destination.Length) { new ReadOnlySpan <byte>(converted).CopyTo(destination); bytesWritten = converted.Length; return(true); } else { bytesWritten = 0; return(false); } }
public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } Interop.Crypto.RsaPadding rsaPadding = GetInteropPadding(padding, out RsaPaddingProcessor oaepProcessor); SafeRsaHandle key = _key.Value; CheckInvalidKey(key); int rsaSize = Interop.Crypto.RsaSize(key); byte[] buf = null; Span <byte> destination = default; try { buf = CryptoPool.Rent(rsaSize); 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); } }
private void ConvertFromBase64(Span <byte> tmpBuffer, Span <byte> outputBuffer, out int consumed, out int written) { int bytesToTransform = _inputIndex + tmpBuffer.Length; Debug.Assert(bytesToTransform >= 4); byte[]? transformBufferArray = null; Span <byte> transformBuffer = stackalloc byte[StackAllocSize]; if (bytesToTransform > StackAllocSize) { transformBuffer = transformBufferArray = CryptoPool.Rent(bytesToTransform); } // Copy _inputBuffer to transformBuffer and append tmpBuffer Debug.Assert(_inputIndex < _inputBuffer.Length); _inputBuffer.AsSpan(0, _inputIndex).CopyTo(transformBuffer); tmpBuffer.CopyTo(transformBuffer.Slice(_inputIndex)); // Save data that won't be transformed to _inputBuffer, so it can be transformed later _inputIndex = bytesToTransform & 3; // bit hack for % 4 bytesToTransform -= _inputIndex; // only transform up to the next multiple of 4 Debug.Assert(_inputIndex < _inputBuffer.Length); tmpBuffer.Slice(tmpBuffer.Length - _inputIndex).CopyTo(_inputBuffer); transformBuffer = transformBuffer.Slice(0, bytesToTransform); OperationStatus status = Base64.DecodeFromUtf8(transformBuffer, outputBuffer, out consumed, out written); if (status == OperationStatus.Done) { Debug.Assert(consumed == bytesToTransform); } else { Debug.Assert(status == OperationStatus.InvalidData); ThrowHelper.ThrowBase64FormatException(); } ReturnToCryptoPool(transformBufferArray, transformBuffer.Length); }
private static void Pbkdf2Core( ReadOnlySpan <char> password, ReadOnlySpan <byte> salt, Span <byte> destination, int iterations, HashAlgorithmName hashAlgorithm) { Debug.Assert(hashAlgorithm.Name is not null); Debug.Assert(iterations > 0); if (destination.IsEmpty) { return; } const int MaxPasswordStackSize = 256; byte[]? rentedPasswordBuffer = null; int maxEncodedSize = s_throwingUtf8Encoding.GetMaxByteCount(password.Length); Span <byte> passwordBuffer = maxEncodedSize > MaxPasswordStackSize ? (rentedPasswordBuffer = CryptoPool.Rent(maxEncodedSize)) : stackalloc byte[MaxPasswordStackSize]; int passwordBytesWritten = s_throwingUtf8Encoding.GetBytes(password, passwordBuffer); Span <byte> passwordBytes = passwordBuffer.Slice(0, passwordBytesWritten); try { Pbkdf2Implementation.Fill(passwordBytes, salt, iterations, hashAlgorithm, destination); } finally { CryptographicOperations.ZeroMemory(passwordBytes); } if (rentedPasswordBuffer is not null) { CryptoPool.Return(rentedPasswordBuffer, clearSize: 0); // manually cleared above. } }
private static SafeSecKeyRefHandle ImportKey(RSAParameters parameters) { AsnWriter keyWriter; bool hasPrivateKey; if (parameters.D != null) { keyWriter = RSAKeyFormatHelper.WritePkcs1PrivateKey(parameters); hasPrivateKey = true; } else { keyWriter = RSAKeyFormatHelper.WritePkcs1PublicKey(parameters); hasPrivateKey = false; } byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); if (!keyWriter.TryEncode(rented, out int written)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new InvalidOperationException(); } // Explicitly clear the inner buffer keyWriter.Reset(); try { return(Interop.AppleCrypto.CreateDataKey( rented.AsSpan(0, written), Interop.AppleCrypto.PAL_KeyAlgorithm.RSA, isPublic: !hasPrivateKey)); } finally { CryptoPool.Return(rented, written); } }
public virtual bool VerifyData(ReadOnlySpan <byte> data, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) { ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm)); ArgumentNullException.ThrowIfNull(padding); for (int i = 256; ; i = checked (i * 2)) { int hashLength = 0; byte[] hash = CryptoPool.Rent(i); try { if (TryHashData(data, hash, hashAlgorithm, out hashLength)) { return(VerifyHash(new ReadOnlySpan <byte>(hash, 0, hashLength), signature, hashAlgorithm, padding)); } } finally { CryptoPool.Return(hash, hashLength); } } }
public int TransformFinal(ReadOnlySpan <byte> input, Span <byte> output) { #if DEBUG if (_isFinalized) { Debug.Fail("Cipher was reused without being reset."); throw new CryptographicException(); } _isFinalized = true; #endif Debug.Assert((input.Length % PaddingSizeInBytes) == 0); Debug.Assert(input.Length <= output.Length); int written = 0; if (input.Overlaps(output, out int offset) && offset != 0) { byte[] rented = CryptoPool.Rent(output.Length); try { written = ProcessFinalBlock(input, rented); rented.AsSpan(0, written).CopyTo(output); } finally { CryptoPool.Return(rented, clearSize: written); } } else { written = ProcessFinalBlock(input, output); } return(written); }
public override unsafe bool TryExportSubjectPublicKeyInfo(Span <byte> destination, out int bytesWritten) { // The PKCS1 RSAPublicKey format is just the modulus (KeySize bits) and Exponent (usually 3 bytes), // with each field having up to 7 bytes of overhead and then up to 6 extra bytes of overhead for the // SEQUENCE tag. // // So KeySize / 4 is ideally enough to start. int rentSize = KeySize / 4; while (true) { byte[] rented = CryptoPool.Rent(rentSize); rentSize = rented.Length; int pkcs1Size = 0; fixed(byte *rentPtr = rented) { try { if (!TryExportRSAPublicKey(rented, out pkcs1Size)) { rentSize = checked (rentSize * 2); continue; } using (AsnWriter writer = RSAKeyFormatHelper.WriteSubjectPublicKeyInfo(rented.AsSpan(0, pkcs1Size))) { return(writer.TryEncode(destination, out bytesWritten)); } } finally { CryptoPool.Return(rented, pkcs1Size); } } } }
private static SafeSecKeyRefHandle ImportLegacyPrivateKey(ref ECParameters parameters) { AsnWriter keyWriter = EccKeyFormatHelper.WriteECPrivateKey(parameters); byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); if (!keyWriter.TryEncode(rented, out int written)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new InvalidOperationException(); } // Explicitly clear the inner buffer keyWriter.Reset(); try { return(Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), true)); } finally { CryptoPool.Return(rented, written); } }
public static unsafe bool TryExportToEncryptedPem <T>( T arg, ReadOnlySpan <char> password, PbeParameters pbeParameters, TryExportEncryptedKeyAction <T> exporter, Span <char> destination, out int charsWritten) { int bufferSize = 4096; while (true) { byte[] buffer = CryptoPool.Rent(bufferSize); int bytesWritten = 0; bufferSize = buffer.Length; // Fixed to prevent GC moves. fixed(byte *bufferPtr = buffer) { try { if (exporter(arg, password, pbeParameters, buffer, out bytesWritten)) { Span <byte> writtenSpan = new Span <byte>(buffer, 0, bytesWritten); return(PemEncoding.TryWrite(PemLabels.EncryptedPkcs8PrivateKey, writtenSpan, destination, out charsWritten)); } } finally { CryptoPool.Return(buffer, bytesWritten); } bufferSize = checked (bufferSize * 2); } } }
private static SafeSecKeyRefHandle ImportKey(ECParameters parameters) { AsnWriter keyWriter; bool hasPrivateKey; if (parameters.D != null) { keyWriter = EccKeyFormatHelper.WriteECPrivateKey(parameters); hasPrivateKey = true; } else { keyWriter = EccKeyFormatHelper.WriteSubjectPublicKeyInfo(parameters); hasPrivateKey = false; } byte[] rented = CryptoPool.Rent(keyWriter.GetEncodedLength()); if (!keyWriter.TryEncode(rented, out int written)) { Debug.Fail("TryEncode failed with a pre-allocated buffer"); throw new InvalidOperationException(); } // Explicitly clear the inner buffer keyWriter.Reset(); try { return(Interop.AppleCrypto.ImportEphemeralKey(rented.AsSpan(0, written), hasPrivateKey)); } finally { CryptoPool.Return(rented, written); } }
private void EncryptCore( ReadOnlySpan <byte> nonce, ReadOnlySpan <byte> plaintext, Span <byte> ciphertext, Span <byte> tag, ReadOnlySpan <byte> associatedData = default) { // Convert key length to bits. using (SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreatePartial(GetCipher(_key.Length * 8))) { if (ctx.IsInvalid) { throw new CryptographicException(); } if (!Interop.Crypto.CipherSetTagLength(ctx, tag.Length)) { throw new CryptographicException(); } Interop.Crypto.CipherSetNonceLength(ctx, nonce.Length); Interop.Crypto.EvpCipherSetKeyAndIV(ctx, _key, nonce, Interop.Crypto.EvpCipherDirection.Encrypt); if (associatedData.Length != 0) { Interop.Crypto.CipherUpdateAAD(ctx, associatedData); } byte[]? rented = null; try { Span <byte> ciphertextAndTag = stackalloc byte[0]; // Arbitrary limit. const int StackAllocMax = 128; if (checked (ciphertext.Length + tag.Length) <= StackAllocMax) { ciphertextAndTag = stackalloc byte[ciphertext.Length + tag.Length]; } else { rented = CryptoPool.Rent(ciphertext.Length + tag.Length); ciphertextAndTag = new Span <byte>(rented, 0, ciphertext.Length + tag.Length); } if (!Interop.Crypto.EvpCipherUpdate(ctx, ciphertextAndTag, out int ciphertextBytesWritten, plaintext)) { throw new CryptographicException(); } if (!Interop.Crypto.EvpCipherFinalEx( ctx, ciphertextAndTag.Slice(ciphertextBytesWritten), out int bytesWritten)) { throw new CryptographicException(); } ciphertextBytesWritten += bytesWritten; // NOTE: Android appends tag to the end of the ciphertext in case of CCM/GCM and "encryption" mode if (ciphertextBytesWritten != ciphertextAndTag.Length) { Debug.Fail($"CCM encrypt wrote {ciphertextBytesWritten} of {ciphertextAndTag.Length} bytes."); throw new CryptographicException(); } ciphertextAndTag[..ciphertext.Length].CopyTo(ciphertext);
public override bool TryDecrypt( ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten) { if (padding == null) { throw new ArgumentNullException(nameof(padding)); } ValidatePadding(padding); SafeEvpPKeyHandle key = GetKey(); int keySizeBytes = Interop.Crypto.EvpPKeySize(key); // OpenSSL requires that the decryption buffer be at least as large as EVP_PKEY_size. // So if the destination is too small, use a temporary buffer so we can match // Windows behavior of succeeding so long as the buffer can hold the final output. if (destination.Length < keySizeBytes) { // RSA up through 4096 bits use a stackalloc Span <byte> tmp = stackalloc byte[512]; byte[]? rent = null; if (keySizeBytes > tmp.Length) { rent = CryptoPool.Rent(keySizeBytes); tmp = rent; } int written = Decrypt(key, data, tmp, padding); bool ret; if (destination.Length < written) { bytesWritten = 0; ret = false; } else { tmp.Slice(0, written).CopyTo(destination); bytesWritten = written; ret = true; } // Whether a stackalloc or a rented array, clear our copy of // the decrypted content. CryptographicOperations.ZeroMemory(tmp.Slice(0, written)); if (rent != null) { // Already cleared. CryptoPool.Return(rent, clearSize: 0); } return(ret); } bytesWritten = Decrypt(key, data, destination, padding); 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 static unsafe bool OneShotDecrypt( ILiteSymmetricCipher cipher, PaddingMode paddingMode, ReadOnlySpan <byte> input, Span <byte> output, out int bytesWritten) { if (input.Length % cipher.PaddingSizeInBytes != 0) { throw new CryptographicException(SR.Cryptography_PartialBlock); } // If there is no padding that needs to be removed, and the output buffer is large enough to hold // the resulting plaintext, we can decrypt directly in to the output buffer. // We do not do this for modes that require padding removal. // // This is not done for padded ciphertexts because we don't know if the padding is valid // until it's been decrypted. We don't want to decrypt in to a user-supplied buffer and then throw // a padding exception after we've already filled the user buffer with plaintext. We should only // release the plaintext to the caller once we know the padding is valid. if (!SymmetricPadding.DepaddingRequired(paddingMode)) { if (output.Length >= input.Length) { bytesWritten = cipher.TransformFinal(input, output); return(true); } // If no padding is going to be removed, we know the buffer is too small and we can bail out. bytesWritten = 0; return(false); } byte[] rentedBuffer = CryptoPool.Rent(input.Length); Span <byte> buffer = rentedBuffer.AsSpan(0, input.Length); Span <byte> decryptedBuffer = default; fixed(byte *pBuffer = buffer) { try { int transformWritten = cipher.TransformFinal(input, buffer); decryptedBuffer = buffer.Slice(0, transformWritten); // This intentionally passes in BlockSizeInBytes instead of PaddingSizeInBytes. This is so that // "extra padded" CFB data can still be decrypted. The .NET Framework always padded CFB8 to the // block size, not the feedback size. We want the one-shot to be able to continue to decrypt // those ciphertexts, so for CFB8 we are more lenient on the number of allowed padding bytes. int unpaddedLength = SymmetricPadding.GetPaddingLength(decryptedBuffer, paddingMode, cipher.BlockSizeInBytes); // validates padding if (unpaddedLength > output.Length) { bytesWritten = 0; return(false); } decryptedBuffer.Slice(0, unpaddedLength).CopyTo(output); bytesWritten = unpaddedLength; return(true); } finally { CryptographicOperations.ZeroMemory(decryptedBuffer); CryptoPool.Return(rentedBuffer, clearSize: 0); // ZeroMemory clears the part of the buffer that was written to. } } }
/// <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 = CryptoPool.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) { CryptoPool.Return(rented, secretLength); } } }
public override bool VerifyHash(ReadOnlySpan <byte> hash, ReadOnlySpan <byte> signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) { ArgumentException.ThrowIfNullOrEmpty(hashAlgorithm.Name, nameof(hashAlgorithm)); ArgumentNullException.ThrowIfNull(padding); ThrowIfDisposed(); if (padding == RSASignaturePadding.Pkcs1) { Interop.AppleCrypto.PAL_HashAlgorithm palAlgId = PalAlgorithmFromAlgorithmName(hashAlgorithm, out _); return(Interop.AppleCrypto.VerifySignature( GetKeys().PublicKey, hash, signature, palAlgId, Interop.AppleCrypto.PAL_SignatureAlgorithm.RsaPkcs1)); } else if (padding.Mode == RSASignaturePaddingMode.Pss) { SafeSecKeyRefHandle publicKey = GetKeys().PublicKey; int keySize = KeySize; int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(keySize); if (signature.Length != rsaSize) { return(false); } if (hash.Length != RsaPaddingProcessor.HashLength(hashAlgorithm)) { return(false); } byte[] rented = CryptoPool.Rent(rsaSize); Span <byte> unwrapped = new Span <byte>(rented, 0, rsaSize); try { if (!Interop.AppleCrypto.TryRsaVerificationPrimitive( publicKey, signature, unwrapped, out int bytesWritten)) { Debug.Fail($"TryRsaVerificationPrimitive with a pre-allocated buffer"); throw new CryptographicException(); } Debug.Assert(bytesWritten == rsaSize); return(RsaPaddingProcessor.VerifyPss(hashAlgorithm, hash, unwrapped, keySize)); } finally { CryptographicOperations.ZeroMemory(unwrapped); CryptoPool.Return(rented, clearSize: 0); } } throw new CryptographicException(SR.Cryptography_InvalidPaddingMode); }