diff --git a/NSspi/Contexts/Context.cs b/NSspi/Contexts/Context.cs
index 67525bd..92bc0a4 100644
--- a/NSspi/Contexts/Context.cs
+++ b/NSspi/Contexts/Context.cs
@@ -221,17 +221,18 @@ out token
/// Encrypts the byte array using the context's session key.
///
///
- /// The structure of the returned data is as follows:
- /// - 2 bytes, an unsigned big-endian integer indicating the length of the trailer buffer size
- /// - 4 bytes, an unsigned big-endian integer indicating the length of the message buffer size.
- /// - 2 bytes, an unsigned big-endian integer indicating the length of the encryption padding buffer size.
+ /// If is false, this is equivalent to calling and
+ /// the returned array contains encoded versions of the trailer, message and padding buffers.
+ ///
+ /// If is true the lengths will be ommitted and the returned array contains only:
/// - The trailer buffer
/// - The message buffer
/// - The padding buffer.
///
/// The raw message to encrypt.
+ /// Indicates if the returned data should contain only the encrypted data (true) or also an encoded version of the length of each buffer (false)
/// The packed and encrypted message.
- public byte[] Encrypt( byte[] input )
+ public byte[] Encrypt( byte[] input, bool stream )
{
// The message is encrypted in place in the buffer we provide to Win32 EncryptMessage
SecPkgContext_Sizes sizes;
@@ -276,16 +277,19 @@ public byte[] Encrypt( byte[] input )
// -- 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];
+ result = new byte[(stream ? 0 : (2 + 4 + 2)) + trailerBuffer.Length + dataBuffer.Length + paddingBuffer.Length];
- ByteWriter.WriteInt16_BE( (short)trailerBuffer.Length, result, position );
- position += 2;
+ if( !stream )
+ {
+ ByteWriter.WriteInt16_BE( (short)trailerBuffer.Length, result, position );
+ position += 2;
- ByteWriter.WriteInt32_BE( dataBuffer.Length, result, position );
- position += 4;
+ ByteWriter.WriteInt32_BE( dataBuffer.Length, result, position );
+ position += 4;
- ByteWriter.WriteInt16_BE( (short)paddingBuffer.Length, result, position );
- position += 2;
+ ByteWriter.WriteInt16_BE( (short)paddingBuffer.Length, result, position );
+ position += 2;
+ }
Array.Copy( trailerBuffer.Buffer, 0, result, position, trailerBuffer.Length );
position += trailerBuffer.Length;
@@ -299,6 +303,72 @@ public byte[] Encrypt( byte[] input )
return result;
}
+ ///
+ /// Encrypts the byte array using the context's session key.
+ ///
+ ///
+ /// The structure of the returned data is as follows:
+ /// - 2 bytes, an unsigned big-endian integer indicating the length of the trailer buffer size
+ /// - 4 bytes, an unsigned big-endian integer indicating the length of the message buffer size.
+ /// - 2 bytes, an unsigned big-endian integer indicating the length of the encryption padding buffer size.
+ /// - The trailer buffer
+ /// - The message buffer
+ /// - The padding buffer.
+ ///
+ /// The raw message to encrypt.
+ /// The packed and encrypted message.
+ public byte[] Encrypt( byte[] input )
+ {
+ return Encrypt( input, false );
+ }
+
+ ///
+ /// Decrypts a previously encrypted message.
+ ///
+ ///
+ /// If is false, this is equivalent to calling and
+ /// the must contain encoded versions of the trailer, message and padding buffers.
+ ///
+ /// If is true the lengths should be ommitted and the
+ /// should contain only:
+ /// - The trailer buffer
+ /// - The message buffer
+ /// - The padding buffer.
+ ///
+ /// The packed and encrypted data.
+ /// Indicates if the data contains only the encrypted data (true) or also contains an encoded version of the length of each buffer (false)
+ /// The original plaintext message.
+ public byte[] Decrypt( byte[] input, bool stream )
+ {
+ if( !stream )
+ {
+ return Decrypt( input );
+ }
+
+ byte[] inputCopy = new byte[input.Length];
+ Array.Copy( input, 0, inputCopy, 0, input.Length );
+
+ SecureBuffer inputBuffer;
+ SecureBuffer outputBuffer;
+ SecureBufferAdapter adapter;
+ SecurityStatus status;
+
+ inputBuffer = new SecureBuffer( inputCopy, BufferType.Stream );
+ outputBuffer = new SecureBuffer( null, BufferType.Data );
+
+ using( adapter = new SecureBufferAdapter( new[] { inputBuffer, outputBuffer } ) )
+ {
+ status = ContextNativeMethods.SafeDecryptMessage(
+ this.ContextHandle,
+ 0,
+ adapter,
+ 0
+ );
+
+ return adapter.ExtractData(1);
+ }
+ }
+
///
/// Decrypts a previously encrypted message.
///
diff --git a/NSspi/SecureBuffer/SecureBuffer.cs b/NSspi/SecureBuffer/SecureBuffer.cs
index 8ce6d1b..d06b034 100644
--- a/NSspi/SecureBuffer/SecureBuffer.cs
+++ b/NSspi/SecureBuffer/SecureBuffer.cs
@@ -52,7 +52,7 @@ public SecureBuffer( byte[] buffer, BufferType type )
{
this.Buffer = buffer;
this.Type = type;
- this.Length = this.Buffer.Length;
+ this.Length = this.Buffer?.Length ?? 0;
}
///
diff --git a/NSspi/SecureBuffer/SecureBufferAdapter.cs b/NSspi/SecureBuffer/SecureBufferAdapter.cs
index 7da36a2..61d2a53 100644
--- a/NSspi/SecureBuffer/SecureBufferAdapter.cs
+++ b/NSspi/SecureBuffer/SecureBufferAdapter.cs
@@ -123,7 +123,7 @@ public SecureBufferAdapter( IList buffers ) : base()
this.bufferCarrier[i] = new SecureBufferInternal();
this.bufferCarrier[i].Type = this.buffers[i].Type;
- this.bufferCarrier[i].Count = this.buffers[i].Buffer.Length;
+ this.bufferCarrier[i].Count = this.buffers[i].Buffer?.Length ?? 0;
this.bufferCarrier[i].Buffer = bufferHandles[i].AddrOfPinnedObject();
}
@@ -137,6 +137,23 @@ public SecureBufferAdapter( IList buffers ) : base()
this.descriptorHandle = GCHandle.Alloc( descriptor, GCHandleType.Pinned );
}
+ ///
+ /// Extracts data from a buffer allocated by the platform
+ ///
+ ///
+ /// If a buffer is allocated by the platform, the original .NET byte[] would be null and so can't
+ /// be read directly by the caller. This method extracts the data from the unmanaged buffer and copies it
+ /// to managed memory so it can be used easily.
+ ///
+ /// The index of the buffer to extract the data from
+ /// The data in the buffer
+ public byte[] ExtractData(int index)
+ {
+ byte[] buf = new byte[this.bufferCarrier[index].Count];
+ Marshal.Copy( this.bufferCarrier[index].Buffer, buf, 0, this.bufferCarrier[index].Count );
+ return buf;
+ }
+
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
~SecureBufferAdapter()
{
diff --git a/NsspiDemo/Program.cs b/NsspiDemo/Program.cs
index 0d40d68..d0767f2 100644
--- a/NsspiDemo/Program.cs
+++ b/NsspiDemo/Program.cs
@@ -137,6 +137,30 @@ private static void CredTest( string packageName )
}
}
+ cipherText = client.Encrypt(plainText, true);
+
+ roundTripPlaintext = server.Decrypt(cipherText, true);
+
+ if (roundTripPlaintext.Length != plainText.Length)
+ {
+ throw new Exception();
+ }
+
+ for (int i = 0; i < plainText.Length; i++)
+ {
+ if (plainText[i] != roundTripPlaintext[i])
+ {
+ throw new Exception();
+ }
+ }
+
+ rtMessage = Encoding.UTF8.GetString(roundTripPlaintext, 0, roundTripPlaintext.Length);
+
+ if (rtMessage.Equals(message) == false)
+ {
+ throw new Exception();
+ }
+
Console.Out.Flush();
}
finally