예제 #1
0
        internal static unsafe int Wrap(SafeDeleteContext securityContext, ReadOnlySpan <byte> buffer, [NotNull] ref byte[]?output, bool isConfidential)
        {
            SecPkgContext_Sizes sizes = default;
            bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);

            Debug.Assert(success);

            // alloc new output buffer if not supplied or too small
            int resultSize = buffer.Length + sizes.cbMaxSignature;

            if (output == null || output.Length < resultSize)
            {
                output = new byte[resultSize];
            }

            // make a copy of user data for in-place encryption
            buffer.CopyTo(output.AsSpan(sizes.cbMaxSignature, buffer.Length));

            fixed(byte *outputPtr = output)
            {
                // Prepare buffers TOKEN(signature), DATA and Padding.
                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
                Interop.SspiCli.SecBuffer *tokenBuffer     = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
                tokenBuffer->pvBuffer   = (IntPtr)(outputPtr);
                tokenBuffer->cbBuffer   = sizes.cbMaxSignature;
                dataBuffer->BufferType  = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer    = (IntPtr)(outputPtr + sizes.cbMaxSignature);
                dataBuffer->cbBuffer    = buffer.Length;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
                {
                    pBuffers = unmanagedBuffer
                };

                uint qop       = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
                int  errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop);

                if (errorCode != 0)
                {
                    Exception e = new Win32Exception(errorCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, e);
                    }
                    throw new Win32Exception(errorCode);
                }

                // return signed size
                return(tokenBuffer->cbBuffer + dataBuffer->cbBuffer);
            }
        }
예제 #2
0
        public static unsafe SecurityStatusPal DecryptMessage(SafeDeleteContext securityContext, byte[] buffer, ref int offset, ref int count)
        {
            const int NumSecBuffers   = 4; // data + empty + empty + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *bufferPtr = buffer)
            {
                Interop.SspiCli.SecBuffer *dataBuffer = &unmanagedBuffer[0];
                dataBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer   = (IntPtr)bufferPtr + offset;
                dataBuffer->cbBuffer   = count;

                for (int i = 1; i < NumSecBuffers; i++)
                {
                    Interop.SspiCli.SecBuffer *emptyBuffer = &unmanagedBuffer[i];
                    emptyBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                    emptyBuffer->pvBuffer   = IntPtr.Zero;
                    emptyBuffer->cbBuffer   = 0;
                }

                Interop.SECURITY_STATUS errorCode = (Interop.SECURITY_STATUS)GlobalSSPI.SSPISecureChannel.DecryptMessage(securityContext, ref sdcInOut, 0);

                // Decrypt may repopulate the sec buffers, likely with header + data + trailer + empty.
                // We need to find the data.
                count = 0;
                for (int i = 0; i < NumSecBuffers; i++)
                {
                    // Successfully decoded data and placed it at the following position in the buffer,
                    if ((errorCode == Interop.SECURITY_STATUS.OK && unmanagedBuffer[i].BufferType == SecurityBufferType.SECBUFFER_DATA)
                        // or we failed to decode the data, here is the encoded data.
                        || (errorCode != Interop.SECURITY_STATUS.OK && unmanagedBuffer[i].BufferType == SecurityBufferType.SECBUFFER_EXTRA))
                    {
                        offset = (int)((byte *)unmanagedBuffer[i].pvBuffer - bufferPtr);
                        count  = unmanagedBuffer[i].cbBuffer;

                        Debug.Assert(offset >= 0 && count >= 0, $"Expected offset and count greater than 0, got {offset} and {count}");
                        Debug.Assert(checked (offset + count) <= buffer.Length, $"Expected offset+count <= buffer.Length, got {offset}+{count}>={buffer.Length}");

                        break;
                    }
                }

                return(SecurityStatusAdapterPal.GetSecurityStatusPalFromInterop(errorCode));
            }
        }
