// Transport Security Layer (TLS)
// Copyright (c) 2003-2004 Carlos Guzman Alvarez
-// Copyright (C) 2006 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
{
#region Fields
+ private static ManualResetEvent record_processing = new ManualResetEvent (true);
+
protected Stream innerStream;
protected Context context;
public IAsyncResult BeginReceiveRecord(Stream record, AsyncCallback callback, object state)
{
- if (this.context.ConnectionEnd)
+ if (this.context.ReceivedConnectionEnd)
{
throw new TlsException(
AlertDescription.InternalError,
"The session is finished and it's no longer valid.");
}
+ record_processing.Reset ();
byte[] recordTypeBuffer = new byte[1];
ReceiveRecordAsyncResult internalResult = new ReceiveRecordAsyncResult(callback, state, recordTypeBuffer, record);
if (internalResult.CompletedWithError)
throw internalResult.AsyncException;
- else
- return internalResult.ResultingBuffer;
+
+ byte[] result = internalResult.ResultingBuffer;
+ record_processing.Set ();
+ return result;
}
public byte[] ReceiveRecord(Stream record)
{
+ if (this.context.ReceivedConnectionEnd)
+ {
+ throw new TlsException(
+ AlertDescription.InternalError,
+ "The session is finished and it's no longer valid.");
+ }
+
+ record_processing.Reset ();
+ byte[] recordTypeBuffer = new byte[1];
+
+ int bytesRead = record.Read(recordTypeBuffer, 0, recordTypeBuffer.Length);
+
+ //We're at the end of the stream. Time to bail.
+ if (bytesRead == 0)
+ {
+ return null;
+ }
+
+ // Try to read the Record Content Type
+ int type = recordTypeBuffer[0];
+
+ // Set last handshake message received to None
+ this.context.LastHandshakeMsg = HandshakeType.ClientHello;
+
+ ContentType contentType = (ContentType)type;
+ byte[] buffer = this.ReadRecordBuffer(type, record);
+ if (buffer == null)
+ {
+ // record incomplete (at the moment)
+ return null;
+ }
+
+ // Decrypt message contents if needed
+ if (contentType == ContentType.Alert && buffer.Length == 2)
+ {
+ }
+ else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
+ {
+ buffer = this.decryptRecordFragment (contentType, buffer);
+ DebugHelper.WriteLine ("Decrypted record data", buffer);
+ }
+
+ // Process record
+ switch (contentType)
+ {
+ case ContentType.Alert:
+ this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
+ if (record.CanSeek)
+ {
+ // don't reprocess that memory block
+ record.SetLength (0);
+ }
+ buffer = null;
+ break;
+
+ case ContentType.ChangeCipherSpec:
+ this.ProcessChangeCipherSpec();
+ break;
+
+ case ContentType.ApplicationData:
+ break;
+
+ case ContentType.Handshake:
+ TlsStream message = new TlsStream (buffer);
+ while (!message.EOF)
+ {
+ this.ProcessHandshakeMessage(message);
+ }
+ break;
- IAsyncResult ar = this.BeginReceiveRecord(record, null, null);
- return this.EndReceiveRecord(ar);
+ case (ContentType)0x80:
+ this.context.HandshakeMessages.Write (buffer);
+ break;
+ default:
+ throw new TlsException(
+ AlertDescription.UnexpectedMessage,
+ "Unknown record received from server.");
+ }
+
+ record_processing.Set ();
+ return buffer;
}
private byte[] ReadRecordBuffer (int contentType, Stream record)
switch (alertDesc)
{
case AlertDescription.CloseNotify:
- this.context.ConnectionEnd = true;
+ this.context.ReceivedConnectionEnd = true;
break;
}
break;
// Write record
this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
- if (close)
- {
- this.context.ConnectionEnd = true;
+ if (close) {
+ this.context.SentConnectionEnd = true;
}
}
}
}
+ public void SendChangeCipherSpec(Stream recordStream)
+ {
+ DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
+
+ byte[] record = this.EncodeRecord (ContentType.ChangeCipherSpec, new byte[] { 1 });
+
+ // Send Change Cipher Spec message with the current cipher
+ // or as plain text if this is the initial negotiation
+ recordStream.Write(record, 0, record.Length);
+
+ Context ctx = this.context;
+
+ // Reset sequence numbers
+ ctx.WriteSequenceNumber = 0;
+
+ // all further data sent will be encrypted with the negotiated
+ // security parameters (now the current parameters)
+ if (ctx is ClientContext) {
+ ctx.StartSwitchingSecurityParameters (true);
+ } else {
+ ctx.EndSwitchingSecurityParameters (false);
+ }
+ }
+
+ public IAsyncResult BeginSendChangeCipherSpec(AsyncCallback callback, object state)
+ {
+ DebugHelper.WriteLine (">>>> Write Change Cipher Spec");
+
+ // Send Change Cipher Spec message with the current cipher
+ // or as plain text if this is the initial negotiation
+ return this.BeginSendRecord (ContentType.ChangeCipherSpec, new byte[] { 1 }, callback, state);
+ }
+
+ public void EndSendChangeCipherSpec (IAsyncResult asyncResult)
+ {
+ this.EndSendRecord (asyncResult);
+
+ Context ctx = this.context;
+
+ // Reset sequence numbers
+ ctx.WriteSequenceNumber = 0;
+
+ // all further data sent will be encrypted with the negotiated
+ // security parameters (now the current parameters)
+ if (ctx is ClientContext) {
+ ctx.StartSwitchingSecurityParameters (true);
+ } else {
+ ctx.EndSwitchingSecurityParameters (false);
+ }
+ }
+
public IAsyncResult BeginSendRecord(HandshakeType handshakeType, AsyncCallback callback, object state)
{
HandshakeMessage msg = this.GetMessage(handshakeType);
public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
{
- if (this.context.ConnectionEnd)
+ if (this.context.SentConnectionEnd)
{
throw new TlsException(
AlertDescription.InternalError,
int offset,
int count)
{
- if (this.context.ConnectionEnd)
+ if (this.context.SentConnectionEnd)
{
throw new TlsException(
AlertDescription.InternalError,
return record.ToArray();
}
-
+
+ public byte[] EncodeHandshakeRecord(HandshakeType handshakeType)
+ {
+ HandshakeMessage msg = this.GetMessage(handshakeType);
+
+ msg.Process();
+
+ var bytes = this.EncodeRecord (msg.ContentType, msg.EncodeMessage ());
+
+ msg.Update();
+
+ msg.Reset();
+
+ return bytes;
+ }
+
#endregion
#region Cryptography Methods
{
this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
}
-
throw;
}