Пример #1
0
        // Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both
        // array-based APIs invoke this common helper with the "encrypt" parameter determining whether encryption or decryption is done.
        private unsafe byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, bool encrypt)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            if (padding == null)
            {
                throw new ArgumentNullException(nameof(padding));
            }

            int modulusSizeInBytes = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);

            if (!encrypt && data.Length != modulusSizeInBytes)
            {
                throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize);
            }

            if (encrypt &&
                padding.Mode == RSAEncryptionPaddingMode.Pkcs1 &&
                data.Length > modulusSizeInBytes - Pkcs1PaddingOverhead)
            {
                throw new CryptographicException(
                          SR.Format(SR.Cryptography_Encryption_MessageTooLong, modulusSizeInBytes - Pkcs1PaddingOverhead));
            }

            using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
            {
                if (encrypt && data.Length == 0)
                {
                    byte[]      rented        = CryptoPool.Rent(modulusSizeInBytes);
                    Span <byte> paddedMessage = new Span <byte>(rented, 0, modulusSizeInBytes);

                    try
                    {
                        if (padding == RSAEncryptionPadding.Pkcs1)
                        {
                            RsaPaddingProcessor.PadPkcs1Encryption(data, paddedMessage);
                        }
                        else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
                        {
                            RsaPaddingProcessor processor =
                                RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm);

                            processor.PadOaep(data, paddedMessage);
                        }
                        else
                        {
                            throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode);
                        }

                        return(EncryptOrDecrypt(keyHandle, paddedMessage, AsymmetricPaddingMode.NCRYPT_NO_PADDING_FLAG, null, encrypt));
                    }
                    finally
                    {
                        CryptographicOperations.ZeroMemory(paddedMessage);
                        CryptoPool.Return(rented, clearSize: 0);
                    }
                }

                switch (padding.Mode)
                {
                case RSAEncryptionPaddingMode.Pkcs1:
                    return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encrypt));

                case RSAEncryptionPaddingMode.Oaep:
                    IntPtr namePtr = Marshal.StringToHGlobalUni(padding.OaepHashAlgorithm.Name);
                    try
                    {
                        var paddingInfo = new BCRYPT_OAEP_PADDING_INFO()
                        {
                            pszAlgId = namePtr,

                            // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this.
                            pbLabel = IntPtr.Zero,
                            cbLabel = 0,
                        };
                        return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encrypt));
                    }
                    finally
                    {
                        Marshal.FreeHGlobal(namePtr);
                    }

                default:
                    throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode);
                }
            }
        }
Пример #2
0
            public override RSAParameters ExportParameters(bool includePrivateParameters)
            {
                SecKeyPair keys = GetKeys();

                if (includePrivateParameters && keys.PrivateKey == null)
                {
                    throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
                }

                bool gotKeyBlob = Interop.AppleCrypto.TrySecKeyCopyExternalRepresentation(
                    includePrivateParameters ? keys.PrivateKey ! : keys.PublicKey,
                    out byte[] keyBlob);

                if (!gotKeyBlob)
                {
                    return(ExportParametersFromLegacyKey(keys, includePrivateParameters));
                }

                try
                {
                    if (!includePrivateParameters)
                    {
                        // When exporting a key handle opened from a certificate, it seems to
                        // export as a PKCS#1 blob instead of an X509 SubjectPublicKeyInfo blob.
                        // So, check for that.
                        // NOTE: It doesn't affect macOS Mojave when SecCertificateCopyKey API
                        // is used.
                        RSAParameters key;

                        AsnReader reader         = new AsnReader(keyBlob, AsnEncodingRules.BER);
                        AsnReader sequenceReader = reader.ReadSequence();

                        if (sequenceReader.PeekTag().Equals(Asn1Tag.Integer))
                        {
                            AlgorithmIdentifierAsn ignored = default;
                            RSAKeyFormatHelper.ReadRsaPublicKey(keyBlob, ignored, out key);
                        }
                        else
                        {
                            RSAKeyFormatHelper.ReadSubjectPublicKeyInfo(
                                keyBlob,
                                out int localRead,
                                out key);
                            Debug.Assert(localRead == keyBlob.Length);
                        }
                        return(key);
                    }
                    else
                    {
                        AlgorithmIdentifierAsn ignored = default;
                        RSAKeyFormatHelper.FromPkcs1PrivateKey(
                            keyBlob,
                            ignored,
                            out RSAParameters key);
                        return(key);
                    }
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(keyBlob);
                }
            }
