| File: System\Security\Cryptography\BasicSymmetricCipherLiteBCrypt.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using Internal.NativeCrypto; namespace System.Security.Cryptography { internal sealed class BasicSymmetricCipherLiteBCrypt : ILiteSymmetricCipher { private readonly bool _encrypting; private readonly byte[]? _currentIv; private SafeKeyHandle _hKey; public int BlockSizeInBytes { get; } public int PaddingSizeInBytes { get; } public BasicSymmetricCipherLiteBCrypt( SafeAlgorithmHandle algorithm, int blockSizeInBytes, int paddingSizeInBytes, ReadOnlySpan<byte> key, bool ownsParentHandle, ReadOnlySpan<byte> iv, bool encrypting) { if (!iv.IsEmpty) { // Must copy the input IV _currentIv = iv.ToArray(); } BlockSizeInBytes = blockSizeInBytes; PaddingSizeInBytes = paddingSizeInBytes; _encrypting = encrypting; _hKey = Interop.BCrypt.BCryptImportKey(algorithm, key); if (ownsParentHandle) { _hKey.SetParentHandle(algorithm); } } 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 void Reset(ReadOnlySpan<byte> iv) { if (_currentIv is not null) { iv.CopyTo(_currentIv); } } public int TransformFinal(ReadOnlySpan<byte> input, Span<byte> output) { Debug.Assert((input.Length % PaddingSizeInBytes) == 0); int numBytesWritten = 0; if (input.Length != 0) { numBytesWritten = Transform(input, output); Debug.Assert(numBytesWritten == input.Length); // Our implementation of Transform() guarantees this. See comment above. } return numBytesWritten; } public void Dispose() { if (_currentIv is not null) { CryptographicOperations.ZeroMemory(_currentIv); } _hKey?.Dispose(); _hKey = null!; } } }