예제 #3
0
        internal static unsafe int Unwrap(SafeDeleteContext securityContext, Span <byte> buffer, out int newOffset, out bool wasConfidential)
        {
            fixed(byte *bufferPtr = buffer)
            {
                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
                Interop.SspiCli.SecBuffer *streamBuffer    = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                streamBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM;
                streamBuffer->pvBuffer   = (IntPtr)bufferPtr;
                streamBuffer->cbBuffer   = buffer.Length;
                dataBuffer->BufferType   = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer     = IntPtr.Zero;
                dataBuffer->cbBuffer     = 0;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
                {
                    pBuffers = unmanagedBuffer
                };

                uint qop;
                int  errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);

                if (errorCode != 0)
                {
                    Exception e = new Win32Exception(errorCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, e);
                    }
                    throw new Win32Exception(errorCode);
                }

                if (dataBuffer->BufferType != SecurityBufferType.SECBUFFER_DATA)
                {
                    throw new InternalException(dataBuffer->BufferType);
                }

                wasConfidential = qop != Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;

                Debug.Assert((nint)dataBuffer->pvBuffer >= (nint)bufferPtr);
                Debug.Assert((nint)dataBuffer->pvBuffer + dataBuffer->cbBuffer <= (nint)bufferPtr + buffer.Length);
                newOffset = (int)((byte *)dataBuffer->pvBuffer - bufferPtr);
                return(dataBuffer->cbBuffer);
            }
        }
예제 #4
0
        internal static unsafe NegotiateAuthenticationStatusCode UnwrapInPlace(
            SafeDeleteContext securityContext,
            Span <byte> input,
            out int unwrappedOffset,
            out int unwrappedLength,
            out bool wasEncrypted)
        {
            fixed(byte *inputPtr = input)
            {
                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
                Interop.SspiCli.SecBuffer *streamBuffer    = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                streamBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM;
                streamBuffer->pvBuffer   = (IntPtr)inputPtr;
                streamBuffer->cbBuffer   = input.Length;
                dataBuffer->BufferType   = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer     = IntPtr.Zero;
                dataBuffer->cbBuffer     = 0;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
                {
                    pBuffers = unmanagedBuffer
                };

                uint qop;
                int  errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);

                if (errorCode != 0)
                {
                    unwrappedOffset = 0;
                    unwrappedLength = 0;
                    wasEncrypted    = false;
                    return(errorCode switch
                    {
                        (int)Interop.SECURITY_STATUS.MessageAltered => NegotiateAuthenticationStatusCode.MessageAltered,
                        _ => NegotiateAuthenticationStatusCode.InvalidToken
                    });
예제 #5
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, ReadOnlyMemory <byte> input, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;

            try
            {
                bufferSizeNeeded = checked (input.Length + headerSize + trailerSize);
            }
            catch
            {
                NetEventSource.Fail(securityContext, "Arguments out of range");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            input.Span.CopyTo(new Span <byte>(output, headerSize, input.Length));

            const int NumSecBuffers   = 4; // header + data + trailer + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *outputPtr = output)
            {
                Interop.SspiCli.SecBuffer *headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer   = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer   = headerSize;

                Interop.SspiCli.SecBuffer *dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer   = input.Length;

                Interop.SspiCli.SecBuffer *trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize + input.Length);
                trailerSecBuffer->cbBuffer   = trailerSize;

                Interop.SspiCli.SecBuffer *emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer   = 0;
                emptySecBuffer->pvBuffer   = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, ref sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(securityContext, $"Encrypt ERROR {errorCode:X}");
                    }
                    resultSize = 0;
                    return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.OK));
            }
        }