Пример #3
0
        private async Task <int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool useAsync)
        {
            // read <= count bytes from the input stream, transforming as we go.
            // Basic idea: first we deliver any bytes we already have in the
            // _OutputBuffer, because we know they're good.  Then, if asked to deliver
            // more bytes, we read & transform a block at a time until either there are
            // no bytes ready or we've delivered enough.
            int bytesToDeliver     = count;
            int currentOutputIndex = offset;

            if (_outputBufferIndex != 0)
            {
                // we have some already-transformed bytes in the output buffer
                if (_outputBufferIndex <= count)
                {
                    Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, _outputBufferIndex);
                    bytesToDeliver     -= _outputBufferIndex;
                    currentOutputIndex += _outputBufferIndex;
                    int toClear = _outputBuffer.Length - _outputBufferIndex;
                    CryptographicOperations.ZeroMemory(new Span <byte>(_outputBuffer, _outputBufferIndex, toClear));
                    _outputBufferIndex = 0;
                }
                else
                {
                    Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, count);
                    Buffer.BlockCopy(_outputBuffer, count, _outputBuffer, 0, _outputBufferIndex - count);
                    _outputBufferIndex -= count;

                    int toClear = _outputBuffer.Length - _outputBufferIndex;
                    CryptographicOperations.ZeroMemory(new Span <byte>(_outputBuffer, _outputBufferIndex, toClear));

                    return(count);
                }
            }
            // _finalBlockTransformed == true implies we're at the end of the input stream
            // if we got through the previous if block then _OutputBufferIndex = 0, meaning
            // we have no more transformed bytes to give
            // so return count-bytesToDeliver, the amount we were able to hand back
            // eventually, we'll just always return 0 here because there's no more to read
            if (_finalBlockTransformed)
            {
                return(count - bytesToDeliver);
            }
            // ok, now loop until we've delivered enough or there's nothing available
            int amountRead = 0;
            int numOutputBytes;

            // OK, see first if it's a multi-block transform and we can speed up things
            int blocksToProcess = bytesToDeliver / _outputBlockSize;

            if (blocksToProcess > 1 && _transform.CanTransformMultipleBlocks)
            {
                int    numWholeBlocksInBytes = blocksToProcess * _inputBlockSize;
                byte[] tempInputBuffer       = ArrayPool <byte> .Shared.Rent(numWholeBlocksInBytes);

                byte[] tempOutputBuffer = null;

                try
                {
                    amountRead = useAsync ?
                                 await _stream.ReadAsync(new Memory <byte>(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex), cancellationToken) : // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                                 _stream.Read(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex);

                    int totalInput = _inputBufferIndex + amountRead;

                    // If there's still less than a block, copy the new data into the hold buffer and move to the slow read.
                    if (totalInput < _inputBlockSize)
                    {
                        Buffer.BlockCopy(tempInputBuffer, _inputBufferIndex, _inputBuffer, _inputBufferIndex, amountRead);
                        _inputBufferIndex = totalInput;
                    }
                    else
                    {
                        // Copy any held data into tempInputBuffer now that we know we're proceeding
                        Buffer.BlockCopy(_inputBuffer, 0, tempInputBuffer, 0, _inputBufferIndex);
                        CryptographicOperations.ZeroMemory(new Span <byte>(_inputBuffer, 0, _inputBufferIndex));
                        amountRead       += _inputBufferIndex;
                        _inputBufferIndex = 0;

                        // Make amountRead an integral multiple of _InputBlockSize
                        int numWholeReadBlocks        = amountRead / _inputBlockSize;
                        int numWholeReadBlocksInBytes = numWholeReadBlocks * _inputBlockSize;
                        int numIgnoredBytes           = amountRead - numWholeReadBlocksInBytes;

                        if (numIgnoredBytes != 0)
                        {
                            _inputBufferIndex = numIgnoredBytes;
                            Buffer.BlockCopy(tempInputBuffer, numWholeReadBlocksInBytes, _inputBuffer, 0, numIgnoredBytes);
                        }

                        tempOutputBuffer = ArrayPool <byte> .Shared.Rent(numWholeReadBlocks *_outputBlockSize);

                        numOutputBytes = _transform.TransformBlock(tempInputBuffer, 0, numWholeReadBlocksInBytes, tempOutputBuffer, 0);
                        Buffer.BlockCopy(tempOutputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);

                        // Clear what was written while we know how much that was
                        CryptographicOperations.ZeroMemory(new Span <byte>(tempOutputBuffer, 0, numOutputBytes));
                        ArrayPool <byte> .Shared.Return(tempOutputBuffer);

                        tempOutputBuffer = null;

                        bytesToDeliver     -= numOutputBytes;
                        currentOutputIndex += numOutputBytes;
                    }
                }
                finally
                {
                    // If we rented and then an exception happened we don't know how much was written to,
                    // clear the whole thing and return it.
                    if (tempOutputBuffer != null)
                    {
                        CryptographicOperations.ZeroMemory(tempOutputBuffer);
                        ArrayPool <byte> .Shared.Return(tempOutputBuffer);

                        tempOutputBuffer = null;
                    }

                    CryptographicOperations.ZeroMemory(new Span <byte>(tempInputBuffer, 0, numWholeBlocksInBytes));
                    ArrayPool <byte> .Shared.Return(tempInputBuffer);

                    tempInputBuffer = null;
                }
            }

            // try to fill _InputBuffer so we have something to transform
            while (bytesToDeliver > 0)
            {
                while (_inputBufferIndex < _inputBlockSize)
                {
                    amountRead = useAsync ?
                                 await _stream.ReadAsync(new Memory <byte>(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex), cancellationToken) : // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                                 _stream.Read(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex);

                    // first, check to see if we're at the end of the input stream
                    if (amountRead == 0)
                    {
                        goto ProcessFinalBlock;
                    }
                    _inputBufferIndex += amountRead;
                }

                numOutputBytes    = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
                _inputBufferIndex = 0;

                if (bytesToDeliver >= numOutputBytes)
                {
                    Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
                    CryptographicOperations.ZeroMemory(new Span <byte>(_outputBuffer, 0, numOutputBytes));
                    currentOutputIndex += numOutputBytes;
                    bytesToDeliver     -= numOutputBytes;
                }
                else
                {
                    Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
                    _outputBufferIndex = numOutputBytes - bytesToDeliver;
                    Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
                    int toClear = _outputBuffer.Length - _outputBufferIndex;
                    CryptographicOperations.ZeroMemory(new Span <byte>(_outputBuffer, _outputBufferIndex, toClear));
                    return(count);
                }
            }
            return(count);

ProcessFinalBlock:
            // if so, then call TransformFinalBlock to get whatever is left
            byte[] finalBytes = _transform.TransformFinalBlock(_inputBuffer, 0, _inputBufferIndex);
            // now, since _OutputBufferIndex must be 0 if we're in the while loop at this point,
            // reset it to be what we just got back
            _outputBuffer      = finalBytes;
            _outputBufferIndex = finalBytes.Length;
            // set the fact that we've transformed the final block
            _finalBlockTransformed = true;
            // now, return either everything we just got or just what's asked for, whichever is smaller
            if (bytesToDeliver < _outputBufferIndex)
            {
                Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
                _outputBufferIndex -= bytesToDeliver;
                Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
                int toClear = _outputBuffer.Length - _outputBufferIndex;
                CryptographicOperations.ZeroMemory(new Span <byte>(_outputBuffer, _outputBufferIndex, toClear));
                return(count);
            }
            else
            {
                Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, _outputBufferIndex);
                bytesToDeliver    -= _outputBufferIndex;
                _outputBufferIndex = 0;
                CryptographicOperations.ZeroMemory(_outputBuffer);
                return(count - bytesToDeliver);
            }
        }
