Exemple #1
0
        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);
        }
Exemple #5
0
        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);
        }
Exemple #6
0
            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);
                }
            }
Exemple #7
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 !;
Exemple #9
0
        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);
        }
Exemple #10
0
            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);
            }
Exemple #11
0
        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));
            }
        }
Exemple #12
0
        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);
        }
Exemple #13
0
        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);
            }
        }
Exemple #14
0
        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);
            }
        }
Exemple #15
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));
            }

            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);
            }
        }
Exemple #16
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);
        }
Exemple #17
0
        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);
                }
            }
Exemple #19
0
        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);
                }
            }
        }
Exemple #20
0
        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);
        }
Exemple #21
0
        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);
                    }
                }
            }
        }
Exemple #22
0
        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);
            }
        }
Exemple #23
0
        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);
            }
        }
Exemple #25
0
        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);
Exemple #26
0
        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);
        }
Exemple #27
0
            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.
                }
            }
        }
Exemple #29
0
        /// <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);
            }