| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984 |
- // Transport Security Layer (TLS)
- // Copyright (c) 2003-2004 Carlos Guzman Alvarez
- // 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
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- //
- using System;
- using System.Collections;
- using System.IO;
- using System.Threading;
- using Mono.Security.Protocol.Tls.Handshake;
- namespace Mono.Security.Protocol.Tls
- {
- internal abstract class RecordProtocol
- {
- #region Fields
- private static ManualResetEvent record_processing = new ManualResetEvent (true);
- protected Stream innerStream;
- protected Context context;
- #endregion
- #region Properties
- public Context Context
- {
- get { return this.context; }
- set { this.context = value; }
- }
- #endregion
- #region Constructors
- public RecordProtocol(Stream innerStream, Context context)
- {
- this.innerStream = innerStream;
- this.context = context;
- this.context.RecordProtocol = this;
- }
- #endregion
- #region Abstract Methods
- public virtual void SendRecord(HandshakeType type)
- {
- IAsyncResult ar = this.BeginSendRecord(type, null, null);
- this.EndSendRecord(ar);
- }
- protected abstract void ProcessHandshakeMessage(TlsStream handMsg);
- protected virtual void ProcessChangeCipherSpec ()
- {
- Context ctx = this.Context;
- // Reset sequence numbers
- ctx.ReadSequenceNumber = 0;
- if (ctx is ClientContext) {
- ctx.EndSwitchingSecurityParameters (true);
- } else {
- ctx.StartSwitchingSecurityParameters (false);
- }
- }
- public virtual HandshakeMessage GetMessage(HandshakeType type)
- {
- throw new NotSupportedException();
- }
- #endregion
- #region Receive Record Async Result
- private class ReceiveRecordAsyncResult : IAsyncResult
- {
- private object locker = new object ();
- private AsyncCallback _userCallback;
- private object _userState;
- private Exception _asyncException;
- private ManualResetEvent handle;
- private byte[] _resultingBuffer;
- private Stream _record;
- private bool completed;
- private byte[] _initialBuffer;
- public ReceiveRecordAsyncResult(AsyncCallback userCallback, object userState, byte[] initialBuffer, Stream record)
- {
- _userCallback = userCallback;
- _userState = userState;
- _initialBuffer = initialBuffer;
- _record = record;
- }
- public Stream Record
- {
- get { return _record; }
- }
- public byte[] ResultingBuffer
- {
- get { return _resultingBuffer; }
- }
- public byte[] InitialBuffer
- {
- get { return _initialBuffer; }
- }
- public object AsyncState
- {
- get { return _userState; }
- }
- public Exception AsyncException
- {
- get { return _asyncException; }
- }
- public bool CompletedWithError
- {
- get {
- if (!IsCompleted)
- return false; // Perhaps throw InvalidOperationExcetion?
- return null != _asyncException;
- }
- }
- public WaitHandle AsyncWaitHandle
- {
- get {
- lock (locker) {
- if (handle == null)
- handle = new ManualResetEvent (completed);
- }
- return handle;
- }
-
- }
- public bool CompletedSynchronously
- {
- get { return false; }
- }
- public bool IsCompleted
- {
- get {
- lock (locker) {
- return completed;
- }
- }
- }
- private void SetComplete(Exception ex, byte[] resultingBuffer)
- {
- lock (locker) {
- if (completed)
- return;
- completed = true;
- _asyncException = ex;
- _resultingBuffer = resultingBuffer;
- if (handle != null)
- handle.Set ();
- if (_userCallback != null)
- _userCallback.BeginInvoke (this, null, null);
- }
- }
- public void SetComplete(Exception ex)
- {
- SetComplete(ex, null);
- }
- public void SetComplete(byte[] resultingBuffer)
- {
- SetComplete(null, resultingBuffer);
- }
- public void SetComplete()
- {
- SetComplete(null, null);
- }
- }
- #endregion
- #region Receive Record Async Result
- private class SendRecordAsyncResult : IAsyncResult
- {
- private object locker = new object ();
- private AsyncCallback _userCallback;
- private object _userState;
- private Exception _asyncException;
- private ManualResetEvent handle;
- private HandshakeMessage _message;
- private bool completed;
- public SendRecordAsyncResult(AsyncCallback userCallback, object userState, HandshakeMessage message)
- {
- _userCallback = userCallback;
- _userState = userState;
- _message = message;
- }
- public HandshakeMessage Message
- {
- get { return _message; }
- }
- public object AsyncState
- {
- get { return _userState; }
- }
- public Exception AsyncException
- {
- get { return _asyncException; }
- }
- public bool CompletedWithError
- {
- get {
- if (!IsCompleted)
- return false; // Perhaps throw InvalidOperationExcetion?
- return null != _asyncException;
- }
- }
- public WaitHandle AsyncWaitHandle
- {
- get {
- lock (locker) {
- if (handle == null)
- handle = new ManualResetEvent (completed);
- }
- return handle;
- }
-
- }
- public bool CompletedSynchronously
- {
- get { return false; }
- }
- public bool IsCompleted
- {
- get {
- lock (locker) {
- return completed;
- }
- }
- }
- public void SetComplete(Exception ex)
- {
- lock (locker) {
- if (completed)
- return;
- completed = true;
- if (handle != null)
- handle.Set ();
- if (_userCallback != null)
- _userCallback.BeginInvoke (this, null, null);
- _asyncException = ex;
- }
- }
- public void SetComplete()
- {
- SetComplete(null);
- }
- }
- #endregion
- #region Reveive Record Methods
- public IAsyncResult BeginReceiveRecord(Stream record, AsyncCallback callback, object state)
- {
- if (this.context.ConnectionEnd)
- {
- 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);
- record.BeginRead(internalResult.InitialBuffer, 0, internalResult.InitialBuffer.Length, new AsyncCallback(InternalReceiveRecordCallback), internalResult);
- return internalResult;
- }
- private void InternalReceiveRecordCallback(IAsyncResult asyncResult)
- {
- ReceiveRecordAsyncResult internalResult = asyncResult.AsyncState as ReceiveRecordAsyncResult;
- Stream record = internalResult.Record;
- try
- {
-
- int bytesRead = internalResult.Record.EndRead(asyncResult);
- //We're at the end of the stream. Time to bail.
- if (bytesRead == 0)
- {
- internalResult.SetComplete((byte[])null);
- return;
- }
- // Try to read the Record Content Type
- int type = internalResult.InitialBuffer[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)
- internalResult.SetComplete((byte[])null);
- return;
- }
- // 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;
- case (ContentType)0x80:
- this.context.HandshakeMessages.Write (buffer);
- break;
- default:
- throw new TlsException(
- AlertDescription.UnexpectedMessage,
- "Unknown record received from server.");
- }
- internalResult.SetComplete(buffer);
- }
- catch (Exception ex)
- {
- internalResult.SetComplete(ex);
- }
- }
- public byte[] EndReceiveRecord(IAsyncResult asyncResult)
- {
- ReceiveRecordAsyncResult internalResult = asyncResult as ReceiveRecordAsyncResult;
- if (null == internalResult)
- throw new ArgumentException("Either the provided async result is null or was not created by this RecordProtocol.");
- if (!internalResult.IsCompleted)
- internalResult.AsyncWaitHandle.WaitOne();
- if (internalResult.CompletedWithError)
- throw internalResult.AsyncException;
- byte[] result = internalResult.ResultingBuffer;
- record_processing.Set ();
- return result;
- }
- public byte[] ReceiveRecord(Stream record)
- {
- IAsyncResult ar = this.BeginReceiveRecord(record, null, null);
- return this.EndReceiveRecord(ar);
- }
- private byte[] ReadRecordBuffer (int contentType, Stream record)
- {
- switch (contentType)
- {
- case 0x80:
- return this.ReadClientHelloV2(record);
- default:
- if (!Enum.IsDefined(typeof(ContentType), (ContentType)contentType))
- {
- throw new TlsException(AlertDescription.DecodeError);
- }
- return this.ReadStandardRecordBuffer(record);
- }
- }
- private byte[] ReadClientHelloV2 (Stream record)
- {
- int msgLength = record.ReadByte ();
- // process further only if the whole record is available
- if (record.CanSeek && (msgLength + 1 > record.Length))
- {
- return null;
- }
- byte[] message = new byte[msgLength];
- record.Read (message, 0, msgLength);
- int msgType = message [0];
- if (msgType != 1)
- {
- throw new TlsException(AlertDescription.DecodeError);
- }
- int protocol = (message [1] << 8 | message [2]);
- int cipherSpecLength = (message [3] << 8 | message [4]);
- int sessionIdLength = (message [5] << 8 | message [6]);
- int challengeLength = (message [7] << 8 | message [8]);
- int length = (challengeLength > 32) ? 32 : challengeLength;
- // Read CipherSpecs
- byte[] cipherSpecV2 = new byte[cipherSpecLength];
- Buffer.BlockCopy (message, 9, cipherSpecV2, 0, cipherSpecLength);
- // Read session ID
- byte[] sessionId = new byte[sessionIdLength];
- Buffer.BlockCopy (message, 9 + cipherSpecLength, sessionId, 0, sessionIdLength);
- // Read challenge ID
- byte[] challenge = new byte[challengeLength];
- Buffer.BlockCopy (message, 9 + cipherSpecLength + sessionIdLength, challenge, 0, challengeLength);
-
- if (challengeLength < 16 || cipherSpecLength == 0 || (cipherSpecLength % 3) != 0)
- {
- throw new TlsException(AlertDescription.DecodeError);
- }
- // Updated the Session ID
- if (sessionId.Length > 0)
- {
- this.context.SessionId = sessionId;
- }
- // Update the protocol version
- this.Context.ChangeProtocol((short)protocol);
- // Select the Cipher suite
- this.ProcessCipherSpecV2Buffer(this.Context.SecurityProtocol, cipherSpecV2);
- // Updated the Client Random
- this.context.ClientRandom = new byte [32]; // Always 32
- // 1. if challenge is bigger than 32 bytes only use the last 32 bytes
- // 2. right justify (0) challenge in ClientRandom if less than 32
- Buffer.BlockCopy (challenge, challenge.Length - length, this.context.ClientRandom, 32 - length, length);
- // Set
- this.context.LastHandshakeMsg = HandshakeType.ClientHello;
- this.context.ProtocolNegotiated = true;
- return message;
- }
- private byte[] ReadStandardRecordBuffer (Stream record)
- {
- byte[] header = new byte[4];
- if (record.Read (header, 0, 4) != 4)
- throw new TlsException ("buffer underrun");
-
- short protocol = (short)((header [0] << 8) | header [1]);
- short length = (short)((header [2] << 8) | header [3]);
- // process further only if the whole record is available
- // note: the first 5 bytes aren't part of the length
- if (record.CanSeek && (length + 5 > record.Length))
- {
- return null;
- }
-
- // Read Record data
- int totalReceived = 0;
- byte[] buffer = new byte[length];
- while (totalReceived != length)
- {
- int justReceived = record.Read(buffer, totalReceived, buffer.Length - totalReceived);
- //Make sure we get some data so we don't end up in an infinite loop here before shutdown.
- if (0 == justReceived)
- {
- throw new TlsException(AlertDescription.CloseNotify, "Received 0 bytes from stream. It must be closed.");
- }
- totalReceived += justReceived;
- }
- // Check that the message has a valid protocol version
- if (protocol != this.context.Protocol && this.context.ProtocolNegotiated)
- {
- throw new TlsException(
- AlertDescription.ProtocolVersion, "Invalid protocol version on message received");
- }
- DebugHelper.WriteLine("Record data", buffer);
- return buffer;
- }
- private void ProcessAlert(AlertLevel alertLevel, AlertDescription alertDesc)
- {
- switch (alertLevel)
- {
- case AlertLevel.Fatal:
- throw new TlsException(alertLevel, alertDesc);
- case AlertLevel.Warning:
- default:
- switch (alertDesc)
- {
- case AlertDescription.CloseNotify:
- this.context.ConnectionEnd = true;
- break;
- }
- break;
- }
- }
- #endregion
- #region Send Alert Methods
- public void SendAlert(AlertDescription description)
- {
- this.SendAlert(new Alert(description));
- }
- public void SendAlert(
- AlertLevel level,
- AlertDescription description)
- {
- this.SendAlert(new Alert(level, description));
- }
- public void SendAlert(Alert alert)
- {
- AlertLevel level;
- AlertDescription description;
- bool close;
- if (alert == null) {
- DebugHelper.WriteLine(">>>> Write Alert NULL");
- level = AlertLevel.Fatal;
- description = AlertDescription.InternalError;
- close = true;
- } else {
- DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
- level = alert.Level;
- description = alert.Description;
- close = alert.IsCloseNotify;
- }
- // Write record
- this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
- if (close)
- {
- this.context.ConnectionEnd = true;
- }
- }
- #endregion
- #region Send Record Methods
- public void SendChangeCipherSpec()
- {
- 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
- this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
- 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);
- msg.Process();
- DebugHelper.WriteLine(">>>> Write handshake record ({0}|{1})", context.Protocol, msg.ContentType);
- SendRecordAsyncResult internalResult = new SendRecordAsyncResult(callback, state, msg);
- this.BeginSendRecord(msg.ContentType, msg.EncodeMessage(), new AsyncCallback(InternalSendRecordCallback), internalResult);
- return internalResult;
- }
- private void InternalSendRecordCallback(IAsyncResult ar)
- {
- SendRecordAsyncResult internalResult = ar.AsyncState as SendRecordAsyncResult;
-
- try
- {
- this.EndSendRecord(ar);
- // Update session
- internalResult.Message.Update();
- // Reset message contents
- internalResult.Message.Reset();
- internalResult.SetComplete();
- }
- catch (Exception ex)
- {
- internalResult.SetComplete(ex);
- }
- }
- public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
- {
- if (this.context.ConnectionEnd)
- {
- throw new TlsException(
- AlertDescription.InternalError,
- "The session is finished and it's no longer valid.");
- }
- byte[] record = this.EncodeRecord(contentType, recordData);
- return this.innerStream.BeginWrite(record, 0, record.Length, callback, state);
- }
- public void EndSendRecord(IAsyncResult asyncResult)
- {
- if (asyncResult is SendRecordAsyncResult)
- {
- SendRecordAsyncResult internalResult = asyncResult as SendRecordAsyncResult;
- if (!internalResult.IsCompleted)
- internalResult.AsyncWaitHandle.WaitOne();
- if (internalResult.CompletedWithError)
- throw internalResult.AsyncException;
- }
- else
- {
- this.innerStream.EndWrite(asyncResult);
- }
- }
- public void SendRecord(ContentType contentType, byte[] recordData)
- {
- IAsyncResult ar = this.BeginSendRecord(contentType, recordData, null, null);
- this.EndSendRecord(ar);
- }
- public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
- {
- return this.EncodeRecord(
- contentType,
- recordData,
- 0,
- recordData.Length);
- }
- public byte[] EncodeRecord(
- ContentType contentType,
- byte[] recordData,
- int offset,
- int count)
- {
- if (this.context.ConnectionEnd)
- {
- throw new TlsException(
- AlertDescription.InternalError,
- "The session is finished and it's no longer valid.");
- }
- TlsStream record = new TlsStream();
- int position = offset;
- while (position < ( offset + count ))
- {
- short fragmentLength = 0;
- byte[] fragment;
- if ((count + offset - position) > Context.MAX_FRAGMENT_SIZE)
- {
- fragmentLength = Context.MAX_FRAGMENT_SIZE;
- }
- else
- {
- fragmentLength = (short)(count + offset - position);
- }
- // Fill the fragment data
- fragment = new byte[fragmentLength];
- Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
- if ((this.Context.Write != null) && (this.Context.Write.Cipher != null))
- {
- // Encrypt fragment
- fragment = this.encryptRecordFragment (contentType, fragment);
- }
- // Write tls message
- record.Write((byte)contentType);
- record.Write(this.context.Protocol);
- record.Write((short)fragment.Length);
- record.Write(fragment);
- DebugHelper.WriteLine("Record data", fragment);
- // Update buffer position
- position += fragmentLength;
- }
- return record.ToArray();
- }
-
- #endregion
- #region Cryptography Methods
- private byte[] encryptRecordFragment(
- ContentType contentType,
- byte[] fragment)
- {
- byte[] mac = null;
- // Calculate message MAC
- if (this.Context is ClientContext)
- {
- mac = this.context.Write.Cipher.ComputeClientRecordMAC(contentType, fragment);
- }
- else
- {
- mac = this.context.Write.Cipher.ComputeServerRecordMAC (contentType, fragment);
- }
- DebugHelper.WriteLine(">>>> Record MAC", mac);
- // Encrypt the message
- byte[] ecr = this.context.Write.Cipher.EncryptRecord (fragment, mac);
- // Update sequence number
- this.context.WriteSequenceNumber++;
- return ecr;
- }
- private byte[] decryptRecordFragment(
- ContentType contentType,
- byte[] fragment)
- {
- byte[] dcrFragment = null;
- byte[] dcrMAC = null;
- try
- {
- this.context.Read.Cipher.DecryptRecord (fragment, out dcrFragment, out dcrMAC);
- }
- catch
- {
- if (this.context is ServerContext)
- {
- this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
- }
- throw;
- }
-
- // Generate record MAC
- byte[] mac = null;
- if (this.Context is ClientContext)
- {
- mac = this.context.Read.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
- }
- else
- {
- mac = this.context.Read.Cipher.ComputeClientRecordMAC (contentType, dcrFragment);
- }
- DebugHelper.WriteLine(">>>> Record MAC", mac);
- // Check record MAC
- if (!Compare (mac, dcrMAC))
- {
- throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
- }
- // Update sequence number
- this.context.ReadSequenceNumber++;
- return dcrFragment;
- }
- private bool Compare (byte[] array1, byte[] array2)
- {
- if (array1 == null)
- return (array2 == null);
- if (array2 == null)
- return false;
- if (array1.Length != array2.Length)
- return false;
- for (int i = 0; i < array1.Length; i++) {
- if (array1[i] != array2[i])
- return false;
- }
- return true;
- }
- #endregion
- #region CipherSpecV2 processing
- private void ProcessCipherSpecV2Buffer (SecurityProtocolType protocol, byte[] buffer)
- {
- TlsStream codes = new TlsStream(buffer);
- string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
- while (codes.Position < codes.Length)
- {
- byte check = codes.ReadByte();
- if (check == 0)
- {
- // SSL/TLS cipher spec
- short code = codes.ReadInt16();
- int index = this.Context.SupportedCiphers.IndexOf(code);
- if (index != -1)
- {
- this.Context.Negotiating.Cipher = this.Context.SupportedCiphers[index];
- break;
- }
- }
- else
- {
- byte[] tmp = new byte[2];
- codes.Read(tmp, 0, tmp.Length);
- int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
- CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
- if (cipher != null)
- {
- this.Context.Negotiating.Cipher = cipher;
- break;
- }
- }
- }
- if (this.Context.Negotiating == null)
- {
- throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
- }
- }
- private CipherSuite MapV2CipherCode(string prefix, int code)
- {
- try
- {
- switch (code)
- {
- case 65664:
- // TLS_RC4_128_WITH_MD5
- return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
-
- case 131200:
- // TLS_RC4_128_EXPORT40_WITH_MD5
- return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
-
- case 196736:
- // TLS_RC2_CBC_128_CBC_WITH_MD5
- return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
-
- case 262272:
- // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
- return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
-
- case 327808:
- // TLS_IDEA_128_CBC_WITH_MD5
- return null;
-
- case 393280:
- // TLS_DES_64_CBC_WITH_MD5
- return null;
- case 458944:
- // TLS_DES_192_EDE3_CBC_WITH_MD5
- return null;
- default:
- return null;
- }
- }
- catch
- {
- return null;
- }
- }
- #endregion
- }
- }
|