Пример #4
0
        private async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool useAsync)
        {
            // write <= count bytes to the output stream, transforming as we go.
            // Basic idea: using bytes in the _InputBuffer first, make whole blocks,
            // transform them, and write them out.  Cache any remaining bytes in the _InputBuffer.
            int bytesToWrite      = count;
            int currentInputIndex = offset;

            // if we have some bytes in the _InputBuffer, we have to deal with those first,
            // so let's try to make an entire block out of it
            if (_inputBufferIndex > 0)
            {
                if (count >= _inputBlockSize - _inputBufferIndex)
                {
                    // we have enough to transform at least a block, so fill the input block
                    Buffer.BlockCopy(buffer, offset, _inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex);
                    currentInputIndex += (_inputBlockSize - _inputBufferIndex);
                    bytesToWrite      -= (_inputBlockSize - _inputBufferIndex);
                    _inputBufferIndex  = _inputBlockSize;
                    // Transform the block and write it out
                }
                else
                {
                    // not enough to transform a block, so just copy the bytes into the _InputBuffer
                    // and return
                    Buffer.BlockCopy(buffer, offset, _inputBuffer, _inputBufferIndex, count);
                    _inputBufferIndex += count;
                    return;
                }
            }
            // If the OutputBuffer has anything in it, write it out
            if (_outputBufferIndex > 0)
            {
                if (useAsync)
                {
                    await _stream.WriteAsync(new ReadOnlyMemory <byte>(_outputBuffer, 0, _outputBufferIndex), cancellationToken); // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                }
                else
                {
                    _stream.Write(_outputBuffer, 0, _outputBufferIndex);
                }
                _outputBufferIndex = 0;
            }
            // At this point, either the _InputBuffer is full, empty, or we've already returned.
            // If full, let's process it -- we now know the _OutputBuffer is empty
            int numOutputBytes;

            if (_inputBufferIndex == _inputBlockSize)
            {
                numOutputBytes = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
                // write out the bytes we just got
                if (useAsync)
                {
                    await _stream.WriteAsync(new ReadOnlyMemory <byte>(_outputBuffer, 0, numOutputBytes), cancellationToken); // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                }
                else
                {
                    _stream.Write(_outputBuffer, 0, numOutputBytes);
                }

                // reset the _InputBuffer
                _inputBufferIndex = 0;
            }
            while (bytesToWrite > 0)
            {
                if (bytesToWrite >= _inputBlockSize)
                {
                    // We have at least an entire block's worth to transform
                    int numWholeBlocks = bytesToWrite / _inputBlockSize;

                    // If the transform will handle multiple blocks at once, do that
                    if (_transform.CanTransformMultipleBlocks && numWholeBlocks > 1)
                    {
                        int    numWholeBlocksInBytes = numWholeBlocks * _inputBlockSize;
                        byte[] tempOutputBuffer      = ArrayPool <byte> .Shared.Rent(numWholeBlocks *_outputBlockSize);

                        numOutputBytes = 0;

                        try
                        {
                            numOutputBytes =
                                _transform.TransformBlock(buffer, currentInputIndex, numWholeBlocksInBytes, tempOutputBuffer, 0);

                            if (useAsync)
                            {
                                await _stream.WriteAsync(new ReadOnlyMemory <byte>(tempOutputBuffer, 0, numOutputBytes), cancellationToken); // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                            }
                            else
                            {
                                _stream.Write(tempOutputBuffer, 0, numOutputBytes);
                            }

                            currentInputIndex += numWholeBlocksInBytes;
                            bytesToWrite      -= numWholeBlocksInBytes;
                        }
                        finally
                        {
                            CryptographicOperations.ZeroMemory(new Span <byte>(tempOutputBuffer, 0, numOutputBytes));
                            ArrayPool <byte> .Shared.Return(tempOutputBuffer);

                            tempOutputBuffer = null;
                        }
                    }
                    else
                    {
                        // do it the slow way
                        numOutputBytes = _transform.TransformBlock(buffer, currentInputIndex, _inputBlockSize, _outputBuffer, 0);

                        if (useAsync)
                        {
                            await _stream.WriteAsync(new ReadOnlyMemory <byte>(_outputBuffer, 0, numOutputBytes), cancellationToken); // ConfigureAwait not needed, as useAsync is only true if we're already on a TP thread
                        }
                        else
                        {
                            _stream.Write(_outputBuffer, 0, numOutputBytes);
                        }

                        currentInputIndex += _inputBlockSize;
                        bytesToWrite      -= _inputBlockSize;
                    }
                }
                else
                {
                    // In this case, we don't have an entire block's worth left, so store it up in the
                    // input buffer, which by now must be empty.
                    Buffer.BlockCopy(buffer, currentInputIndex, _inputBuffer, 0, bytesToWrite);
                    _inputBufferIndex += bytesToWrite;
                    return;
                }
            }
            return;
        }
Пример #5
0
        private static unsafe void FillKeyDerivation(
            ReadOnlySpan <byte> password,
            ReadOnlySpan <byte> salt,
            int iterations,
            string hashAlgorithmName,
            Span <byte> destination)
        {
            SafeBCryptKeyHandle keyHandle;
            int hashBlockSizeBytes = GetHashBlockSize(hashAlgorithmName);

            // stackalloc 0 to let compiler know this cannot escape.
            Span <byte>         clearSpan            = stackalloc byte[0];
            ReadOnlySpan <byte> symmetricKeyMaterial = stackalloc byte[0];
            int symmetricKeyMaterialLength;

            if (password.IsEmpty)
            {
                // CNG won't accept a null pointer for the password.
                symmetricKeyMaterial       = stackalloc byte[1];
                symmetricKeyMaterialLength = 0;
                clearSpan = default;
            }
            else if (password.Length <= hashBlockSizeBytes)
            {
                // Password is small enough to use as-is.
                symmetricKeyMaterial       = password;
                symmetricKeyMaterialLength = password.Length;
                clearSpan = default;
            }
            else
            {
                // RFC 2104: "The key for HMAC can be of any length (keys longer than B bytes are
                //     first hashed using H).
                //     We denote by B the byte-length of such
                //     blocks (B=64 for all the above mentioned examples of hash functions)
                //
                // Windows' PBKDF2 will do this up to a point. To ensure we accept arbitrary inputs for
                // PBKDF2, we do the hashing ourselves.
                Span <byte> hashBuffer = stackalloc byte[512 / 8]; // 64 bytes is SHA512, the largest digest handled.
                int         hashBufferSize;

                switch (hashAlgorithmName)
                {
                case HashAlgorithmNames.SHA1:
                    hashBufferSize = SHA1.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA256:
                    hashBufferSize = SHA256.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA384:
                    hashBufferSize = SHA384.HashData(password, hashBuffer);
                    break;

                case HashAlgorithmNames.SHA512:
                    hashBufferSize = SHA512.HashData(password, hashBuffer);
                    break;

                default:
                    Debug.Fail($"Unexpected hash algorithm '{hashAlgorithmName}'");
                    throw new CryptographicException();
                }

                clearSpan                  = hashBuffer.Slice(0, hashBufferSize);
                symmetricKeyMaterial       = clearSpan;
                symmetricKeyMaterialLength = hashBufferSize;
            }

            Debug.Assert(symmetricKeyMaterial.Length > 0);

            NTSTATUS generateKeyStatus;

            if (Interop.BCrypt.PseudoHandlesSupported)
            {
                fixed(byte *pSymmetricKeyMaterial = symmetricKeyMaterial)
                {
                    generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
                        (nuint)BCryptAlgPseudoHandle.BCRYPT_PBKDF2_ALG_HANDLE,
                        out keyHandle,
                        pbKeyObject: IntPtr.Zero,
                        cbKeyObject: 0,
                        pSymmetricKeyMaterial,
                        symmetricKeyMaterialLength,
                        dwFlags: 0);
                }
            }
            else
            {
                if (s_pbkdf2AlgorithmHandle is null)
                {
                    NTSTATUS openStatus = Interop.BCrypt.BCryptOpenAlgorithmProvider(
                        out SafeBCryptAlgorithmHandle pbkdf2AlgorithmHandle,
                        Internal.NativeCrypto.BCryptNative.AlgorithmName.Pbkdf2,
                        null,
                        BCryptOpenAlgorithmProviderFlags.None);

                    if (openStatus != NTSTATUS.STATUS_SUCCESS)
                    {
                        pbkdf2AlgorithmHandle.Dispose();
                        CryptographicOperations.ZeroMemory(clearSpan);
                        throw Interop.BCrypt.CreateCryptographicException(openStatus);
                    }

                    // This might race, and that's okay. Worst case the algorithm is opened
                    // more than once, and the ones that lost will get cleaned up during collection.
                    Interlocked.CompareExchange(ref s_pbkdf2AlgorithmHandle, pbkdf2AlgorithmHandle, null);
                }

                fixed(byte *pSymmetricKeyMaterial = symmetricKeyMaterial)
                {
                    generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
                        s_pbkdf2AlgorithmHandle,
                        out keyHandle,
                        pbKeyObject: IntPtr.Zero,
                        cbKeyObject: 0,
                        pSymmetricKeyMaterial,
                        symmetricKeyMaterialLength,
                        dwFlags: 0);
                }
            }

            CryptographicOperations.ZeroMemory(clearSpan);

            if (generateKeyStatus != NTSTATUS.STATUS_SUCCESS)
            {
                keyHandle.Dispose();
                throw Interop.BCrypt.CreateCryptographicException(generateKeyStatus);
            }

            Debug.Assert(!keyHandle.IsInvalid);

            ulong kdfIterations = (ulong)iterations; // Previously asserted to be positive.

            using (keyHandle)
                fixed(char *pHashAlgorithmName = hashAlgorithmName)
                fixed(byte *pSalt        = salt)
                fixed(byte *pDestination = destination)
                {
                    Span <BCryptBuffer> buffers = stackalloc BCryptBuffer[3];

                    buffers[0].BufferType = CngBufferDescriptors.KDF_ITERATION_COUNT;
                    buffers[0].pvBuffer   = (IntPtr)(&kdfIterations);
                    buffers[0].cbBuffer   = sizeof(ulong);

                    buffers[1].BufferType = CngBufferDescriptors.KDF_SALT;
                    buffers[1].pvBuffer   = (IntPtr)pSalt;
                    buffers[1].cbBuffer   = salt.Length;

                    buffers[2].BufferType = CngBufferDescriptors.KDF_HASH_ALGORITHM;
                    buffers[2].pvBuffer   = (IntPtr)pHashAlgorithmName;

                    // C# spec: "A char* value produced by fixing a string instance always points to a null-terminated string"
                    buffers[2].cbBuffer = checked ((hashAlgorithmName.Length + 1) * sizeof(char)); // Add null terminator.

                    fixed(BCryptBuffer *pBuffers = buffers)
                    {
                        Interop.BCrypt.BCryptBufferDesc bufferDesc;
                        bufferDesc.ulVersion = Interop.BCrypt.BCRYPTBUFFER_VERSION;
                        bufferDesc.cBuffers  = buffers.Length;
                        bufferDesc.pBuffers  = (IntPtr)pBuffers;

                        NTSTATUS deriveStatus = Interop.BCrypt.BCryptKeyDerivation(
                            keyHandle,
                            &bufferDesc,
                            pDestination,
                            destination.Length,
                            out uint resultLength,
                            dwFlags: 0);

                        if (deriveStatus != NTSTATUS.STATUS_SUCCESS)
                        {
                            throw Interop.BCrypt.CreateCryptographicException(deriveStatus);
                        }

                        if (destination.Length != resultLength)
                        {
                            Debug.Fail("PBKDF2 resultLength != destination.Length");
                            throw new CryptographicException();
                        }
                    }
                }
        }
Пример #6
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);
        }
