protected override bool TryEncryptCfbCore( ReadOnlySpan <byte> plaintext, ReadOnlySpan <byte> iv, Span <byte> destination, PaddingMode paddingMode, int feedbackSizeInBits, out int bytesWritten) { ValidateCFBFeedbackSize(feedbackSizeInBits); ILiteSymmetricCipher cipher = CreateLiteCipher( CipherMode.CFB, paddingMode, Key, iv, blockSize: BlockSize / BitsPerByte, feedbackSizeInBits / BitsPerByte, paddingSize: feedbackSizeInBits / BitsPerByte, encrypting: true); using (cipher) { return(UniversalCryptoOneShot.OneShotEncrypt(cipher, paddingMode, plaintext, destination, out bytesWritten)); } }
public static bool OneShotEncrypt( ILiteSymmetricCipher cipher, PaddingMode paddingMode, ReadOnlySpan <byte> input, Span <byte> output, out int bytesWritten) { int ciphertextLength = SymmetricPadding.GetCiphertextLength(input.Length, cipher.PaddingSizeInBytes, paddingMode); if (output.Length < ciphertextLength) { bytesWritten = 0; return(false); } // Copy the input to the output, and apply padding if required. This will not throw since the // output length has already been checked, and PadBlock will not copy from input to output // until it has checked that it will be able to apply padding correctly. int padWritten = SymmetricPadding.PadBlock(input, output, cipher.PaddingSizeInBytes, paddingMode); // Do an in-place encrypt. All of our implementations support this, either natively // or making a temporary buffer themselves if in-place is not supported by the native // implementation. Span <byte> paddedOutput = output.Slice(0, padWritten); bytesWritten = cipher.TransformFinal(paddedOutput, paddedOutput); // After padding, we should have an even number of blocks, and the same applies // to the transform. Debug.Assert(padWritten == bytesWritten); return(true); }
protected override bool TryDecryptEcbCore( ReadOnlySpan <byte> ciphertext, Span <byte> destination, PaddingMode paddingMode, out int bytesWritten) { if (!ValidKeySize(Key.Length, out int keySize)) { throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); } int effectiveKeySize = EffectiveKeySizeValue == 0 ? keySize : EffectiveKeySize; ILiteSymmetricCipher cipher = CreateLiteCipher( CipherMode.ECB, paddingMode, Key, effectiveKeyLength: effectiveKeySize, iv: null, blockSize: BlockSize / BitsPerByte, 0, /*feedback size */ paddingSize: BlockSize / BitsPerByte, encrypting: false); using (cipher) { return(UniversalCryptoOneShot.OneShotDecrypt(cipher, paddingMode, ciphertext, destination, out bytesWritten)); } }
protected override bool TryDecryptEcbCore( ReadOnlySpan <byte> ciphertext, Span <byte> destination, PaddingMode paddingMode, out int bytesWritten) { ILiteSymmetricCipher cipher = _core.CreateLiteSymmetricCipher( iv: default,
protected override bool TryEncryptEcbCore( ReadOnlySpan <byte> plaintext, Span <byte> destination, PaddingMode paddingMode, out int bytesWritten) { if (!ValidKeySize(Key.Length, out int keySize)) { throw new InvalidOperationException(SR.Cryptography_InvalidKeySize); } Debug.Assert(EffectiveKeySize == KeySize); ILiteSymmetricCipher cipher = CreateLiteCipher( CipherMode.ECB, paddingMode, Key, iv: default,
protected override bool TryDecryptEcbCore( ReadOnlySpan <byte> ciphertext, Span <byte> destination, PaddingMode paddingMode, out int bytesWritten) { ILiteSymmetricCipher cipher = CreateLiteCipher( CipherMode.ECB, paddingMode, Key, iv: null, blockSize: BlockSize / BitsPerByte, 0, /*feedback size */ paddingSize: BlockSize / BitsPerByte, encrypting: false); using (cipher) { return(UniversalCryptoOneShot.OneShotDecrypt(cipher, paddingMode, ciphertext, destination, out bytesWritten)); } }
protected override bool TryEncryptCbcCore( ReadOnlySpan <byte> plaintext, ReadOnlySpan <byte> iv, Span <byte> destination, PaddingMode paddingMode, out int bytesWritten) { ILiteSymmetricCipher cipher = CreateLiteCipher( CipherMode.CBC, paddingMode, Key, iv, blockSize: BlockSize / BitsPerByte, 0, /*feedback size */ paddingSize: BlockSize / BitsPerByte, encrypting: true); using (cipher) { return(UniversalCryptoOneShot.OneShotEncrypt(cipher, paddingMode, plaintext, destination, out bytesWritten)); } }
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. } } }
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); } // The internal implementation of the one-shots are never expected to create // a plaintext larger than the ciphertext. If the buffer supplied is large enough // to do the transform, use it directly. // This does mean that the TransformFinal will write more than what is reported in // bytesWritten when padding needs to be removed. The padding is "removed" simply // by reporting less of the amount written, then zeroing out the extra padding. if (output.Length >= input.Length) { int bytesTransformed = cipher.TransformFinal(input, output); Span <byte> transformBuffer = output.Slice(0, bytesTransformed); try { // validates padding // 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. bytesWritten = SymmetricPadding.GetPaddingLength(transformBuffer, paddingMode, cipher.BlockSizeInBytes); // Zero out the padding so that the buffer does not contain the padding data "after" the bytesWritten. CryptographicOperations.ZeroMemory(transformBuffer.Slice(bytesWritten)); return(true); } catch (CryptographicException) { // The padding is invalid, but don't leave the plaintext in the buffer. CryptographicOperations.ZeroMemory(transformBuffer); throw; } } // If no padding is going to removed, then we already know the buffer is too small // since that requires a buffer at-least the size of the ciphertext. Bail out early. // The second condition is where the output length is short by more than a whole block. // All valid padding is at most one complete block. If the difference between the // output and the input is more than a whole block then we know the output is too small. if (!SymmetricPadding.DepaddingRequired(paddingMode) || input.Length - cipher.BlockSizeInBytes > output.Length) { bytesWritten = 0; return(false); } // At this point the destination might be big enough but we don't know until we've // transformed the last block, and input is within one block of being the right // size. // For sufficiently small ciphertexts, do them on the stack. // This buffer needs to be at least twice as big as the largest block size // we support, which is 16 bytes for AES. const int MaxInStackDecryptionBuffer = 128; Span <byte> stackBuffer = stackalloc byte[MaxInStackDecryptionBuffer]; if (input.Length <= MaxInStackDecryptionBuffer) { int stackTransformFinal = cipher.TransformFinal(input, stackBuffer); int depaddedLength = SymmetricPadding.GetPaddingLength( stackBuffer.Slice(0, stackTransformFinal), paddingMode, cipher.BlockSizeInBytes); Span <byte> writtenDepadded = stackBuffer.Slice(0, depaddedLength); if (output.Length < depaddedLength) { CryptographicOperations.ZeroMemory(writtenDepadded); bytesWritten = 0; return(false); } writtenDepadded.CopyTo(output); CryptographicOperations.ZeroMemory(writtenDepadded); bytesWritten = depaddedLength; return(true); } // If the source and destination do not overlap, we can decrypt directly in to the user buffer. if (!input.Overlaps(output, out int overlap) || overlap == 0) { // At this point we know that we have multiple blocks that need to be decrypted. // So we decrypt all but the last block directly in to the buffer. The final // block we decrypt in to a stack buffer, and if it fits, copy the last block to // the output. // We should only get here if we have multiple blocks to transform. The single // block case should have happened on the stack. Debug.Assert(input.Length > cipher.BlockSizeInBytes); int writtenToOutput = 0; int finalTransformWritten = 0; // CFB8 means this may not be an exact multiple of the block size. // If the an AES CFB8 ciphertext length is 129 with PKCS7 padding, then // we'll have 113 bytes in the unpaddedBlocks and 16 in the paddedBlock. // We still need to do this on block size, not padding size. The CFB8 // padding byte might be block size. We don't want unpaddedBlocks to // contain removable padding, so split on block size. ReadOnlySpan <byte> unpaddedBlocks = input[..^ cipher.BlockSizeInBytes];