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