diff --git a/ByteWriter.cs b/ByteWriter.cs
new file mode 100644
index 0000000..088e591
--- /dev/null
+++ b/ByteWriter.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NSspi
+{
+ public static class ByteWriter
+ {
+ // Big endian: Most significant byte at lowest address in memory.
+
+ public static void WriteInt16_BE( Int16 value, byte[] buffer, int position )
+ {
+ buffer[position + 0] = (byte)( value >> 8 );
+ buffer[position + 1] = (byte)( value );
+ }
+
+ public static void WriteInt32_BE( Int32 value, byte[] buffer, int position )
+ {
+ buffer[position + 0] = (byte)( value >> 24 );
+ buffer[position + 1] = (byte)( value >> 16 );
+ buffer[position + 2] = (byte)( value >> 8 );
+ buffer[position + 3] = (byte)( value);
+
+ }
+
+ public static Int16 ReadInt16_BE( byte[] buffer, int position )
+ {
+ Int16 value;
+
+ value = (Int16)( buffer[position + 0] << 8 );
+ value += (Int16)( buffer[position + 1] );
+
+ return value;
+ }
+
+ public static Int32 ReadInt32_BE( byte[] buffer, int position )
+ {
+ Int32 value;
+
+ value = (Int32)( buffer[position + 0] << 24 );
+ value |= (Int32)( buffer[position + 1] << 16 );
+ value |= (Int32)( buffer[position + 2] << 8 );
+ value |= (Int32)( buffer[position + 3] );
+
+ return value;
+ }
+
+ }
+}
diff --git a/Contexts/Context.cs b/Contexts/Context.cs
index cd9d922..58b9873 100644
--- a/Contexts/Context.cs
+++ b/Contexts/Context.cs
@@ -72,12 +72,172 @@ namespace NSspi
/// byte array, which is formatted such that the first four bytes are the original message length
/// as an unsigned integer and the remaining bytes are the encrypted bytes of the original message.
///
+ ///
+ /// The resulting byte array stores the SSPI buffer data in the following buffer format:
+ /// - Token
+ /// - Data
+ /// - Padding
+ ///
///
///
public byte[] Encrypt( byte[] input )
{
// The message is encrypted in place in the buffer we provide to Win32 EncryptMessage
- return null;
+ SecPkgContext_Sizes sizes = QueryBufferSizes();
+
+ SecureBuffer trailerBuffer;
+ SecureBuffer dataBuffer;
+ SecureBuffer paddingBuffer;
+ SecureBufferAdapter adapter;
+
+ long contextHandle = this.ContextHandle;
+
+ SecurityStatus status;
+ byte[] result;
+
+ trailerBuffer = new SecureBuffer( new byte[sizes.SecurityTrailer], BufferType.Token );
+ dataBuffer = new SecureBuffer( new byte[input.Length], BufferType.Data );
+ paddingBuffer = new SecureBuffer( new byte[sizes.BlockSize], BufferType.Padding );
+
+ Array.Copy( input, dataBuffer.Buffer, input.Length );
+
+ using( adapter = new SecureBufferAdapter( new[] { trailerBuffer, dataBuffer, paddingBuffer } ) )
+ {
+ status = ContextNativeMethods.EncryptMessage(
+ ref contextHandle,
+ 0,
+ adapter.Handle,
+ 0
+ );
+ }
+
+ if( status != SecurityStatus.OK )
+ {
+ throw new SSPIException( "Failed to encrypt message", status );
+ }
+
+ int position = 0;
+
+ // Enough room to fit:
+ // -- 2 bytes for the trailer buffer size
+ // -- 4 bytes for the message size
+ // -- 2 bytes for the padding size.
+ // -- The encrypted message
+ result = new byte[2 + 4 + 2 + trailerBuffer.Length + dataBuffer.Length + paddingBuffer.Length];
+
+ ByteWriter.WriteInt16_BE( (short)trailerBuffer.Length, result, position );
+ position += 2;
+
+ ByteWriter.WriteInt32_BE( dataBuffer.Length, result, position );
+ position += 4;
+
+ ByteWriter.WriteInt16_BE( (short)paddingBuffer.Length, result, position );
+ position += 2;
+
+ Array.Copy( trailerBuffer.Buffer, 0, result, position, trailerBuffer.Length );
+ position += trailerBuffer.Length;
+
+ Array.Copy( dataBuffer.Buffer, 0, result, position, dataBuffer.Length );
+ position += dataBuffer.Length;
+
+ Array.Copy( paddingBuffer.Buffer, 0, result, position, paddingBuffer.Length );
+ position += paddingBuffer.Length;
+
+ return result;
+ }
+
+ public byte[] Decrypt( byte[] input )
+ {
+ SecPkgContext_Sizes sizes = QueryBufferSizes();
+
+ SecureBuffer trailerBuffer;
+ SecureBuffer dataBuffer;
+ SecureBuffer paddingBuffer;
+ SecureBufferAdapter adapter;
+
+ long contextHandle = this.ContextHandle;
+
+ SecurityStatus status;
+ byte[] result = null;
+ int remaining;
+ int position;
+
+ int trailerLength;
+ int dataLength;
+ int paddingLength;
+
+ // This check is required, but not sufficient. We could be stricter.
+ if( input.Length < 2 + 4 + 2 + sizes.SecurityTrailer )
+ {
+ throw new ArgumentException( "Buffer is too small to possibly contain an encrypted message" );
+ }
+
+ position = 0;
+
+ trailerLength = ByteWriter.ReadInt16_BE( input, position );
+ position += 2;
+
+ dataLength = ByteWriter.ReadInt32_BE( input, position );
+ position += 4;
+
+ paddingLength = ByteWriter.ReadInt16_BE( input, position );
+ position += 2;
+
+
+ trailerBuffer = new SecureBuffer( new byte[trailerLength], BufferType.Token );
+ dataBuffer = new SecureBuffer( new byte[dataLength], BufferType.Data );
+ paddingBuffer = new SecureBuffer( new byte[paddingLength], BufferType.Padding );
+
+ remaining = input.Length - position;
+
+ if( trailerBuffer.Length <= remaining )
+ {
+ Array.Copy( input, position, trailerBuffer.Buffer, 0, trailerBuffer.Length );
+ position += trailerBuffer.Length;
+ remaining -= trailerBuffer.Length;
+ }
+ else
+ {
+ throw new ArgumentException( "Input is missing data - it is not long enough to contain a fully encrypted message" );
+ }
+
+ if( dataBuffer.Length <= remaining )
+ {
+ Array.Copy( input, position, dataBuffer.Buffer, 0, dataBuffer.Length );
+ position += dataBuffer.Length;
+ remaining -= dataBuffer.Length;
+ }
+ else
+ {
+ throw new ArgumentException( "Input is missing data - it is not long enough to contain a fully encrypted message" );
+ }
+
+ if( paddingBuffer.Length <= remaining )
+ {
+ Array.Copy( input, position, paddingBuffer.Buffer, 0, paddingBuffer.Length );
+ }
+ // else there was no padding.
+
+
+ using( adapter = new SecureBufferAdapter( new [] { trailerBuffer, dataBuffer, paddingBuffer } ) )
+ {
+ status = ContextNativeMethods.DecryptMessage(
+ ref contextHandle,
+ adapter.Handle,
+ 0,
+ 0
+ );
+ }
+
+ if( status != SecurityStatus.OK )
+ {
+ throw new SSPIException( "Failed to encrypt message", status );
+ }
+
+ result = new byte[dataBuffer.Length];
+ Array.Copy( dataBuffer.Buffer, 0, result, 0, dataBuffer.Length );
+
+ return result;
}
internal SecPkgContext_Sizes QueryBufferSizes()
diff --git a/Contexts/ContextNativeMethods.cs b/Contexts/ContextNativeMethods.cs
index 7e1893d..bf9bb93 100644
--- a/Contexts/ContextNativeMethods.cs
+++ b/Contexts/ContextNativeMethods.cs
@@ -163,6 +163,21 @@ namespace NSspi.Contexts
int sequenceNumber
);
+
+ [DllImport(
+ "Secur32.dll",
+ EntryPoint = "DecryptMessage",
+ CallingConvention = CallingConvention.Winapi,
+ CharSet = CharSet.Unicode,
+ SetLastError = true
+ )]
+ public static extern SecurityStatus DecryptMessage(
+ ref long contextHandle,
+ IntPtr bufferDescriptor,
+ int sequenceNumber,
+ int qualityOfProtection
+ );
+
[DllImport(
"Secur32.dll",
EntryPoint = "QueryContextAttributes",
diff --git a/NSspi.csproj b/NSspi.csproj
index ddd141a..034826b 100644
--- a/NSspi.csproj
+++ b/NSspi.csproj
@@ -15,12 +15,12 @@
true
full
- false
+ true
bin\Debug\
DEBUG;TRACE
prompt
4
- true
+ false
pdbonly
@@ -45,6 +45,7 @@
+
@@ -71,9 +72,7 @@
-
-
-
+