Пример #7
0
        void ILiteSymmetricCipher.Reset(ReadOnlySpan <byte> iv) => throw new NotImplementedException(); // never invoked

        private void Reset()
        {
            CryptographicOperations.ZeroMemory(_lastBlockBuffer);
            _lastBlockBuffer = null;
        }
Пример #8
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));
                }

                ThrowIfDisposed();

                if (padding == RSASignaturePadding.Pkcs1)
                {
                    Interop.AppleCrypto.PAL_HashAlgorithm palAlgId =
                        PalAlgorithmFromAlgorithmName(hashAlgorithm, out int expectedSize);
                    return(Interop.AppleCrypto.VerifySignature(GetKeys().PublicKey, hash, signature, palAlgId));
                }
                else if (padding.Mode == RSASignaturePaddingMode.Pss)
                {
                    RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
                    SafeSecKeyRefHandle publicKey = GetKeys().PublicKey;

                    int keySize = KeySize;
                    int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(keySize);

                    if (signature.Length != rsaSize)
                    {
                        return(false);
                    }

                    if (hash.Length != processor.HashLength)
                    {
                        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(processor.VerifyPss(hash, unwrapped, keySize));
                    }
                    finally
                    {
                        CryptographicOperations.ZeroMemory(unwrapped);
                        CryptoPool.Return(rented, clearSize: 0);
                    }
                }

                throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
            }
        internal void PadOaep(
            ReadOnlySpan <byte> source,
            Span <byte> destination)
        {
            // https://tools.ietf.org/html/rfc3447#section-7.1.1

            byte[]? dbMask = null;
            Span <byte> dbMaskSpan = Span <byte> .Empty;

            try
            {
                // Since the biggest known _hLen is 512/8 (64) and destination.Length is 0 or more,
                // this shouldn't underflow without something having severely gone wrong.
                int maxInput = checked (destination.Length - _hLen - _hLen - 2);

                // 1(a) does not apply, we do not allow custom label values.

                // 1(b)
                if (source.Length > maxInput)
                {
                    throw new CryptographicException(
                              SR.Format(SR.Cryptography_Encryption_MessageTooLong, maxInput));
                }

                // The final message (step 2(i)) will be
                // 0x00 || maskedSeed (hLen long) || maskedDB (rest of the buffer)
                Span <byte> seed = destination.Slice(1, _hLen);
                Span <byte> db   = destination.Slice(1 + _hLen);

                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    // DB = lHash || PS || 0x01 || M
                    Span <byte> lHash = db.Slice(0, _hLen);
                    Span <byte> mDest = db.Slice(db.Length - source.Length);
                    Span <byte> ps    = db.Slice(_hLen, db.Length - _hLen - 1 - mDest.Length);
                    Span <byte> psEnd = db.Slice(_hLen + ps.Length, 1);

                    // 2(a) lHash = Hash(L), where L is the empty string.
                    if (!hasher.TryGetHashAndReset(lHash, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 2(b) generate a padding string of all zeros equal to the amount of unused space.
                    ps.Clear();

                    // 2(c)
                    psEnd[0] = 0x01;

                    // still 2(c)
                    source.CopyTo(mDest);

                    // 2(d)
                    RandomNumberGenerator.Fill(seed);

                    // 2(e)
                    dbMask     = CryptoPool.Rent(db.Length);
                    dbMaskSpan = new Span <byte>(dbMask, 0, db.Length);
                    Mgf1(hasher, seed, dbMaskSpan);

                    // 2(f)
                    Xor(db, dbMaskSpan);

                    // 2(g)
                    Span <byte> seedMask = stackalloc byte[_hLen];
                    Mgf1(hasher, db, seedMask);

                    // 2(h)
                    Xor(seed, seedMask);

                    // 2(i)
                    destination[0] = 0;
                }
            }
            catch (Exception e) when(!(e is CryptographicException))
            {
                Debug.Fail("Bad exception produced from OAEP padding: " + e);
                throw new CryptographicException();
            }
            finally
            {
                if (dbMask != null)
                {
                    CryptographicOperations.ZeroMemory(dbMaskSpan);
                    CryptoPool.Return(dbMask, clearSize: 0);
                }
            }
        }
Пример #10
0
        private static bool TryDecrypt(
            SafeRsaHandle key,
            ReadOnlySpan <byte> data,
            Span <byte> destination,
            Interop.Crypto.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.Crypto.RsaPadding.NoPadding) ==
                (rsaPaddingProcessor != null));

            // Caller should have already checked this.
            Debug.Assert(!key.IsInvalid);

            int rsaSize = Interop.Crypto.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 = ArrayPool <byte> .Shared.Rent(rsaSize);

                decryptBuf = paddingBuf;
            }

            try
            {
                int returnValue = Interop.Crypto.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);
                    ArrayPool <byte> .Shared.Return(paddingBuf);
                }
            }
        }
        internal bool VerifyPss(ReadOnlySpan <byte> mHash, ReadOnlySpan <byte> em, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.2

            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                return(false);
            }

            Debug.Assert(em.Length >= emLen);

            // In this implementation, sLen is restricted to hLen.
            int sLen = _hLen;

            // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
            if (emLen < _hLen + sLen + 2)
            {
                return(false);
            }

            // 4. If the last byte is not 0xBC, output "inconsistent" and stop.
            if (em[em.Length - 1] != 0xBC)
            {
                return(false);
            }

            // 5. maskedDB is the leftmost emLen - hLen -1 bytes, H is the next hLen bytes.
            int dbLen = emLen - _hLen - 1;

            ReadOnlySpan <byte> maskedDb = em.Slice(0, dbLen);
            ReadOnlySpan <byte> h        = em.Slice(dbLen, _hLen);

            // 6. If the unused bits aren't zero, output "inconsistent" and stop.
            int  unusedBits   = 8 * emLen - emBits;
            byte usedBitsMask = (byte)(0xFF >> unusedBits);

            if ((maskedDb[0] & usedBitsMask) != maskedDb[0])
            {
                return(false);
            }

            // 7. dbMask = MGF(H, emLen - hLen - 1)
            byte[]      dbMaskRented = CryptoPool.Rent(maskedDb.Length);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, maskedDb.Length);

            try
            {
                using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
                {
                    Mgf1(hasher, h, dbMask);

                    // 8. DB = maskedDB XOR dbMask
                    Xor(dbMask, maskedDb);

                    // 9. Set the unused bits of DB to 0
                    dbMask[0] &= usedBitsMask;

                    // 10 ("a"): If the emLen - hLen - sLen - 2 leftmost bytes are not 0,
                    // output "inconsistent" and stop.
                    //
                    // Since signature verification is a public key operation there's no need to
                    // use fixed time equality checking here.
                    for (int i = emLen - _hLen - sLen - 2 - 1; i >= 0; --i)
                    {
                        if (dbMask[i] != 0)
                        {
                            return(false);
                        }
                    }

                    // 10 ("b") If the octet at position emLen - hLen - sLen - 1 (under a 1-indexed scheme)
                    // is not 0x01, output "inconsistent" and stop.
                    if (dbMask[emLen - _hLen - sLen - 2] != 0x01)
                    {
                        return(false);
                    }

                    // 11. Let salt be the last sLen octets of DB.
                    ReadOnlySpan <byte> salt = dbMask.Slice(dbMask.Length - sLen);

                    // 12/13. Let H' = Hash(eight zeros || mHash || salt)
                    hasher.AppendData(EightZeros);
                    hasher.AppendData(mHash);
                    hasher.AppendData(salt);

                    Span <byte> hPrime = stackalloc byte[_hLen];

                    if (!hasher.TryGetHashAndReset(hPrime, out int hLen2) || hLen2 != _hLen)
                    {
                        Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                        throw new CryptographicException();
                    }

                    // 14. If H = H' output "consistent". Otherwise, output "inconsistent"
                    //
                    // Since this is a public key operation, no need to provide fixed time
                    // checking.
                    return(h.SequenceEqual(hPrime));
                }
            }
            finally
            {
                CryptographicOperations.ZeroMemory(dbMask);
                CryptoPool.Return(dbMaskRented, clearSize: 0);
            }
        }
        internal void EncodePss(ReadOnlySpan <byte> mHash, Span <byte> destination, int keySize)
        {
            // https://tools.ietf.org/html/rfc3447#section-9.1.1
            int emBits = keySize - 1;
            int emLen  = BytesRequiredForBitCount(emBits);

            if (mHash.Length != _hLen)
            {
                throw new CryptographicException(SR.Cryptography_SignHash_WrongSize);
            }

            // In this implementation, sLen is restricted to the length of the input hash.
            int sLen = _hLen;

            // 3.  if emLen < hLen + sLen + 2, encoding error.
            //
            // sLen = hLen in this implementation.

            if (emLen < 2 + _hLen + sLen)
            {
                throw new CryptographicException(SR.Cryptography_KeyTooSmall);
            }

            // Set any leading bytes to zero, since that will be required for the pending
            // RSA operation.
            destination.Slice(0, destination.Length - emLen).Clear();

            // 12. Let EM = maskedDB || H || 0xbc (H has length hLen)
            Span <byte> em = destination.Slice(destination.Length - emLen, emLen);

            int dbLen = emLen - _hLen - 1;

            Span <byte> db    = em.Slice(0, dbLen);
            Span <byte> hDest = em.Slice(dbLen, _hLen);

            em[emLen - 1] = 0xBC;

            byte[]      dbMaskRented = CryptoPool.Rent(dbLen);
            Span <byte> dbMask       = new Span <byte>(dbMaskRented, 0, dbLen);

            using (IncrementalHash hasher = IncrementalHash.CreateHash(_hashAlgorithmName))
            {
                // 4. Generate a random salt of length sLen
                Span <byte> salt = stackalloc byte[sLen];
                RandomNumberGenerator.Fill(salt);

                // 5. Let M' = an octet string of 8 zeros concat mHash concat salt
                // 6. Let H = Hash(M')

                hasher.AppendData(EightZeros);
                hasher.AppendData(mHash);
                hasher.AppendData(salt);

                if (!hasher.TryGetHashAndReset(hDest, out int hLen2) || hLen2 != _hLen)
                {
                    Debug.Fail("TryGetHashAndReset failed with exact-size destination");
                    throw new CryptographicException();
                }

                // 7. Generate PS as zero-valued bytes of length emLen - sLen - hLen - 2.
                // 8. Let DB = PS || 0x01 || salt
                int psLen = emLen - sLen - _hLen - 2;
                db.Slice(0, psLen).Clear();
                db[psLen] = 0x01;
                salt.CopyTo(db.Slice(psLen + 1));

                // 9. Let dbMask = MGF(H, emLen - hLen - 1)
                Mgf1(hasher, hDest, dbMask);

                // 10. Let maskedDB = DB XOR dbMask
                Xor(db, dbMask);

                // 11. Set the "unused" bits in the leftmost byte of maskedDB to 0.
                int unusedBits = 8 * emLen - emBits;

                if (unusedBits != 0)
                {
                    byte mask = (byte)(0xFF >> unusedBits);
                    db[0] &= mask;
                }
            }

            CryptographicOperations.ZeroMemory(dbMask);
            CryptoPool.Return(dbMaskRented, clearSize: 0);
        }
        internal static unsafe Pkcs8Response ImportEncryptedPkcs8PrivateKey(
            ReadOnlySpan <char> password,
            ReadOnlySpan <byte> source,
            out int bytesRead)
        {
            try
            {
                AsnDecoder.ReadEncodedValue(
                    source,
                    AsnEncodingRules.BER,
                    out _,
                    out _,
                    out int len);

                source = source.Slice(0, len);

                fixed(byte *ptr = &MemoryMarshal.GetReference(source))
                {
                    using (MemoryManager <byte> manager = new PointerMemoryManager <byte>(ptr, source.Length))
                    {
                        try
                        {
                            bytesRead = len;
                            return(ImportPkcs8(source, password));
                        }
                        catch (CryptographicException)
                        {
                        }

                        ArraySegment <byte> decrypted = KeyFormatHelper.DecryptPkcs8(
                            password,
                            manager.Memory.Slice(0, len),
                            out int innerRead);

                        Span <byte> decryptedSpan = decrypted;

                        try
                        {
                            if (innerRead != len)
                            {
                                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                            }

                            bytesRead = len;
                            return(ImportPkcs8(decryptedSpan));
                        }
                        catch (CryptographicException e)
                        {
                            AsnWriter?pkcs8ZeroPublicKey = RewritePkcs8ECPrivateKeyWithZeroPublicKey(decryptedSpan);

                            if (pkcs8ZeroPublicKey == null)
                            {
                                throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
                            }

                            try
                            {
                                bytesRead = len;
                                return(ImportPkcs8(pkcs8ZeroPublicKey));
                            }
                            catch (CryptographicException)
                            {
                                throw new CryptographicException(SR.Cryptography_Pkcs8_EncryptedReadFailed, e);
                            }
                        }
                        finally
                        {
                            CryptographicOperations.ZeroMemory(decryptedSpan);
                            CryptoPool.Return(decrypted.Array !, clearSize: 0);
                        }
                    }
                }
            }
            catch (AsnContentException e)
            {
                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
            }
        }