예제 #6
0
        private static unsafe int DecryptNtlm(
            SafeDeleteContext securityContext,
            Span <byte> buffer,
            bool isConfidential,
            out int newOffset)
        {
            const int NtlmSignatureLength = 16;

            // For the most part the arguments are verified in Decrypt().
            if (buffer.Length < NtlmSignatureLength)
            {
                Debug.Fail("Argument 'count' out of range.");
                throw new Win32Exception((int)Interop.SECURITY_STATUS.InvalidToken);
            }

            fixed(byte *bufferPtr = buffer)
            {
                SecurityBufferType realDataType = SecurityBufferType.SECBUFFER_DATA;

                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
                Interop.SspiCli.SecBuffer *tokenBuffer     = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                tokenBuffer->BufferType = SecurityBufferType.SECBUFFER_TOKEN;
                tokenBuffer->pvBuffer   = (IntPtr)bufferPtr;
                tokenBuffer->cbBuffer   = NtlmSignatureLength;
                dataBuffer->BufferType  = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer    = (IntPtr)(bufferPtr + NtlmSignatureLength);
                dataBuffer->cbBuffer    = buffer.Length - NtlmSignatureLength;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
                {
                    pBuffers = unmanagedBuffer
                };
                uint qop;
                int  errorCode;

                if (!isConfidential)
                {
                    realDataType          |= SecurityBufferType.SECBUFFER_READONLY;
                    dataBuffer->BufferType = realDataType;
                }

                errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);

                if (errorCode != 0)
                {
                    Exception e = new Win32Exception(errorCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, e);
                    }
                    throw new Win32Exception(errorCode);
                }

                if (isConfidential && qop == Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT)
                {
                    Debug.Fail($"Expected qop = 0, returned value = {qop}");
                    throw new InvalidOperationException(SR.net_auth_message_not_encrypted);
                }

                if (dataBuffer->BufferType != realDataType)
                {
                    throw new InternalException(dataBuffer->BufferType);
                }

                Debug.Assert((nint)dataBuffer->pvBuffer >= (nint)bufferPtr);
                Debug.Assert((nint)dataBuffer->pvBuffer + dataBuffer->cbBuffer <= (nint)bufferPtr + buffer.Length);
                newOffset = (int)((byte *)dataBuffer->pvBuffer - bufferPtr);
                return(dataBuffer->cbBuffer);
            }
        }
예제 #7
0
        internal static unsafe int Decrypt(
            SafeDeleteContext securityContext,
            Span <byte> buffer,
            bool isConfidential,
            bool isNtlm,
            out int newOffset)
        {
            if (isNtlm)
            {
                return(DecryptNtlm(securityContext, buffer, isConfidential, out newOffset));
            }

            //
            // Kerberos and up
            //
            fixed(byte *bufferPtr = buffer)
            {
                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[2];
                Interop.SspiCli.SecBuffer *streamBuffer    = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                streamBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM;
                streamBuffer->pvBuffer   = (IntPtr)bufferPtr;
                streamBuffer->cbBuffer   = buffer.Length;
                dataBuffer->BufferType   = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer     = IntPtr.Zero;
                dataBuffer->cbBuffer     = 0;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(2)
                {
                    pBuffers = unmanagedBuffer
                };

                uint qop;
                int  errorCode = GlobalSSPI.SSPIAuth.DecryptMessage(securityContext, ref sdcInOut, out qop);

                if (errorCode != 0)
                {
                    Exception e = new Win32Exception(errorCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, e);
                    }
                    throw new Win32Exception(errorCode);
                }

                if (qop == Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT && isConfidential)
                {
                    Debug.Fail($"Expected qop = 0, returned value = {qop}");
                    throw new InvalidOperationException(SR.net_auth_message_not_encrypted);
                }

                if (dataBuffer->BufferType != SecurityBufferType.SECBUFFER_DATA)
                {
                    throw new InternalException(dataBuffer->BufferType);
                }

                Debug.Assert((nint)dataBuffer->pvBuffer >= (nint)bufferPtr);
                Debug.Assert((nint)dataBuffer->pvBuffer + dataBuffer->cbBuffer <= (nint)bufferPtr + buffer.Length);
                newOffset = (int)((byte *)dataBuffer->pvBuffer - bufferPtr);
                return(dataBuffer->cbBuffer);
            }
        }
