public static ICryptoTransform Create(PaddingMode paddingMode, BasicSymmetricCipher cipher, bool encrypting)
 {
     if (encrypting)
         return new UniversalCryptoEncryptor(paddingMode, cipher);
     else
         return new UniversalCryptoDecryptor(paddingMode, cipher);
 }
Exemple #2
0
        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);
        }
Exemple #3
0
 protected virtual void Dispose(bool disposing)
 {
     if (disposing)
     {
         BasicSymmetricCipher.Dispose();
     }
 }
        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.
                }
            }
        }
Exemple #5
0
 public static ICryptoTransform Create(PaddingMode paddingMode, BasicSymmetricCipher cipher, bool encrypting)
 {
     if (encrypting)
     {
         return(new UniversalCryptoEncryptor(paddingMode, cipher));
     }
     else
     {
         return(new UniversalCryptoDecryptor(paddingMode, cipher));
     }
 }
        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);
            }
        }
Exemple #9
0
        protected sealed override int UncheckedTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
        {
            //
            // If we're decrypting, it's possible to be called with the last blocks of the data, and then
            // have TransformFinalBlock called with an empty array. Since we don't know if this is the case,
            // we won't decrypt the last block of the input until either TransformBlock or
            // TransformFinalBlock is next called.
            //
            // We don't need to do this for PaddingMode.None because there is no padding to strip, and
            // we also don't do this for PaddingMode.Zeros since there is no way for us to tell if the
            // zeros at the end of a block are part of the plaintext or the padding.
            //
            int decryptedBytes = 0;

            if (DepaddingRequired)
            {
                // If we have data saved from a previous call, decrypt that into the output first
                if (_heldoverCipher != null)
                {
                    int depadDecryptLength = BasicSymmetricCipher.Transform(_heldoverCipher, 0, _heldoverCipher.Length, outputBuffer, outputOffset);
                    outputOffset   += depadDecryptLength;
                    decryptedBytes += depadDecryptLength;
                }
                else
                {
                    _heldoverCipher = new byte[InputBlockSize];
                }

                // Postpone the last block to the next round.
                Debug.Assert(inputCount >= _heldoverCipher.Length, "inputCount >= _heldoverCipher.Length");
                int startOfLastBlock = inputOffset + inputCount - _heldoverCipher.Length;
                Buffer.BlockCopy(inputBuffer, startOfLastBlock, _heldoverCipher, 0, _heldoverCipher.Length);
                inputCount -= _heldoverCipher.Length;
                Debug.Assert(inputCount % InputBlockSize == 0, "Did not remove whole blocks for depadding");
            }

            if (inputCount > 0)
            {
                decryptedBytes += BasicSymmetricCipher.Transform(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
            }

            return(decryptedBytes);
        }
Exemple #10
0
 public UniversalCryptoDecryptor(PaddingMode paddingMode, BasicSymmetricCipher basicSymmetricCipher)
     : base(paddingMode, basicSymmetricCipher)
 {
 }
Exemple #11
0
 protected UniversalCryptoTransform(PaddingMode paddingMode, BasicSymmetricCipher basicSymmetricCipher)
 {
     PaddingMode          = paddingMode;
     BasicSymmetricCipher = basicSymmetricCipher;
 }
Exemple #12
0
        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);
                }
            }
        }
 public UniversalCryptoEncryptor(PaddingMode paddingMode, BasicSymmetricCipher basicSymmetricCipher)
     : base(paddingMode, basicSymmetricCipher)
 {
 }
 protected override int UncheckedTransformBlock(ReadOnlySpan <byte> inputBuffer, Span <byte> outputBuffer)
 {
     return(BasicSymmetricCipher.Transform(inputBuffer, outputBuffer));
 }
 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);
 }
 protected sealed override int UncheckedTransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
 {
     return(BasicSymmetricCipher.Transform(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset));
 }
 protected UniversalCryptoTransform(PaddingMode paddingMode, BasicSymmetricCipher basicSymmetricCipher)
 {
     PaddingMode = paddingMode;
     BasicSymmetricCipher = basicSymmetricCipher;
 }