Пример #14
0
            public override bool TryEncrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, out int bytesWritten)
            {
                if (padding == null)
                {
                    throw new ArgumentNullException(nameof(padding));
                }

                ThrowIfDisposed();

                int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize);

                if (destination.Length < rsaSize)
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (padding == RSAEncryptionPadding.Pkcs1 && data.Length > 0)
                {
                    const int Pkcs1PaddingOverhead = 11;
                    int       maxAllowed           = rsaSize - Pkcs1PaddingOverhead;

                    if (data.Length > maxAllowed)
                    {
                        throw new CryptographicException(
                                  SR.Format(SR.Cryptography_Encryption_MessageTooLong, maxAllowed));
                    }

                    return(Interop.AppleCrypto.TryRsaEncrypt(
                               GetKeys().PublicKey,
                               data,
                               destination,
                               padding,
                               out bytesWritten));
                }

                RsaPaddingProcessor processor;

                switch (padding.Mode)
                {
                case RSAEncryptionPaddingMode.Pkcs1:
                    processor = null;
                    break;

                case RSAEncryptionPaddingMode.Oaep:
                    processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm);
                    break;

                default:
                    throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
                }

                byte[]      rented = CryptoPool.Rent(rsaSize);
                Span <byte> tmp    = new Span <byte>(rented, 0, rsaSize);

                try
                {
                    if (processor != null)
                    {
                        processor.PadOaep(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);
                }
            }
