RecordProtocol.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  1. // Transport Security Layer (TLS)
  2. // Copyright (c) 2003-2004 Carlos Guzman Alvarez
  3. // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining
  6. // a copy of this software and associated documentation files (the
  7. // "Software"), to deal in the Software without restriction, including
  8. // without limitation the rights to use, copy, modify, merge, publish,
  9. // distribute, sublicense, and/or sell copies of the Software, and to
  10. // permit persons to whom the Software is furnished to do so, subject to
  11. // the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be
  14. // included in all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. //
  24. using System;
  25. using System.Collections;
  26. using System.IO;
  27. using System.Threading;
  28. using Mono.Security.Protocol.Tls.Handshake;
  29. namespace Mono.Security.Protocol.Tls
  30. {
  31. internal abstract class RecordProtocol
  32. {
  33. #region Fields
  34. private static ManualResetEvent record_processing = new ManualResetEvent (true);
  35. protected Stream innerStream;
  36. protected Context context;
  37. #endregion
  38. #region Properties
  39. public Context Context
  40. {
  41. get { return this.context; }
  42. set { this.context = value; }
  43. }
  44. #endregion
  45. #region Constructors
  46. public RecordProtocol(Stream innerStream, Context context)
  47. {
  48. this.innerStream = innerStream;
  49. this.context = context;
  50. this.context.RecordProtocol = this;
  51. }
  52. #endregion
  53. #region Abstract Methods
  54. public virtual void SendRecord(HandshakeType type)
  55. {
  56. IAsyncResult ar = this.BeginSendRecord(type, null, null);
  57. this.EndSendRecord(ar);
  58. }
  59. protected abstract void ProcessHandshakeMessage(TlsStream handMsg);
  60. protected virtual void ProcessChangeCipherSpec ()
  61. {
  62. Context ctx = this.Context;
  63. // Reset sequence numbers
  64. ctx.ReadSequenceNumber = 0;
  65. if (ctx is ClientContext) {
  66. ctx.EndSwitchingSecurityParameters (true);
  67. } else {
  68. ctx.StartSwitchingSecurityParameters (false);
  69. }
  70. }
  71. public virtual HandshakeMessage GetMessage(HandshakeType type)
  72. {
  73. throw new NotSupportedException();
  74. }
  75. #endregion
  76. #region Receive Record Async Result
  77. private class ReceiveRecordAsyncResult : IAsyncResult
  78. {
  79. private object locker = new object ();
  80. private AsyncCallback _userCallback;
  81. private object _userState;
  82. private Exception _asyncException;
  83. private ManualResetEvent handle;
  84. private byte[] _resultingBuffer;
  85. private Stream _record;
  86. private bool completed;
  87. private byte[] _initialBuffer;
  88. public ReceiveRecordAsyncResult(AsyncCallback userCallback, object userState, byte[] initialBuffer, Stream record)
  89. {
  90. _userCallback = userCallback;
  91. _userState = userState;
  92. _initialBuffer = initialBuffer;
  93. _record = record;
  94. }
  95. public Stream Record
  96. {
  97. get { return _record; }
  98. }
  99. public byte[] ResultingBuffer
  100. {
  101. get { return _resultingBuffer; }
  102. }
  103. public byte[] InitialBuffer
  104. {
  105. get { return _initialBuffer; }
  106. }
  107. public object AsyncState
  108. {
  109. get { return _userState; }
  110. }
  111. public Exception AsyncException
  112. {
  113. get { return _asyncException; }
  114. }
  115. public bool CompletedWithError
  116. {
  117. get {
  118. if (!IsCompleted)
  119. return false; // Perhaps throw InvalidOperationExcetion?
  120. return null != _asyncException;
  121. }
  122. }
  123. public WaitHandle AsyncWaitHandle
  124. {
  125. get {
  126. lock (locker) {
  127. if (handle == null)
  128. handle = new ManualResetEvent (completed);
  129. }
  130. return handle;
  131. }
  132. }
  133. public bool CompletedSynchronously
  134. {
  135. get { return false; }
  136. }
  137. public bool IsCompleted
  138. {
  139. get {
  140. lock (locker) {
  141. return completed;
  142. }
  143. }
  144. }
  145. private void SetComplete(Exception ex, byte[] resultingBuffer)
  146. {
  147. lock (locker) {
  148. if (completed)
  149. return;
  150. completed = true;
  151. _asyncException = ex;
  152. _resultingBuffer = resultingBuffer;
  153. if (handle != null)
  154. handle.Set ();
  155. if (_userCallback != null)
  156. _userCallback.BeginInvoke (this, null, null);
  157. }
  158. }
  159. public void SetComplete(Exception ex)
  160. {
  161. SetComplete(ex, null);
  162. }
  163. public void SetComplete(byte[] resultingBuffer)
  164. {
  165. SetComplete(null, resultingBuffer);
  166. }
  167. public void SetComplete()
  168. {
  169. SetComplete(null, null);
  170. }
  171. }
  172. #endregion
  173. #region Receive Record Async Result
  174. private class SendRecordAsyncResult : IAsyncResult
  175. {
  176. private object locker = new object ();
  177. private AsyncCallback _userCallback;
  178. private object _userState;
  179. private Exception _asyncException;
  180. private ManualResetEvent handle;
  181. private HandshakeMessage _message;
  182. private bool completed;
  183. public SendRecordAsyncResult(AsyncCallback userCallback, object userState, HandshakeMessage message)
  184. {
  185. _userCallback = userCallback;
  186. _userState = userState;
  187. _message = message;
  188. }
  189. public HandshakeMessage Message
  190. {
  191. get { return _message; }
  192. }
  193. public object AsyncState
  194. {
  195. get { return _userState; }
  196. }
  197. public Exception AsyncException
  198. {
  199. get { return _asyncException; }
  200. }
  201. public bool CompletedWithError
  202. {
  203. get {
  204. if (!IsCompleted)
  205. return false; // Perhaps throw InvalidOperationExcetion?
  206. return null != _asyncException;
  207. }
  208. }
  209. public WaitHandle AsyncWaitHandle
  210. {
  211. get {
  212. lock (locker) {
  213. if (handle == null)
  214. handle = new ManualResetEvent (completed);
  215. }
  216. return handle;
  217. }
  218. }
  219. public bool CompletedSynchronously
  220. {
  221. get { return false; }
  222. }
  223. public bool IsCompleted
  224. {
  225. get {
  226. lock (locker) {
  227. return completed;
  228. }
  229. }
  230. }
  231. public void SetComplete(Exception ex)
  232. {
  233. lock (locker) {
  234. if (completed)
  235. return;
  236. completed = true;
  237. if (handle != null)
  238. handle.Set ();
  239. if (_userCallback != null)
  240. _userCallback.BeginInvoke (this, null, null);
  241. _asyncException = ex;
  242. }
  243. }
  244. public void SetComplete()
  245. {
  246. SetComplete(null);
  247. }
  248. }
  249. #endregion
  250. #region Reveive Record Methods
  251. public IAsyncResult BeginReceiveRecord(Stream record, AsyncCallback callback, object state)
  252. {
  253. if (this.context.ConnectionEnd)
  254. {
  255. throw new TlsException(
  256. AlertDescription.InternalError,
  257. "The session is finished and it's no longer valid.");
  258. }
  259. record_processing.Reset ();
  260. byte[] recordTypeBuffer = new byte[1];
  261. ReceiveRecordAsyncResult internalResult = new ReceiveRecordAsyncResult(callback, state, recordTypeBuffer, record);
  262. record.BeginRead(internalResult.InitialBuffer, 0, internalResult.InitialBuffer.Length, new AsyncCallback(InternalReceiveRecordCallback), internalResult);
  263. return internalResult;
  264. }
  265. private void InternalReceiveRecordCallback(IAsyncResult asyncResult)
  266. {
  267. ReceiveRecordAsyncResult internalResult = asyncResult.AsyncState as ReceiveRecordAsyncResult;
  268. Stream record = internalResult.Record;
  269. try
  270. {
  271. int bytesRead = internalResult.Record.EndRead(asyncResult);
  272. //We're at the end of the stream. Time to bail.
  273. if (bytesRead == 0)
  274. {
  275. internalResult.SetComplete((byte[])null);
  276. return;
  277. }
  278. // Try to read the Record Content Type
  279. int type = internalResult.InitialBuffer[0];
  280. // Set last handshake message received to None
  281. this.context.LastHandshakeMsg = HandshakeType.ClientHello;
  282. ContentType contentType = (ContentType)type;
  283. byte[] buffer = this.ReadRecordBuffer(type, record);
  284. if (buffer == null)
  285. {
  286. // record incomplete (at the moment)
  287. internalResult.SetComplete((byte[])null);
  288. return;
  289. }
  290. // Decrypt message contents if needed
  291. if (contentType == ContentType.Alert && buffer.Length == 2)
  292. {
  293. }
  294. else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
  295. {
  296. buffer = this.decryptRecordFragment (contentType, buffer);
  297. DebugHelper.WriteLine ("Decrypted record data", buffer);
  298. }
  299. // Process record
  300. switch (contentType)
  301. {
  302. case ContentType.Alert:
  303. this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
  304. if (record.CanSeek)
  305. {
  306. // don't reprocess that memory block
  307. record.SetLength (0);
  308. }
  309. buffer = null;
  310. break;
  311. case ContentType.ChangeCipherSpec:
  312. this.ProcessChangeCipherSpec();
  313. break;
  314. case ContentType.ApplicationData:
  315. break;
  316. case ContentType.Handshake:
  317. TlsStream message = new TlsStream (buffer);
  318. while (!message.EOF)
  319. {
  320. this.ProcessHandshakeMessage(message);
  321. }
  322. break;
  323. case (ContentType)0x80:
  324. this.context.HandshakeMessages.Write (buffer);
  325. break;
  326. default:
  327. throw new TlsException(
  328. AlertDescription.UnexpectedMessage,
  329. "Unknown record received from server.");
  330. }
  331. internalResult.SetComplete(buffer);
  332. }
  333. catch (Exception ex)
  334. {
  335. internalResult.SetComplete(ex);
  336. }
  337. }
  338. public byte[] EndReceiveRecord(IAsyncResult asyncResult)
  339. {
  340. ReceiveRecordAsyncResult internalResult = asyncResult as ReceiveRecordAsyncResult;
  341. if (null == internalResult)
  342. throw new ArgumentException("Either the provided async result is null or was not created by this RecordProtocol.");
  343. if (!internalResult.IsCompleted)
  344. internalResult.AsyncWaitHandle.WaitOne();
  345. if (internalResult.CompletedWithError)
  346. throw internalResult.AsyncException;
  347. byte[] result = internalResult.ResultingBuffer;
  348. record_processing.Set ();
  349. return result;
  350. }
  351. public byte[] ReceiveRecord(Stream record)
  352. {
  353. IAsyncResult ar = this.BeginReceiveRecord(record, null, null);
  354. return this.EndReceiveRecord(ar);
  355. }
  356. private byte[] ReadRecordBuffer (int contentType, Stream record)
  357. {
  358. switch (contentType)
  359. {
  360. case 0x80:
  361. return this.ReadClientHelloV2(record);
  362. default:
  363. if (!Enum.IsDefined(typeof(ContentType), (ContentType)contentType))
  364. {
  365. throw new TlsException(AlertDescription.DecodeError);
  366. }
  367. return this.ReadStandardRecordBuffer(record);
  368. }
  369. }
  370. private byte[] ReadClientHelloV2 (Stream record)
  371. {
  372. int msgLength = record.ReadByte ();
  373. // process further only if the whole record is available
  374. if (record.CanSeek && (msgLength + 1 > record.Length))
  375. {
  376. return null;
  377. }
  378. byte[] message = new byte[msgLength];
  379. record.Read (message, 0, msgLength);
  380. int msgType = message [0];
  381. if (msgType != 1)
  382. {
  383. throw new TlsException(AlertDescription.DecodeError);
  384. }
  385. int protocol = (message [1] << 8 | message [2]);
  386. int cipherSpecLength = (message [3] << 8 | message [4]);
  387. int sessionIdLength = (message [5] << 8 | message [6]);
  388. int challengeLength = (message [7] << 8 | message [8]);
  389. int length = (challengeLength > 32) ? 32 : challengeLength;
  390. // Read CipherSpecs
  391. byte[] cipherSpecV2 = new byte[cipherSpecLength];
  392. Buffer.BlockCopy (message, 9, cipherSpecV2, 0, cipherSpecLength);
  393. // Read session ID
  394. byte[] sessionId = new byte[sessionIdLength];
  395. Buffer.BlockCopy (message, 9 + cipherSpecLength, sessionId, 0, sessionIdLength);
  396. // Read challenge ID
  397. byte[] challenge = new byte[challengeLength];
  398. Buffer.BlockCopy (message, 9 + cipherSpecLength + sessionIdLength, challenge, 0, challengeLength);
  399. if (challengeLength < 16 || cipherSpecLength == 0 || (cipherSpecLength % 3) != 0)
  400. {
  401. throw new TlsException(AlertDescription.DecodeError);
  402. }
  403. // Updated the Session ID
  404. if (sessionId.Length > 0)
  405. {
  406. this.context.SessionId = sessionId;
  407. }
  408. // Update the protocol version
  409. this.Context.ChangeProtocol((short)protocol);
  410. // Select the Cipher suite
  411. this.ProcessCipherSpecV2Buffer(this.Context.SecurityProtocol, cipherSpecV2);
  412. // Updated the Client Random
  413. this.context.ClientRandom = new byte [32]; // Always 32
  414. // 1. if challenge is bigger than 32 bytes only use the last 32 bytes
  415. // 2. right justify (0) challenge in ClientRandom if less than 32
  416. Buffer.BlockCopy (challenge, challenge.Length - length, this.context.ClientRandom, 32 - length, length);
  417. // Set
  418. this.context.LastHandshakeMsg = HandshakeType.ClientHello;
  419. this.context.ProtocolNegotiated = true;
  420. return message;
  421. }
  422. private byte[] ReadStandardRecordBuffer (Stream record)
  423. {
  424. byte[] header = new byte[4];
  425. if (record.Read (header, 0, 4) != 4)
  426. throw new TlsException ("buffer underrun");
  427. short protocol = (short)((header [0] << 8) | header [1]);
  428. short length = (short)((header [2] << 8) | header [3]);
  429. // process further only if the whole record is available
  430. // note: the first 5 bytes aren't part of the length
  431. if (record.CanSeek && (length + 5 > record.Length))
  432. {
  433. return null;
  434. }
  435. // Read Record data
  436. int totalReceived = 0;
  437. byte[] buffer = new byte[length];
  438. while (totalReceived != length)
  439. {
  440. int justReceived = record.Read(buffer, totalReceived, buffer.Length - totalReceived);
  441. //Make sure we get some data so we don't end up in an infinite loop here before shutdown.
  442. if (0 == justReceived)
  443. {
  444. throw new TlsException(AlertDescription.CloseNotify, "Received 0 bytes from stream. It must be closed.");
  445. }
  446. totalReceived += justReceived;
  447. }
  448. // Check that the message has a valid protocol version
  449. if (protocol != this.context.Protocol && this.context.ProtocolNegotiated)
  450. {
  451. throw new TlsException(
  452. AlertDescription.ProtocolVersion, "Invalid protocol version on message received");
  453. }
  454. DebugHelper.WriteLine("Record data", buffer);
  455. return buffer;
  456. }
  457. private void ProcessAlert(AlertLevel alertLevel, AlertDescription alertDesc)
  458. {
  459. switch (alertLevel)
  460. {
  461. case AlertLevel.Fatal:
  462. throw new TlsException(alertLevel, alertDesc);
  463. case AlertLevel.Warning:
  464. default:
  465. switch (alertDesc)
  466. {
  467. case AlertDescription.CloseNotify:
  468. this.context.ConnectionEnd = true;
  469. break;
  470. }
  471. break;
  472. }
  473. }
  474. #endregion
  475. #region Send Alert Methods
  476. public void SendAlert(AlertDescription description)
  477. {
  478. this.SendAlert(new Alert(description));
  479. }
  480. public void SendAlert(
  481. AlertLevel level,
  482. AlertDescription description)
  483. {
  484. this.SendAlert(new Alert(level, description));
  485. }
  486. public void SendAlert(Alert alert)
  487. {
  488. AlertLevel level;
  489. AlertDescription description;
  490. bool close;
  491. if (alert == null) {
  492. DebugHelper.WriteLine(">>>> Write Alert NULL");
  493. level = AlertLevel.Fatal;
  494. description = AlertDescription.InternalError;
  495. close = true;
  496. } else {
  497. DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
  498. level = alert.Level;
  499. description = alert.Description;
  500. close = alert.IsCloseNotify;
  501. }
  502. // Write record
  503. this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
  504. if (close)
  505. {
  506. this.context.ConnectionEnd = true;
  507. }
  508. }
  509. #endregion
  510. #region Send Record Methods
  511. public void SendChangeCipherSpec()
  512. {
  513. DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
  514. // Send Change Cipher Spec message with the current cipher
  515. // or as plain text if this is the initial negotiation
  516. this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
  517. Context ctx = this.context;
  518. // Reset sequence numbers
  519. ctx.WriteSequenceNumber = 0;
  520. // all further data sent will be encrypted with the negotiated
  521. // security parameters (now the current parameters)
  522. if (ctx is ClientContext) {
  523. ctx.StartSwitchingSecurityParameters (true);
  524. } else {
  525. ctx.EndSwitchingSecurityParameters (false);
  526. }
  527. }
  528. public IAsyncResult BeginSendRecord(HandshakeType handshakeType, AsyncCallback callback, object state)
  529. {
  530. HandshakeMessage msg = this.GetMessage(handshakeType);
  531. msg.Process();
  532. DebugHelper.WriteLine(">>>> Write handshake record ({0}|{1})", context.Protocol, msg.ContentType);
  533. SendRecordAsyncResult internalResult = new SendRecordAsyncResult(callback, state, msg);
  534. this.BeginSendRecord(msg.ContentType, msg.EncodeMessage(), new AsyncCallback(InternalSendRecordCallback), internalResult);
  535. return internalResult;
  536. }
  537. private void InternalSendRecordCallback(IAsyncResult ar)
  538. {
  539. SendRecordAsyncResult internalResult = ar.AsyncState as SendRecordAsyncResult;
  540. try
  541. {
  542. this.EndSendRecord(ar);
  543. // Update session
  544. internalResult.Message.Update();
  545. // Reset message contents
  546. internalResult.Message.Reset();
  547. internalResult.SetComplete();
  548. }
  549. catch (Exception ex)
  550. {
  551. internalResult.SetComplete(ex);
  552. }
  553. }
  554. public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
  555. {
  556. if (this.context.ConnectionEnd)
  557. {
  558. throw new TlsException(
  559. AlertDescription.InternalError,
  560. "The session is finished and it's no longer valid.");
  561. }
  562. byte[] record = this.EncodeRecord(contentType, recordData);
  563. return this.innerStream.BeginWrite(record, 0, record.Length, callback, state);
  564. }
  565. public void EndSendRecord(IAsyncResult asyncResult)
  566. {
  567. if (asyncResult is SendRecordAsyncResult)
  568. {
  569. SendRecordAsyncResult internalResult = asyncResult as SendRecordAsyncResult;
  570. if (!internalResult.IsCompleted)
  571. internalResult.AsyncWaitHandle.WaitOne();
  572. if (internalResult.CompletedWithError)
  573. throw internalResult.AsyncException;
  574. }
  575. else
  576. {
  577. this.innerStream.EndWrite(asyncResult);
  578. }
  579. }
  580. public void SendRecord(ContentType contentType, byte[] recordData)
  581. {
  582. IAsyncResult ar = this.BeginSendRecord(contentType, recordData, null, null);
  583. this.EndSendRecord(ar);
  584. }
  585. public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
  586. {
  587. return this.EncodeRecord(
  588. contentType,
  589. recordData,
  590. 0,
  591. recordData.Length);
  592. }
  593. public byte[] EncodeRecord(
  594. ContentType contentType,
  595. byte[] recordData,
  596. int offset,
  597. int count)
  598. {
  599. if (this.context.ConnectionEnd)
  600. {
  601. throw new TlsException(
  602. AlertDescription.InternalError,
  603. "The session is finished and it's no longer valid.");
  604. }
  605. TlsStream record = new TlsStream();
  606. int position = offset;
  607. while (position < ( offset + count ))
  608. {
  609. short fragmentLength = 0;
  610. byte[] fragment;
  611. if ((count + offset - position) > Context.MAX_FRAGMENT_SIZE)
  612. {
  613. fragmentLength = Context.MAX_FRAGMENT_SIZE;
  614. }
  615. else
  616. {
  617. fragmentLength = (short)(count + offset - position);
  618. }
  619. // Fill the fragment data
  620. fragment = new byte[fragmentLength];
  621. Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
  622. if ((this.Context.Write != null) && (this.Context.Write.Cipher != null))
  623. {
  624. // Encrypt fragment
  625. fragment = this.encryptRecordFragment (contentType, fragment);
  626. }
  627. // Write tls message
  628. record.Write((byte)contentType);
  629. record.Write(this.context.Protocol);
  630. record.Write((short)fragment.Length);
  631. record.Write(fragment);
  632. DebugHelper.WriteLine("Record data", fragment);
  633. // Update buffer position
  634. position += fragmentLength;
  635. }
  636. return record.ToArray();
  637. }
  638. #endregion
  639. #region Cryptography Methods
  640. private byte[] encryptRecordFragment(
  641. ContentType contentType,
  642. byte[] fragment)
  643. {
  644. byte[] mac = null;
  645. // Calculate message MAC
  646. if (this.Context is ClientContext)
  647. {
  648. mac = this.context.Write.Cipher.ComputeClientRecordMAC(contentType, fragment);
  649. }
  650. else
  651. {
  652. mac = this.context.Write.Cipher.ComputeServerRecordMAC (contentType, fragment);
  653. }
  654. DebugHelper.WriteLine(">>>> Record MAC", mac);
  655. // Encrypt the message
  656. byte[] ecr = this.context.Write.Cipher.EncryptRecord (fragment, mac);
  657. // Update sequence number
  658. this.context.WriteSequenceNumber++;
  659. return ecr;
  660. }
  661. private byte[] decryptRecordFragment(
  662. ContentType contentType,
  663. byte[] fragment)
  664. {
  665. byte[] dcrFragment = null;
  666. byte[] dcrMAC = null;
  667. try
  668. {
  669. this.context.Read.Cipher.DecryptRecord (fragment, out dcrFragment, out dcrMAC);
  670. }
  671. catch
  672. {
  673. if (this.context is ServerContext)
  674. {
  675. this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
  676. }
  677. throw;
  678. }
  679. // Generate record MAC
  680. byte[] mac = null;
  681. if (this.Context is ClientContext)
  682. {
  683. mac = this.context.Read.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
  684. }
  685. else
  686. {
  687. mac = this.context.Read.Cipher.ComputeClientRecordMAC (contentType, dcrFragment);
  688. }
  689. DebugHelper.WriteLine(">>>> Record MAC", mac);
  690. // Check record MAC
  691. if (!Compare (mac, dcrMAC))
  692. {
  693. throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
  694. }
  695. // Update sequence number
  696. this.context.ReadSequenceNumber++;
  697. return dcrFragment;
  698. }
  699. private bool Compare (byte[] array1, byte[] array2)
  700. {
  701. if (array1 == null)
  702. return (array2 == null);
  703. if (array2 == null)
  704. return false;
  705. if (array1.Length != array2.Length)
  706. return false;
  707. for (int i = 0; i < array1.Length; i++) {
  708. if (array1[i] != array2[i])
  709. return false;
  710. }
  711. return true;
  712. }
  713. #endregion
  714. #region CipherSpecV2 processing
  715. private void ProcessCipherSpecV2Buffer (SecurityProtocolType protocol, byte[] buffer)
  716. {
  717. TlsStream codes = new TlsStream(buffer);
  718. string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
  719. while (codes.Position < codes.Length)
  720. {
  721. byte check = codes.ReadByte();
  722. if (check == 0)
  723. {
  724. // SSL/TLS cipher spec
  725. short code = codes.ReadInt16();
  726. int index = this.Context.SupportedCiphers.IndexOf(code);
  727. if (index != -1)
  728. {
  729. this.Context.Negotiating.Cipher = this.Context.SupportedCiphers[index];
  730. break;
  731. }
  732. }
  733. else
  734. {
  735. byte[] tmp = new byte[2];
  736. codes.Read(tmp, 0, tmp.Length);
  737. int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
  738. CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
  739. if (cipher != null)
  740. {
  741. this.Context.Negotiating.Cipher = cipher;
  742. break;
  743. }
  744. }
  745. }
  746. if (this.Context.Negotiating == null)
  747. {
  748. throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
  749. }
  750. }
  751. private CipherSuite MapV2CipherCode(string prefix, int code)
  752. {
  753. try
  754. {
  755. switch (code)
  756. {
  757. case 65664:
  758. // TLS_RC4_128_WITH_MD5
  759. return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
  760. case 131200:
  761. // TLS_RC4_128_EXPORT40_WITH_MD5
  762. return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
  763. case 196736:
  764. // TLS_RC2_CBC_128_CBC_WITH_MD5
  765. return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
  766. case 262272:
  767. // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
  768. return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
  769. case 327808:
  770. // TLS_IDEA_128_CBC_WITH_MD5
  771. return null;
  772. case 393280:
  773. // TLS_DES_64_CBC_WITH_MD5
  774. return null;
  775. case 458944:
  776. // TLS_DES_192_EDE3_CBC_WITH_MD5
  777. return null;
  778. default:
  779. return null;
  780. }
  781. }
  782. catch
  783. {
  784. return null;
  785. }
  786. }
  787. #endregion
  788. }
  789. }