예제 #8
0
        internal static unsafe int Encrypt(
            SafeDeleteContext securityContext,
            ReadOnlySpan <byte> buffer,
            bool isConfidential,
            bool isNtlm,
            [NotNull] ref byte[]?output)
        {
            SecPkgContext_Sizes sizes = default;
            bool success = SSPIWrapper.QueryBlittableContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SIZES, ref sizes);

            Debug.Assert(success);

            int maxCount = checked (int.MaxValue - 4 - sizes.cbBlockSize - sizes.cbSecurityTrailer);

            if (buffer.Length > maxCount)
            {
                throw new ArgumentOutOfRangeException(nameof(buffer.Length), SR.Format(SR.net_io_out_range, maxCount));
            }

            int resultSize = buffer.Length + sizes.cbSecurityTrailer + sizes.cbBlockSize;

            if (output == null || output.Length < resultSize + 4)
            {
                output = new byte[resultSize + 4];
            }

            // Make a copy of user data for in-place encryption.
            buffer.CopyTo(output.AsSpan(4 + sizes.cbSecurityTrailer));

            fixed(byte *outputPtr = output)
            {
                // Prepare buffers TOKEN(signature), DATA and Padding.
                Interop.SspiCli.SecBuffer *unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[3];
                Interop.SspiCli.SecBuffer *tokenBuffer     = &unmanagedBuffer[0];
                Interop.SspiCli.SecBuffer *dataBuffer      = &unmanagedBuffer[1];
                Interop.SspiCli.SecBuffer *paddingBuffer   = &unmanagedBuffer[2];
                tokenBuffer->BufferType   = SecurityBufferType.SECBUFFER_TOKEN;
                tokenBuffer->pvBuffer     = (IntPtr)(outputPtr + 4);
                tokenBuffer->cbBuffer     = sizes.cbSecurityTrailer;
                dataBuffer->BufferType    = SecurityBufferType.SECBUFFER_DATA;
                dataBuffer->pvBuffer      = (IntPtr)(outputPtr + 4 + sizes.cbSecurityTrailer);
                dataBuffer->cbBuffer      = buffer.Length;
                paddingBuffer->BufferType = SecurityBufferType.SECBUFFER_PADDING;
                paddingBuffer->pvBuffer   = (IntPtr)(outputPtr + 4 + sizes.cbSecurityTrailer + buffer.Length);
                paddingBuffer->cbBuffer   = sizes.cbBlockSize;

                Interop.SspiCli.SecBufferDesc sdcInOut = new Interop.SspiCli.SecBufferDesc(3)
                {
                    pBuffers = unmanagedBuffer
                };

                if (isNtlm && !isConfidential)
                {
                    dataBuffer->BufferType |= SecurityBufferType.SECBUFFER_READONLY;
                }

                uint qop       = isConfidential ? 0 : Interop.SspiCli.SECQOP_WRAP_NO_ENCRYPT;
                int  errorCode = GlobalSSPI.SSPIAuth.EncryptMessage(securityContext, ref sdcInOut, qop);

                if (errorCode != 0)
                {
                    Exception e = new Win32Exception(errorCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, e);
                    }
                    throw new Win32Exception(errorCode);
                }

                // Compacting the result.
                resultSize = tokenBuffer->cbBuffer;
                bool forceCopy = false;

                if (resultSize != sizes.cbSecurityTrailer)
                {
                    forceCopy = true;
                    output.AsSpan(4 + sizes.cbSecurityTrailer, dataBuffer->cbBuffer).CopyTo(output.AsSpan(4 + resultSize, dataBuffer->cbBuffer));
                }

                resultSize += dataBuffer->cbBuffer;
                if (paddingBuffer->cbBuffer != 0 && (forceCopy || resultSize != (buffer.Length + sizes.cbSecurityTrailer)))
                {
                    output.AsSpan(4 + sizes.cbSecurityTrailer + buffer.Length, paddingBuffer->cbBuffer).CopyTo(output.AsSpan(4 + resultSize, paddingBuffer->cbBuffer));
                }

                resultSize += paddingBuffer->cbBuffer;
                BinaryPrimitives.WriteInt32LittleEndian(output, resultSize);

                return(resultSize + 4);
            }
        }