Пример #15
0
            public override bool TryDecrypt(
                ReadOnlySpan <byte> data,
                Span <byte> destination,
                RSAEncryptionPadding padding,
                out int bytesWritten)
            {
                ArgumentNullException.ThrowIfNull(padding);

                Interop.AndroidCrypto.RsaPadding rsaPadding = GetInteropPadding(padding);
                SafeRsaHandle key = GetKey();

                int keySizeBytes = Interop.AndroidCrypto.RsaSize(key);

                // Android does not take a length value for the destination, so it can write out of bounds.
                // To prevent the OOB write, decrypt into a temporary buffer.
                if (destination.Length < keySizeBytes)
                {
                    scoped Span <byte> tmp;
                    byte[]? rent = null;

                    // RSA up through 4096 stackalloc
                    if (keySizeBytes <= 512)
                    {
                        tmp = stackalloc byte[keySizeBytes];
                    }
                    else
                    {
                        rent = ArrayPool <byte> .Shared.Rent(keySizeBytes);

                        tmp = rent;
                    }

                    bool ret = TryDecrypt(key, data, tmp, rsaPadding, out bytesWritten);

                    if (ret)
                    {
                        tmp = tmp.Slice(0, bytesWritten);

                        if (bytesWritten > destination.Length)
                        {
                            ret          = false;
                            bytesWritten = 0;
                        }
                        else
                        {
                            tmp.CopyTo(destination);
                        }

                        CryptographicOperations.ZeroMemory(tmp);
                    }

                    if (rent != null)
                    {
                        // Already cleared
                        ArrayPool <byte> .Shared.Return(rent);
                    }

                    return(ret);
                }

                return(TryDecrypt(key, data, destination, rsaPadding, out bytesWritten));
            }
