public override bool TransformOneShot(ReadOnlySpan <byte> input, Span <byte> output, out int bytesWritten) { int ciphertextLength = GetCiphertextLength(input.Length); 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 = PadBlock(input, output); // 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 = BasicSymmetricCipher.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); }
public override unsafe bool TransformOneShot(ReadOnlySpan <byte> input, Span <byte> output, out int bytesWritten) { if (input.Length % PaddingSizeBytes != 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 = BasicSymmetricCipher.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 = BasicSymmetricCipher.TransformFinal(input, buffer); decryptedBuffer = buffer.Slice(0, transformWritten); // validates padding int unpaddedLength = SymmetricPadding.GetPaddingLength(decryptedBuffer, PaddingMode, InputBlockSize); 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. } } }
protected sealed override byte[] UncheckedTransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { // We can't complete decryption on a partial block if (inputCount % InputBlockSize != 0) { throw new CryptographicException(SR.Cryptography_PartialBlock); } // // If we have postponed cipher bits from the prior round, copy that into the decryption buffer followed by the input data. // Otherwise the decryption buffer is just the input data. // byte[]? ciphertext = null; if (_heldoverCipher == null) { ciphertext = new byte[inputCount]; Buffer.BlockCopy(inputBuffer, inputOffset, ciphertext, 0, inputCount); } else { ciphertext = new byte[_heldoverCipher.Length + inputCount]; Buffer.BlockCopy(_heldoverCipher, 0, ciphertext, 0, _heldoverCipher.Length); Buffer.BlockCopy(inputBuffer, inputOffset, ciphertext, _heldoverCipher.Length, inputCount); } // Decrypt the data, then strip the padding to get the final decrypted data. Note that even if the cipherText length is 0, we must // invoke TransformFinal() so that the cipher object knows to reset for the next cipher operation. byte[] decryptedBytes = BasicSymmetricCipher.TransformFinal(ciphertext, 0, ciphertext.Length); byte[] outputData; if (ciphertext.Length > 0) { unsafe { fixed(byte *decryptedBytesPtr = decryptedBytes) { outputData = DepadBlock(decryptedBytes, 0, decryptedBytes.Length); if (outputData != decryptedBytes) { CryptographicOperations.ZeroMemory(decryptedBytes); } } } } else { outputData = Array.Empty <byte>(); } Reset(); return(outputData); }
protected override int UncheckedTransformFinalBlock(ReadOnlySpan <byte> inputBuffer, Span <byte> outputBuffer) { // The only caller of this method is the array-allocating overload, outputBuffer is // always new memory, not a user-provided buffer. Debug.Assert(!inputBuffer.Overlaps(outputBuffer)); int padWritten = PadBlock(inputBuffer, outputBuffer); int transformWritten = BasicSymmetricCipher.TransformFinal(outputBuffer.Slice(0, padWritten), outputBuffer); // After padding, we should have an even number of blocks, and the same applies // to the transform. Debug.Assert(padWritten == transformWritten); return(transformWritten); }
protected sealed override unsafe byte[] UncheckedTransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { byte[] paddedBlock = PadBlock(inputBuffer, inputOffset, inputCount); fixed(byte *paddedBlockPtr = paddedBlock) { byte[] output = BasicSymmetricCipher.TransformFinal(paddedBlock, 0, paddedBlock.Length); if (paddedBlock != inputBuffer) { CryptographicOperations.ZeroMemory(paddedBlock); } return(output); } }
protected override unsafe int UncheckedTransformFinalBlock(ReadOnlySpan <byte> inputBuffer, Span <byte> outputBuffer) { // We can't complete decryption on a partial block if (inputBuffer.Length % PaddingSizeBytes != 0) { throw new CryptographicException(SR.Cryptography_PartialBlock); } // // If we have postponed cipher bits from the prior round, copy that into the decryption buffer followed by the input data. // Otherwise the decryption buffer is just the input data. // ReadOnlySpan <byte> inputCiphertext; Span <byte> ciphertext; byte[]? rentedCiphertext = null; int rentedCiphertextSize = 0; try { if (_heldoverCipher == null) { rentedCiphertextSize = inputBuffer.Length; rentedCiphertext = CryptoPool.Rent(inputBuffer.Length); ciphertext = rentedCiphertext.AsSpan(0, inputBuffer.Length); inputCiphertext = inputBuffer; } else { rentedCiphertextSize = _heldoverCipher.Length + inputBuffer.Length; rentedCiphertext = CryptoPool.Rent(rentedCiphertextSize); ciphertext = rentedCiphertext.AsSpan(0, rentedCiphertextSize); _heldoverCipher.AsSpan().CopyTo(ciphertext); inputBuffer.CopyTo(ciphertext.Slice(_heldoverCipher.Length)); // Decrypt in-place inputCiphertext = ciphertext; } int unpaddedLength = 0; fixed(byte *pCiphertext = ciphertext) { // Decrypt the data, then strip the padding to get the final decrypted data. Note that even if the cipherText length is 0, we must // invoke TransformFinal() so that the cipher object knows to reset for the next cipher operation. int decryptWritten = BasicSymmetricCipher.TransformFinal(inputCiphertext, ciphertext); Span <byte> decryptedBytes = ciphertext.Slice(0, decryptWritten); if (decryptedBytes.Length > 0) { unpaddedLength = SymmetricPadding.GetPaddingLength(decryptedBytes, PaddingMode, InputBlockSize); decryptedBytes.Slice(0, unpaddedLength).CopyTo(outputBuffer); } } Reset(); return(unpaddedLength); } finally { if (rentedCiphertext != null) { CryptoPool.Return(rentedCiphertext, clearSize: rentedCiphertextSize); } } }
protected sealed override byte[] UncheckedTransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { byte[] paddedBlock = PadBlock(inputBuffer, inputOffset, inputCount); byte[] output = BasicSymmetricCipher.TransformFinal(paddedBlock, 0, paddedBlock.Length); return(output); }