예제 #9
0
        public static unsafe SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] input, int offset, int size, int headerSize, int trailerSize, ref byte[] output, out int resultSize)
        {
            // Ensure that there is sufficient space for the message output.
            int bufferSizeNeeded;

            try
            {
                bufferSizeNeeded = checked (size + headerSize + trailerSize);
            }
            catch
            {
                if (GlobalLog.IsEnabled)
                {
                    GlobalLog.Assert("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range.");
                }
                Debug.Fail("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range.");
                throw;
            }
            if (output == null || output.Length < bufferSizeNeeded)
            {
                output = new byte[bufferSizeNeeded];
            }

            // Copy the input into the output buffer to prepare for SCHANNEL's expectations
            Buffer.BlockCopy(input, offset, output, headerSize, size);

            const int NumSecBuffers   = 4; // header + data + trailer + empty
            var       unmanagedBuffer = stackalloc Interop.SspiCli.SecBuffer[NumSecBuffers];
            var       sdcInOut        = new Interop.SspiCli.SecBufferDesc(NumSecBuffers);

            sdcInOut.pBuffers = unmanagedBuffer;
            fixed(byte *outputPtr = output)
            {
                Interop.SspiCli.SecBuffer *headerSecBuffer = &unmanagedBuffer[0];
                headerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_HEADER;
                headerSecBuffer->pvBuffer   = (IntPtr)outputPtr;
                headerSecBuffer->cbBuffer   = headerSize;

                Interop.SspiCli.SecBuffer *dataSecBuffer = &unmanagedBuffer[1];
                dataSecBuffer->BufferType = SecurityBufferType.SECBUFFER_DATA;
                dataSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize);
                dataSecBuffer->cbBuffer   = size;

                Interop.SspiCli.SecBuffer *trailerSecBuffer = &unmanagedBuffer[2];
                trailerSecBuffer->BufferType = SecurityBufferType.SECBUFFER_STREAM_TRAILER;
                trailerSecBuffer->pvBuffer   = (IntPtr)(outputPtr + headerSize + size);
                trailerSecBuffer->cbBuffer   = trailerSize;

                Interop.SspiCli.SecBuffer *emptySecBuffer = &unmanagedBuffer[3];
                emptySecBuffer->BufferType = SecurityBufferType.SECBUFFER_EMPTY;
                emptySecBuffer->cbBuffer   = 0;
                emptySecBuffer->pvBuffer   = IntPtr.Zero;

                int errorCode = GlobalSSPI.SSPISecureChannel.EncryptMessage(securityContext, sdcInOut, 0);

                if (errorCode != 0)
                {
                    if (GlobalLog.IsEnabled)
                    {
                        GlobalLog.Print("SslStreamPal.Windows: SecureChannel#" + LoggingHash.HashString(securityContext) + "::Encrypt ERROR" + errorCode.ToString("x"));
                    }

                    resultSize = 0;
                    return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
                }

                Debug.Assert(headerSecBuffer->cbBuffer >= 0 && dataSecBuffer->cbBuffer >= 0 && trailerSecBuffer->cbBuffer >= 0);
                Debug.Assert(checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer) <= output.Length);

                resultSize = checked (headerSecBuffer->cbBuffer + dataSecBuffer->cbBuffer + trailerSecBuffer->cbBuffer);
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.OK));
            }
        }