Пример #16
0
            public override bool TrySignHash(ReadOnlySpan <byte> hash, Span <byte> destination, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding, out int bytesWritten)
            {
                if (string.IsNullOrEmpty(hashAlgorithm.Name))
                {
                    throw HashAlgorithmNameNullOrEmpty();
                }
                if (padding == null)
                {
                    throw new ArgumentNullException(nameof(padding));
                }

                ThrowIfDisposed();

                RsaPaddingProcessor processor = null;

                if (padding.Mode == RSASignaturePaddingMode.Pss)
                {
                    processor = RsaPaddingProcessor.OpenProcessor(hashAlgorithm);
                }
                else if (padding != RSASignaturePadding.Pkcs1)
                {
                    throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
                }

                SecKeyPair keys = GetKeys();

                if (keys.PrivateKey == null)
                {
                    throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
                }

                int keySize = KeySize;
                int rsaSize = RsaPaddingProcessor.BytesRequiredForBitCount(keySize);

                if (processor == null)
                {
                    Interop.AppleCrypto.PAL_HashAlgorithm palAlgId =
                        PalAlgorithmFromAlgorithmName(hashAlgorithm, out int expectedSize);

                    if (hash.Length != expectedSize)
                    {
                        // Windows: NTE_BAD_DATA ("Bad Data.")
                        // OpenSSL: RSA_R_INVALID_MESSAGE_LENGTH ("invalid message length")
                        throw new CryptographicException(
                                  SR.Format(
                                      SR.Cryptography_BadHashSize_ForAlgorithm,
                                      hash.Length,
                                      expectedSize,
                                      hashAlgorithm.Name));
                    }

                    if (destination.Length < rsaSize)
                    {
                        bytesWritten = 0;
                        return(false);
                    }

                    return(Interop.AppleCrypto.TryGenerateSignature(
                               keys.PrivateKey,
                               hash,
                               destination,
                               palAlgId,
                               out bytesWritten));
                }

                Debug.Assert(padding.Mode == RSASignaturePaddingMode.Pss);

                if (destination.Length < rsaSize)
                {
                    bytesWritten = 0;
                    return(false);
                }

                byte[]      rented = CryptoPool.Rent(rsaSize);
                Span <byte> buf    = new Span <byte>(rented, 0, rsaSize);

                processor.EncodePss(hash, buf, keySize);

                try
                {
                    return(Interop.AppleCrypto.TryRsaSignaturePrimitive(keys.PrivateKey, buf, destination, out bytesWritten));
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(buf);
                    CryptoPool.Return(rented, clearSize: 0);
                }
            }
