1 /** 2 * TLS Channel 3 * 4 * Copyright: 5 * (C) 2011,2012,2014 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.tls.channel; 12 13 import botan.constants; 14 static if (BOTAN_HAS_TLS): 15 package: 16 17 public import botan.cert.x509.x509cert; 18 public import botan.tls.policy; 19 public import botan.tls.session; 20 public import botan.tls.alert; 21 public import botan.tls.session_manager; 22 public import botan.tls.version_; 23 public import botan.tls.exceptn; 24 public import botan.rng.rng; 25 import core.thread : Thread; 26 import botan.tls.handshake_state; 27 import botan.tls.messages; 28 import botan.tls.heartbeats; 29 import botan.tls.record; 30 import botan.tls.seq_numbers; 31 import botan.utils.rounding; 32 import memutils.dictionarylist; 33 import botan.utils.loadstor; 34 import botan.utils.types; 35 import botan.utils.get_byte; 36 import memutils.hashmap; 37 import std.string : toStringz; 38 import std.algorithm; 39 40 alias DataWriter = void delegate(in ubyte[]); 41 alias OnClearData = void delegate(in ubyte[]); 42 alias OnAlert = void delegate(in TLSAlert, in ubyte[]); 43 alias OnHandshakeComplete = bool delegate(in TLSSession); 44 45 /** 46 * Generic interface for TLS endpoint 47 */ 48 class TLSChannel 49 { 50 public: 51 52 this(DataWriter output_fn, 53 OnClearData data_cb, 54 OnAlert alert_cb, 55 OnHandshakeComplete handshake_cb, 56 TLSSessionManager session_manager, 57 RandomNumberGenerator rng, 58 bool is_datagram, 59 size_t reserved_io_buffer_size) 60 { 61 m_owner = Thread.getThis(); 62 m_handshake_cb = handshake_cb; 63 m_data_cb = data_cb; 64 m_alert_cb = alert_cb; 65 m_output_fn = output_fn; 66 m_rng = rng; 67 m_session_manager = session_manager; 68 /* epoch 0 is plaintext, thus null cipher state */ 69 //m_write_cipher_states[cast(ushort)0] = ConnectionCipherState.init; 70 //m_read_cipher_states[cast(ushort)0] = ConnectionCipherState.init; 71 72 m_writebuf.reserve(reserved_io_buffer_size); 73 m_readbuf.reserve(reserved_io_buffer_size); 74 } 75 76 /** 77 * Inject TLS traffic received from counterparty 78 * Returns: a hint as the how many more bytes we need to process the 79 * current record (this may be 0 if on a record boundary) 80 */ 81 size_t receivedData(const(ubyte)* input, size_t input_size) 82 { 83 84 const size_t max_fragment_size = maximumFragmentSize(); 85 86 try 87 { 88 while (!isClosed() && input_size) 89 { 90 SecureVector!ubyte record; 91 ulong record_sequence = 0; 92 RecordType record_type = NO_RECORD; 93 TLSProtocolVersion record_version; 94 95 size_t consumed = 0; 96 const size_t needed = .readRecord(m_readbuf, 97 input, 98 input_size, 99 m_is_datagram, 100 consumed, 101 record, 102 record_sequence, 103 record_version, 104 record_type, 105 *m_sequence_numbers, 106 &readCipherStateEpoch); 107 assert(consumed > 0, "Got to eat something"); 108 assert(consumed <= input_size, "Record reader consumed sane amount"); 109 110 input += consumed; 111 input_size -= consumed; 112 113 assert(input_size == 0 || needed == 0, "Got a full record or consumed all input"); 114 115 if (input_size == 0 && needed != 0) 116 return needed; // need more data to complete record 117 118 if (record.length > max_fragment_size) 119 throw new TLSException(TLSAlert.RECORD_OVERFLOW, "Plaintext record is too large"); 120 if (record_type == HANDSHAKE || record_type == CHANGE_CIPHER_SPEC) 121 { 122 if (!m_pending_state) 123 { 124 if (record_version.isDatagramProtocol()) 125 { 126 if (m_sequence_numbers) 127 { 128 129 /* 130 * Might be a peer retransmit under epoch - 1 in which 131 * case we must retransmit last flight 132 */ 133 134 (*m_sequence_numbers).readAccept(record_sequence); 135 136 const ushort epoch = record_sequence >> 48; 137 138 if (epoch == sequenceNumbers().currentReadEpoch()) 139 { 140 createHandshakeState(record_version); 141 } 142 else if (epoch == sequenceNumbers().currentReadEpoch() - 1) 143 { 144 assert(m_active_state, "Have active state here"); 145 auto rec = unlock(record); 146 m_active_state.handshakeIo().addRecord(rec, record_type, record_sequence); 147 } 148 } 149 else if (record_sequence == 0) 150 { 151 createHandshakeState(record_version); 152 } 153 } 154 else 155 { 156 createHandshakeState(record_version); 157 } 158 159 } 160 161 if (m_pending_state) 162 { 163 auto rec = unlock(record); 164 m_pending_state.handshakeIo().addRecord(rec, record_type, record_sequence); 165 166 while (true) { 167 if (auto pending = *m_pending_state) { 168 auto msg = pending.getNextHandshakeMsg(); 169 170 if (msg.type == HANDSHAKE_NONE) // no full handshake yet 171 break; 172 173 processHandshakeMsg(activeState(), pending, msg.type, msg.data); 174 } else break; 175 } 176 } 177 } 178 else if (record_type == HEARTBEAT && peerSupportsHeartbeats()) 179 { 180 if (!activeState()) 181 throw new TLSUnexpectedMessage("Heartbeat sent before handshake done"); 182 183 HeartbeatMessage heartbeat = HeartbeatMessage(unlock(record)); 184 185 const Vector!ubyte* payload = &heartbeat.payload(); 186 187 if (heartbeat.isRequest()) 188 { 189 if (!pendingState()) 190 { 191 HeartbeatMessage response = HeartbeatMessage(HeartbeatMessage.RESPONSE, payload.ptr, payload.length); 192 auto rec = response.contents(); 193 sendRecord(HEARTBEAT, rec); 194 } 195 } 196 else 197 { 198 m_alert_cb(TLSAlert(TLSAlert.HEARTBEAT_PAYLOAD), cast(ubyte[])(*payload)[]); 199 } 200 } 201 else if (record_type == APPLICATION_DATA) 202 { 203 if (!activeState()) 204 throw new TLSUnexpectedMessage("Application data before handshake done"); 205 206 /* 207 * OpenSSL among others sends empty records in versions 208 * before TLS v1.1 in order to randomize the IV of the 209 * following record. Avoid spurious callbacks. 210 */ 211 if (record.length > 0) 212 m_data_cb(cast(ubyte[])record[]); 213 } 214 else if (record_type == ALERT) 215 { 216 TLSAlert alert_msg = TLSAlert(record); 217 218 if (alert_msg.type() == TLSAlert.NO_RENEGOTIATION) 219 m_pending_state.free(); 220 221 if (alert_msg.type() == TLSAlert.CLOSE_NOTIFY) 222 sendWarningAlert(TLSAlert.CLOSE_NOTIFY); // reply in kind 223 224 m_alert_cb(alert_msg, null); 225 226 if (alert_msg.isFatal()) 227 { 228 if (auto active = activeState()) { 229 auto entry = &active.serverHello().sessionId(); 230 m_session_manager.removeEntry(*entry); 231 } 232 return 0; 233 } 234 } 235 else if (record_type != NO_RECORD) 236 throw new TLSUnexpectedMessage("Unexpected record type " ~ to!string(record_type) ~ " from counterparty"); 237 } 238 239 return 0; // on a record boundary 240 } 241 catch(TLSException e) 242 { 243 sendFatalAlert(e.type()); 244 throw e; 245 } 246 catch(IntegrityFailure e) 247 { 248 sendFatalAlert(TLSAlert.BAD_RECORD_MAC); 249 throw e; 250 } 251 catch(DecodingError e) 252 { 253 sendFatalAlert(TLSAlert.DECODE_ERROR); 254 throw e; 255 } 256 catch(Exception e) 257 { 258 sendFatalAlert(TLSAlert.INTERNAL_ERROR); 259 throw e; 260 } 261 } 262 263 /** 264 * Inject TLS traffic received from counterparty 265 * Returns: a hint as the how many more bytes we need to process the 266 * current record (this may be 0 if on a record boundary) 267 */ 268 size_t receivedData(const ref Vector!ubyte buf) 269 { 270 return this.receivedData(buf.ptr, buf.length); 271 } 272 273 /** 274 * Inject plaintext intended for counterparty 275 * Throws an exception if isActive() is false 276 */ 277 void send(const(ubyte)* buf, size_t buf_size) 278 { 279 if (!isActive()) 280 throw new TLSClosedException("Data cannot be sent on inactive TLS connection"); 281 282 sendRecordArray(sequenceNumbers().currentWriteEpoch(), APPLICATION_DATA, buf, buf_size); 283 } 284 285 /** 286 * Inject plaintext intended for counterparty 287 * Throws an exception if isActive() is false 288 */ 289 void send(in string str) 290 { 291 this.send(cast(const(ubyte)*)(str.toStringz), str.length); 292 } 293 294 /** 295 * Inject plaintext intended for counterparty 296 * Throws an exception if isActive() is false 297 */ 298 void send(Alloc)(const ref Vector!( char, Alloc ) val) 299 { 300 send(val.ptr, val.length); 301 } 302 303 /** 304 * Send a TLS alert message. If the alert is fatal, the internal 305 * state (keys, etc) will be reset. 306 * 307 * Params: 308 * alert = the TLSAlert to send 309 */ 310 void sendAlert(in TLSAlert alert) 311 { 312 if (alert.isValid() && !isClosed()) 313 { 314 try 315 { 316 auto rec = alert.serialize(); 317 sendRecord(ALERT, rec); 318 } 319 catch (Exception) { /* swallow it */ } 320 } 321 322 if (alert.type() == TLSAlert.NO_RENEGOTIATION) 323 m_pending_state.free(); 324 325 if (alert.isFatal()) { 326 if (auto active = activeState()) { 327 auto entry = &active.serverHello().sessionId(); 328 m_session_manager.removeEntry(*entry); 329 } 330 } 331 } 332 333 /** 334 * Send a warning alert 335 */ 336 void sendWarningAlert(TLSAlertType type) { sendAlert(TLSAlert(type, false)); } 337 338 /** 339 * Send a fatal alert 340 */ 341 void sendFatalAlert(TLSAlertType type) { sendAlert(TLSAlert(type, true)); } 342 343 /** 344 * Send a close notification alert 345 */ 346 void close() { sendWarningAlert(TLSAlert.CLOSE_NOTIFY); } 347 348 /** 349 * Returns: true iff the connection is active for sending application data 350 */ 351 bool isActive() const 352 { 353 return (activeState() !is null); 354 } 355 356 /** 357 * Returns: true iff the connection has been definitely closed 358 */ 359 bool isClosed() const 360 { 361 if (activeState() || pendingState()) 362 return false; 363 364 /* 365 * If no active or pending state, then either we had a connection 366 * and it has been closed, or we are a server which has never 367 * received a connection. This case is detectable by also lacking 368 * m_sequence_numbers 369 */ 370 return (*m_sequence_numbers !is null); 371 } 372 373 /** 374 * Attempt to renegotiate the session 375 * Params: 376 * force_full_renegotiation = if true, require a full renegotiation, 377 * otherwise allow session resumption 378 */ 379 void renegotiate(bool force_full_renegotiation = false) 380 { 381 if (pendingState()) // currently in handshake? 382 return; 383 384 if (const HandshakeState active = activeState()) 385 initiateHandshake(createHandshakeState(active.Version()), 386 force_full_renegotiation); 387 else 388 throw new Exception("Cannot renegotiate on inactive connection"); 389 } 390 391 /** 392 * Returns: true iff the peer supports heartbeat messages 393 */ 394 bool peerSupportsHeartbeats() const 395 { 396 if (const HandshakeState active = activeState()) 397 return active.serverHello().supportsHeartbeats(); 398 return false; 399 } 400 401 /** 402 * Returns: true iff we are allowed to send heartbeat messages 403 */ 404 bool heartbeatSendingAllowed() const 405 { 406 if (const HandshakeState active = activeState()) 407 return active.serverHello().peerCanSendHeartbeats(); 408 return false; 409 } 410 411 /** 412 * Attempt to send a heartbeat message (if negotiated with counterparty) 413 * Params: 414 * payload = will be echoed back 415 * payload_size = size of payload in bytes 416 */ 417 void heartbeat(const(ubyte)* payload, size_t payload_size) 418 { 419 if (heartbeatSendingAllowed()) 420 { 421 HeartbeatMessage heartbeat = HeartbeatMessage(HeartbeatMessage.REQUEST, payload, payload_size); 422 auto rec = heartbeat.contents(); 423 sendRecord(HEARTBEAT, rec); 424 } 425 } 426 427 /** 428 * Attempt to send a heartbeat message (if negotiated with counterparty) 429 */ 430 void heartbeat() { heartbeat(null, 0); } 431 432 /** 433 * Returns: certificate chain of the peer (may be empty) 434 */ 435 Vector!X509Certificate peerCertChain() const 436 { 437 if (const HandshakeState active = activeState()) 438 return getPeerCertChain(active).dup; 439 return Vector!X509Certificate(); 440 } 441 442 /** 443 * Key material export (RFC 5705) 444 * Params: 445 * label = a disambiguating label string 446 * context = a per-association context value 447 * length = the length of the desired key in bytes 448 * Returns: key of length bytes 449 */ 450 const(SymmetricKey) keyMaterialExport(in string label, 451 in string context, 452 size_t length) const 453 { 454 if (auto active = activeState()) 455 { 456 Unique!KDF prf = active.protocolSpecificPrf(); 457 458 const(SecureVector!ubyte)* master_secret = &active.sessionKeys().masterSecret(); 459 460 Vector!ubyte salt; 461 salt ~= label; 462 salt ~= active.clientHello().randomBytes(); 463 salt ~= active.serverHello().randomBytes(); 464 465 if (context != "") 466 { 467 size_t context_size = context.length; 468 if (context_size > 0xFFFF) 469 throw new Exception("key_material_export context is too long"); 470 salt.pushBack(get_byte(0, cast(ushort) context_size)); 471 salt.pushBack(get_byte(1, cast(ushort) context_size)); 472 salt ~= context; 473 } 474 475 return SymmetricKey(prf.deriveKey(length, *master_secret, salt)); 476 } 477 else 478 throw new Exception("key_material_export connection not active"); 479 } 480 481 /// Returns the ALPN chosen in the ServerHello with the ALPN extention 482 const(string) applicationProtocol() const { return m_application_protocol; } 483 484 /// Returns the current session ID 485 const(ubyte[]) sessionId() const { 486 if (auto active = activeState()) { 487 return active.serverHello().sessionIdBytes(); 488 } 489 return null; 490 } 491 492 ~this() 493 { 494 resetState(); 495 } 496 497 protected: 498 499 abstract void processHandshakeMsg(in HandshakeState active_state, 500 HandshakeState pending_state, 501 HandshakeType type, 502 const ref Vector!ubyte contents); 503 504 abstract void initiateHandshake(HandshakeState state, 505 bool force_full_renegotiation); 506 507 abstract Vector!X509Certificate getPeerCertChain(in HandshakeState state) const; 508 509 abstract HandshakeState newHandshakeState(HandshakeIO io); 510 511 HandshakeState createHandshakeState(TLSProtocolVersion _version) 512 { 513 if (pendingState()) 514 throw new InternalError("createHandshakeState called during handshake"); 515 516 if (const HandshakeState active = activeState()) 517 { 518 TLSProtocolVersion active_version = active.Version(); 519 520 if (active_version.isDatagramProtocol() != _version.isDatagramProtocol()) 521 throw new Exception("Active state using version " ~ active_version.toString() ~ 522 " cannot change to " ~ _version.toString() ~ " in pending"); 523 } 524 525 if (!m_sequence_numbers) 526 { 527 if (_version.isDatagramProtocol()) 528 m_sequence_numbers = new DatagramSequenceNumbers; 529 else 530 m_sequence_numbers = new StreamSequenceNumbers; 531 } 532 533 Unique!HandshakeIO io; 534 if (_version.isDatagramProtocol()) { 535 // default MTU is IPv6 min MTU minus UDP/IP headers (TODO: make configurable) 536 const ushort mtu = 1280 - 40 - 8; 537 io = new DatagramHandshakeIO(*m_sequence_numbers, &sendRecordUnderEpoch, mtu); 538 } 539 else { 540 io = new StreamHandshakeIO(&sendRecord); 541 } 542 543 m_pending_state = newHandshakeState(io.release()); 544 545 if (auto active = activeState()) 546 m_pending_state.setVersion(active.Version()); 547 548 return *m_pending_state; 549 } 550 551 /** 552 * Perform a handshake timeout check. This does nothing unless 553 * this is a DTLS channel with a pending handshake state, in 554 * which case we check for timeout and potentially retransmit 555 * handshake packets. 556 */ 557 bool timeoutCheck() { 558 if (m_pending_state) 559 return m_pending_state.handshakeIo().timeoutCheck(); 560 //FIXME: scan cipher suites and remove epochs older than 2*MSL 561 return false; 562 } 563 564 void activateSession() 565 { 566 std.algorithm.swap(m_active_state, m_pending_state); 567 m_pending_state.free(); 568 569 if (!m_active_state.Version().isDatagramProtocol()) 570 { 571 // TLS is easy just remove all but the current state 572 auto current_epoch = sequenceNumbers().currentWriteEpoch(); 573 574 foreach (const ref ushort k, const ref ConnectionCipherState v; m_write_cipher_states) { 575 if (k != current_epoch) { 576 v.destroy(); 577 m_write_cipher_states.remove(k); 578 } 579 } 580 foreach (const ref ushort k, const ref ConnectionCipherState v; m_read_cipher_states) { 581 if (k != current_epoch) { 582 v.destroy(); 583 m_write_cipher_states.remove(k); 584 } 585 } 586 } 587 } 588 589 void changeCipherSpecReader(ConnectionSide side) 590 { 591 auto pending = pendingState(); 592 593 assert(pending && pending.serverHello(), "Have received server hello"); 594 595 if (pending.serverHello().compressionMethod() != NO_COMPRESSION) 596 throw new InternalError("Negotiated unknown compression algorithm"); 597 598 (*m_sequence_numbers).newReadCipherState(); 599 600 const ushort epoch = sequenceNumbers().currentReadEpoch(); 601 602 assert(m_read_cipher_states.get(epoch, ConnectionCipherState.init) is ConnectionCipherState.init, 603 "No read cipher state currently set for next epoch"); 604 605 // flip side as we are reading 606 ConnectionCipherState read_state = new ConnectionCipherState(pending.Version(), 607 (side == CLIENT) ? SERVER : CLIENT, 608 false, 609 pending.ciphersuite(), 610 pending.sessionKeys()); 611 612 m_read_cipher_states[epoch] = read_state; 613 } 614 615 void changeCipherSpecWriter(ConnectionSide side) 616 { 617 auto pending = pendingState(); 618 619 assert(pending && pending.serverHello(), "Have received server hello"); 620 621 if (pending.serverHello().compressionMethod() != NO_COMPRESSION) 622 throw new InternalError("Negotiated unknown compression algorithm"); 623 624 (*m_sequence_numbers).newWriteCipherState(); 625 626 const ushort epoch = sequenceNumbers().currentWriteEpoch(); 627 628 assert(m_write_cipher_states.get(epoch, ConnectionCipherState.init) is ConnectionCipherState.init, "No write cipher state currently set for next epoch"); 629 630 ConnectionCipherState write_state = new ConnectionCipherState(pending.Version(), 631 side, 632 true, 633 pending.ciphersuite(), 634 pending.sessionKeys()); 635 636 m_write_cipher_states[epoch] = write_state; 637 } 638 639 /* secure renegotiation handling */ 640 void secureRenegotiationCheck(const ClientHello client_hello) 641 { 642 const bool secure_renegotiation = client_hello.secureRenegotiation(); 643 644 if (auto active = activeState()) 645 { 646 const bool active_sr = active.clientHello().secureRenegotiation(); 647 648 if (active_sr != secure_renegotiation) 649 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSClient changed its mind about secure renegotiation"); 650 } 651 652 if (secure_renegotiation) 653 { 654 Vector!ubyte data = client_hello.renegotiationInfo(); 655 656 if (data != secureRenegotiationDataForClientHello()) 657 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSClient sent bad values for secure renegotiation"); 658 } 659 } 660 661 void secureRenegotiationCheck(const ServerHello server_hello) 662 { 663 const bool secure_renegotiation = server_hello.secureRenegotiation(); 664 665 if (auto active = activeState()) 666 { 667 const bool active_sr = active.clientHello().secureRenegotiation(); 668 669 if (active_sr != secure_renegotiation) 670 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer changed its mind about secure renegotiation"); 671 } 672 673 if (secure_renegotiation) 674 { 675 const Vector!ubyte data = server_hello.renegotiationInfo(); 676 677 if (data != secureRenegotiationDataForServerHello()) 678 throw new TLSException(TLSAlert.HANDSHAKE_FAILURE, "TLSServer sent bad values for secure renegotiation"); 679 } 680 } 681 682 Vector!ubyte secureRenegotiationDataForClientHello() const 683 { 684 if (auto active = activeState()) { 685 auto verif_data = active.clientFinished().verifyDataBytes(); 686 return Vector!ubyte(verif_data); 687 } 688 return Vector!ubyte(); 689 } 690 691 Vector!ubyte secureRenegotiationDataForServerHello() const 692 { 693 if (auto active = activeState()) 694 { 695 Vector!ubyte buf = active.clientFinished().verifyDataBytes(); 696 buf ~= active.serverFinished().verifyDataBytes(); 697 return buf.move(); 698 } 699 700 return Vector!ubyte(); 701 } 702 703 /** 704 * Returns: true iff the counterparty supports the secure 705 * renegotiation extensions. 706 */ 707 bool secureRenegotiationSupported() const 708 { 709 if (auto active = activeState()) 710 return active.serverHello().secureRenegotiation(); 711 712 if (auto pending = pendingState()) 713 if (auto hello = pending.serverHello()) 714 return hello.secureRenegotiation(); 715 716 return false; 717 } 718 719 RandomNumberGenerator rng() { return m_rng; } 720 721 TLSSessionManager sessionManager() { return m_session_manager; } 722 723 bool saveSession(in TLSSession session) const { return m_handshake_cb(session); } 724 725 private: 726 727 size_t maximumFragmentSize() const 728 { 729 // should we be caching this value? 730 731 if (auto pending = pendingState()) 732 if (auto server_hello = pending.serverHello()) 733 if (size_t frag = server_hello.fragmentSize()) 734 return frag; 735 736 if (auto active = activeState()) 737 if (size_t frag = active.serverHello().fragmentSize()) 738 return frag; 739 740 return MAX_PLAINTEXT_SIZE; 741 } 742 743 void sendRecord(ubyte record_type, const ref Vector!ubyte record) 744 { 745 sendRecordArray(sequenceNumbers().currentWriteEpoch(), record_type, record.ptr, record.length); 746 } 747 748 void sendRecordUnderEpoch(ushort epoch, ubyte record_type, const ref Vector!ubyte record) 749 { 750 sendRecordArray(epoch, record_type, record.ptr, record.length); 751 } 752 753 void sendRecordArray(ushort epoch, ubyte type, const(ubyte)* input, size_t length) 754 { 755 if (length == 0) 756 return; 757 /* 758 * If using CBC mode without an explicit IV (TLS v1.0), 759 * send a single ubyte of plaintext to randomize the (implicit) IV of 760 * the following main block. If using a stream cipher, or TLS v1.1 761 * or higher, this isn't necessary. 762 * 763 * An empty record also works but apparently some implementations do 764 * not like this (https://bugzilla.mozilla.org/show_bug.cgi?id=665814) 765 * 766 * See http://www.openssl.org/~bodo/tls-cbc.txt for background. 767 */ 768 769 auto cipher_state = cast(ConnectionCipherState)writeCipherStateEpoch(epoch); 770 771 if (type == APPLICATION_DATA && cipher_state.cbcWithoutExplicitIv()) 772 { 773 writeRecord(cipher_state, epoch, type, input, 1); 774 input += 1; 775 length -= 1; 776 } 777 778 const size_t max_fragment_size = maximumFragmentSize(); 779 780 while (length) 781 { 782 const size_t sending = std.algorithm.min(length, max_fragment_size); 783 writeRecord(cipher_state, epoch, type, input, sending); 784 785 input += sending; 786 length -= sending; 787 } 788 } 789 790 void writeRecord(ConnectionCipherState cipher_state, ushort epoch, ubyte record_type, const(ubyte)* input, size_t length) 791 { 792 assert(m_pending_state || m_active_state, "Some connection state exists"); 793 794 TLSProtocolVersion record_version = (m_pending_state) ? (m_pending_state.Version()) : (m_active_state.Version()); 795 796 .writeRecord(m_writebuf, 797 record_type, 798 input, 799 length, 800 record_version, 801 (*m_sequence_numbers).nextWriteSequence(epoch), 802 cipher_state, 803 m_rng); 804 805 m_output_fn(cast(ubyte[]) m_writebuf[]); 806 } 807 808 const(ConnectionSequenceNumbers) sequenceNumbers() const 809 { 810 assert(m_sequence_numbers, "Have a sequence numbers object"); 811 return *m_sequence_numbers; 812 } 813 814 const(ConnectionCipherState) readCipherStateEpoch(ushort epoch) const 815 { 816 auto state = m_read_cipher_states.get(epoch, ConnectionCipherState.init); 817 818 assert(state !is ConnectionCipherState.init || epoch == 0, "Have a cipher state for the specified epoch"); 819 820 return state; 821 } 822 823 const(ConnectionCipherState) writeCipherStateEpoch(ushort epoch) const 824 { 825 auto state = m_write_cipher_states.get(epoch, ConnectionCipherState.init); 826 827 assert(state !is ConnectionCipherState.init || epoch == 0, "Have a cipher state for the specified epoch"); 828 829 return state; 830 } 831 832 protected void resetState() 833 { 834 m_active_state.free(); 835 m_pending_state.free(); 836 m_readbuf.destroy(); 837 m_writebuf.destroy(); 838 foreach (const ref k, const ref v; m_write_cipher_states) 839 { 840 v.destroy(); 841 } 842 m_write_cipher_states.clear(); 843 foreach (const ref k, const ref v; m_read_cipher_states) 844 { 845 v.destroy(); 846 } 847 m_read_cipher_states.clear(); 848 } 849 850 const(HandshakeState) activeState() const { return *m_active_state; } 851 852 const(HandshakeState) pendingState() const { return *m_pending_state; } 853 854 Thread m_owner; 855 package string m_application_protocol; 856 bool m_is_datagram; 857 858 /* callbacks */ 859 OnHandshakeComplete m_handshake_cb; 860 OnClearData m_data_cb; 861 OnAlert m_alert_cb; 862 DataWriter m_output_fn; 863 864 /* external state */ 865 RandomNumberGenerator m_rng; 866 package TLSSessionManager m_session_manager; // fixme: package protection for switchContext, use protected: method instead 867 868 /* sequence number state */ 869 Unique!ConnectionSequenceNumbers m_sequence_numbers; 870 871 /* pending and active connection states */ 872 Unique!HandshakeState m_active_state; 873 Unique!HandshakeState m_pending_state; 874 875 /* cipher states for each epoch */ 876 HashMap!(ushort, ConnectionCipherState) m_write_cipher_states; 877 HashMap!(ushort, ConnectionCipherState) m_read_cipher_states; 878 879 /* I/O buffers */ 880 SecureVector!ubyte m_writebuf; 881 SecureVector!ubyte m_readbuf; 882 }