Пример #17
0
        internal static unsafe bool ExportPkcs8KeyBlob(
            bool allocate,
            SafeNCryptKeyHandle keyHandle,
            ReadOnlySpan <char> password,
            int kdfCount,
            Span <byte> destination,
            out int bytesWritten,
            out byte[] allocated)
        {
            using (SafeUnicodeStringHandle stringHandle = new SafeUnicodeStringHandle(password))
            {
                fixed(byte *oidPtr = s_pkcs12TripleDesOidBytes)
                {
                    Interop.NCrypt.NCryptBuffer *buffers = stackalloc Interop.NCrypt.NCryptBuffer[3];

                    Interop.NCrypt.PBE_PARAMS pbeParams = default;
                    Span <byte> salt = new Span <byte>(pbeParams.rgbSalt, Interop.NCrypt.PBE_PARAMS.RgbSaltSize);

                    RandomNumberGenerator.Fill(salt);
                    pbeParams.Params.cbSalt      = salt.Length;
                    pbeParams.Params.iIterations = kdfCount;

                    buffers[0] = new Interop.NCrypt.NCryptBuffer
                    {
                        BufferType = Interop.NCrypt.BufferType.PkcsSecret,
                        cbBuffer   = checked (2 * (password.Length + 1)),
                        pvBuffer   = stringHandle.DangerousGetHandle(),
                    };

                    if (buffers[0].pvBuffer == IntPtr.Zero)
                    {
                        buffers[0].cbBuffer = 0;
                    }

                    buffers[1] = new Interop.NCrypt.NCryptBuffer
                    {
                        BufferType = Interop.NCrypt.BufferType.PkcsAlgOid,
                        cbBuffer   = s_pkcs12TripleDesOidBytes.Length,
                        pvBuffer   = (IntPtr)oidPtr,
                    };

                    buffers[2] = new Interop.NCrypt.NCryptBuffer
                    {
                        BufferType = Interop.NCrypt.BufferType.PkcsAlgParam,
                        cbBuffer   = sizeof(Interop.NCrypt.PBE_PARAMS),
                        pvBuffer   = (IntPtr)(&pbeParams),
                    };

                    Interop.NCrypt.NCryptBufferDesc desc = new Interop.NCrypt.NCryptBufferDesc
                    {
                        cBuffers  = 3,
                        pBuffers  = (IntPtr)buffers,
                        ulVersion = 0,
                    };

                    Span <byte> empty = default;

                    ErrorCode errorCode = Interop.NCrypt.NCryptExportKey(
                        keyHandle,
                        IntPtr.Zero,
                        Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
                        ref desc,
                        ref MemoryMarshal.GetReference(empty),
                        0,
                        out int numBytesNeeded,
                        0);

                    if (errorCode != ErrorCode.ERROR_SUCCESS)
                    {
                        throw errorCode.ToCryptographicException();
                    }

                    allocated = null;

                    if (allocate)
                    {
                        allocated   = new byte[numBytesNeeded];
                        destination = allocated;
                    }
                    else if (numBytesNeeded > destination.Length)
                    {
                        bytesWritten = 0;
                        return(false);
                    }

                    errorCode = Interop.NCrypt.NCryptExportKey(
                        keyHandle,
                        IntPtr.Zero,
                        Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB,
                        ref desc,
                        ref MemoryMarshal.GetReference(destination),
                        destination.Length,
                        out numBytesNeeded,
                        0);

                    if (errorCode != ErrorCode.ERROR_SUCCESS)
                    {
                        throw errorCode.ToCryptographicException();
                    }

                    if (allocate && numBytesNeeded != destination.Length)
                    {
                        byte[] trimmed = new byte[numBytesNeeded];
                        destination.Slice(0, numBytesNeeded).CopyTo(trimmed);
                        CryptographicOperations.ZeroMemory(allocated.AsSpan(0, numBytesNeeded));
                        allocated = trimmed;
                    }

                    bytesWritten = numBytesNeeded;
                    return(true);
                }
            }
        }
Пример #18
0
            public override RSAParameters ExportParameters(bool includePrivateParameters)
            {
                // Apple requires all private keys to be exported encrypted, but since we're trying to export
                // as parsed structures we will need to decrypt it for the user.
                const string ExportPassword = "******";
                SecKeyPair   keys           = GetKeys();

                if (includePrivateParameters && keys.PrivateKey == null)
                {
                    throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
                }

                byte[] keyBlob = Interop.AppleCrypto.SecKeyExport(
                    includePrivateParameters ? keys.PrivateKey : keys.PublicKey,
                    exportPrivate: includePrivateParameters,
                    password: ExportPassword);

                try
                {
                    if (!includePrivateParameters)
                    {
                        // When exporting a key handle opened from a certificate, it seems to
                        // export as a PKCS#1 blob instead of an X509 SubjectPublicKeyInfo blob.
                        // So, check for that.
                        // NOTE: It doesn't affect macOS Mojave when SecCertificateCopyKey API
                        // is used.
                        RSAParameters key;

                        AsnReader reader         = new AsnReader(keyBlob, AsnEncodingRules.BER);
                        AsnReader sequenceReader = reader.ReadSequence();

                        if (sequenceReader.PeekTag().Equals(Asn1Tag.Integer))
                        {
                            AlgorithmIdentifierAsn ignored = default;
                            RSAKeyFormatHelper.ReadRsaPublicKey(keyBlob, ignored, out key);
                        }
                        else
                        {
                            RSAKeyFormatHelper.ReadSubjectPublicKeyInfo(
                                keyBlob,
                                out int localRead,
                                out key);
                            Debug.Assert(localRead == keyBlob.Length);
                        }
                        return(key);
                    }
                    else
                    {
                        RSAKeyFormatHelper.ReadEncryptedPkcs8(
                            keyBlob,
                            ExportPassword,
                            out int localRead,
                            out RSAParameters key);
                        return(key);
                    }
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(keyBlob);
                }
            }
Пример #19
0
 public void Dispose()
 {
     CryptographicOperations.ZeroMemory(_key);
 }