1// Copyright 2017 Google Inc. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package tls 6 7import ( 8 "errors" 9 "io" 10) 11 12type TLSExtension interface { 13 writeToUConn(*UConn) error 14 15 Len() int // includes header 16 17 // Read reads up to len(p) bytes into p. 18 // It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. 19 Read(p []byte) (n int, err error) // implements io.Reader 20} 21 22type NPNExtension struct { 23 NextProtos []string 24} 25 26func (e *NPNExtension) writeToUConn(uc *UConn) error { 27 uc.config.NextProtos = e.NextProtos 28 uc.HandshakeState.Hello.NextProtoNeg = true 29 return nil 30} 31 32func (e *NPNExtension) Len() int { 33 return 4 34} 35 36func (e *NPNExtension) Read(b []byte) (int, error) { 37 if len(b) < e.Len() { 38 return 0, io.ErrShortBuffer 39 } 40 b[0] = byte(extensionNextProtoNeg >> 8) 41 b[1] = byte(extensionNextProtoNeg & 0xff) 42 // The length is always 0 43 return e.Len(), io.EOF 44} 45 46type SNIExtension struct { 47 ServerName string // not an array because go crypto/tls doesn't support multiple SNIs 48} 49 50func (e *SNIExtension) writeToUConn(uc *UConn) error { 51 uc.config.ServerName = e.ServerName 52 uc.HandshakeState.Hello.ServerName = e.ServerName 53 return nil 54} 55 56func (e *SNIExtension) Len() int { 57 return 4 + 2 + 1 + 2 + len(e.ServerName) 58} 59 60func (e *SNIExtension) Read(b []byte) (int, error) { 61 if len(b) < e.Len() { 62 return 0, io.ErrShortBuffer 63 } 64 // RFC 3546, section 3.1 65 b[0] = byte(extensionServerName >> 8) 66 b[1] = byte(extensionServerName) 67 b[2] = byte((len(e.ServerName) + 5) >> 8) 68 b[3] = byte((len(e.ServerName) + 5)) 69 b[4] = byte((len(e.ServerName) + 3) >> 8) 70 b[5] = byte(len(e.ServerName) + 3) 71 // b[6] Server Name Type: host_name (0) 72 b[7] = byte(len(e.ServerName) >> 8) 73 b[8] = byte(len(e.ServerName)) 74 copy(b[9:], []byte(e.ServerName)) 75 return e.Len(), io.EOF 76} 77 78type StatusRequestExtension struct { 79} 80 81func (e *StatusRequestExtension) writeToUConn(uc *UConn) error { 82 uc.HandshakeState.Hello.OcspStapling = true 83 return nil 84} 85 86func (e *StatusRequestExtension) Len() int { 87 return 9 88} 89 90func (e *StatusRequestExtension) Read(b []byte) (int, error) { 91 if len(b) < e.Len() { 92 return 0, io.ErrShortBuffer 93 } 94 // RFC 4366, section 3.6 95 b[0] = byte(extensionStatusRequest >> 8) 96 b[1] = byte(extensionStatusRequest) 97 b[2] = 0 98 b[3] = 5 99 b[4] = 1 // OCSP type 100 // Two zero valued uint16s for the two lengths. 101 return e.Len(), io.EOF 102} 103 104type SupportedCurvesExtension struct { 105 Curves []CurveID 106} 107 108func (e *SupportedCurvesExtension) writeToUConn(uc *UConn) error { 109 uc.config.CurvePreferences = e.Curves 110 uc.HandshakeState.Hello.SupportedCurves = e.Curves 111 return nil 112} 113 114func (e *SupportedCurvesExtension) Len() int { 115 return 6 + 2*len(e.Curves) 116} 117 118func (e *SupportedCurvesExtension) Read(b []byte) (int, error) { 119 if len(b) < e.Len() { 120 return 0, io.ErrShortBuffer 121 } 122 // http://tools.ietf.org/html/rfc4492#section-5.5.1 123 b[0] = byte(extensionSupportedCurves >> 8) 124 b[1] = byte(extensionSupportedCurves) 125 b[2] = byte((2 + 2*len(e.Curves)) >> 8) 126 b[3] = byte((2 + 2*len(e.Curves))) 127 b[4] = byte((2 * len(e.Curves)) >> 8) 128 b[5] = byte((2 * len(e.Curves))) 129 for i, curve := range e.Curves { 130 b[6+2*i] = byte(curve >> 8) 131 b[7+2*i] = byte(curve) 132 } 133 return e.Len(), io.EOF 134} 135 136type SupportedPointsExtension struct { 137 SupportedPoints []uint8 138} 139 140func (e *SupportedPointsExtension) writeToUConn(uc *UConn) error { 141 uc.HandshakeState.Hello.SupportedPoints = e.SupportedPoints 142 return nil 143} 144 145func (e *SupportedPointsExtension) Len() int { 146 return 5 + len(e.SupportedPoints) 147} 148 149func (e *SupportedPointsExtension) Read(b []byte) (int, error) { 150 if len(b) < e.Len() { 151 return 0, io.ErrShortBuffer 152 } 153 // http://tools.ietf.org/html/rfc4492#section-5.5.2 154 b[0] = byte(extensionSupportedPoints >> 8) 155 b[1] = byte(extensionSupportedPoints) 156 b[2] = byte((1 + len(e.SupportedPoints)) >> 8) 157 b[3] = byte((1 + len(e.SupportedPoints))) 158 b[4] = byte((len(e.SupportedPoints))) 159 for i, pointFormat := range e.SupportedPoints { 160 b[5+i] = pointFormat 161 } 162 return e.Len(), io.EOF 163} 164 165type SignatureAlgorithmsExtension struct { 166 SupportedSignatureAlgorithms []SignatureScheme 167} 168 169func (e *SignatureAlgorithmsExtension) writeToUConn(uc *UConn) error { 170 uc.HandshakeState.Hello.SupportedSignatureAlgorithms = e.SupportedSignatureAlgorithms 171 return nil 172} 173 174func (e *SignatureAlgorithmsExtension) Len() int { 175 return 6 + 2*len(e.SupportedSignatureAlgorithms) 176} 177 178func (e *SignatureAlgorithmsExtension) Read(b []byte) (int, error) { 179 if len(b) < e.Len() { 180 return 0, io.ErrShortBuffer 181 } 182 // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 183 b[0] = byte(extensionSignatureAlgorithms >> 8) 184 b[1] = byte(extensionSignatureAlgorithms) 185 b[2] = byte((2 + 2*len(e.SupportedSignatureAlgorithms)) >> 8) 186 b[3] = byte((2 + 2*len(e.SupportedSignatureAlgorithms))) 187 b[4] = byte((2 * len(e.SupportedSignatureAlgorithms)) >> 8) 188 b[5] = byte((2 * len(e.SupportedSignatureAlgorithms))) 189 for i, sigAndHash := range e.SupportedSignatureAlgorithms { 190 b[6+2*i] = byte(sigAndHash >> 8) 191 b[7+2*i] = byte(sigAndHash) 192 } 193 return e.Len(), io.EOF 194} 195 196type RenegotiationInfoExtension struct { 197 renegotiation RenegotiationSupport 198 SecureRenegotiation []byte // if empty, default []byte{0} is assumed 199} 200 201func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error { 202 uc.config.Renegotiation = e.renegotiation 203 switch e.renegotiation { 204 case RenegotiateOnceAsClient: 205 fallthrough 206 case RenegotiateFreelyAsClient: 207 uc.HandshakeState.Hello.SecureRenegotiationSupported = true 208 // Note that if we manage to use this in renegotiation(currently only in initial handshake), we'd have to point 209 // uc.ClientHelloMsg.SecureRenegotiation = chs.C.clientFinished 210 // and probably do something else. It's a mess. 211 case RenegotiateNever: 212 default: 213 } 214 return nil 215} 216 217func (e *RenegotiationInfoExtension) Len() int { 218 switch e.renegotiation { 219 case RenegotiateOnceAsClient: 220 fallthrough 221 case RenegotiateFreelyAsClient: 222 extBodyLen := len(e.SecureRenegotiation) 223 if extBodyLen == 0 { 224 extBodyLen = 1 225 } 226 return 4 + extBodyLen 227 case RenegotiateNever: 228 default: 229 } 230 return 0 231} 232 233func (e *RenegotiationInfoExtension) Read(b []byte) (int, error) { 234 if len(b) < e.Len() { 235 return 0, io.ErrShortBuffer 236 } 237 switch e.renegotiation { 238 case RenegotiateOnceAsClient: 239 fallthrough 240 case RenegotiateFreelyAsClient: 241 secureRenegBody := e.SecureRenegotiation 242 if len(secureRenegBody) == 0 { 243 secureRenegBody = []byte{0} 244 } 245 extBodyLen := len(secureRenegBody) 246 247 b[0] = byte(extensionRenegotiationInfo >> 8) 248 b[1] = byte(extensionRenegotiationInfo & 0xff) 249 b[2] = byte(extBodyLen >> 8) 250 b[3] = byte(extBodyLen) 251 copy(b[4:], secureRenegBody) 252 253 if len(e.SecureRenegotiation) != 0 { 254 copy(b[5:], e.SecureRenegotiation) 255 } 256 case RenegotiateNever: 257 default: 258 } 259 return e.Len(), io.EOF 260} 261 262type ALPNExtension struct { 263 AlpnProtocols []string 264} 265 266func (e *ALPNExtension) writeToUConn(uc *UConn) error { 267 uc.config.NextProtos = e.AlpnProtocols 268 uc.HandshakeState.Hello.AlpnProtocols = e.AlpnProtocols 269 return nil 270} 271 272func (e *ALPNExtension) Len() int { 273 bLen := 2 + 2 + 2 274 for _, s := range e.AlpnProtocols { 275 bLen += 1 + len(s) 276 } 277 return bLen 278} 279 280func (e *ALPNExtension) Read(b []byte) (int, error) { 281 if len(b) < e.Len() { 282 return 0, io.ErrShortBuffer 283 } 284 285 b[0] = byte(extensionALPN >> 8) 286 b[1] = byte(extensionALPN & 0xff) 287 lengths := b[2:] 288 b = b[6:] 289 290 stringsLength := 0 291 for _, s := range e.AlpnProtocols { 292 l := len(s) 293 b[0] = byte(l) 294 copy(b[1:], s) 295 b = b[1+l:] 296 stringsLength += 1 + l 297 } 298 299 lengths[2] = byte(stringsLength >> 8) 300 lengths[3] = byte(stringsLength) 301 stringsLength += 2 302 lengths[0] = byte(stringsLength >> 8) 303 lengths[1] = byte(stringsLength) 304 305 return e.Len(), io.EOF 306} 307 308type SCTExtension struct { 309} 310 311func (e *SCTExtension) writeToUConn(uc *UConn) error { 312 uc.HandshakeState.Hello.Scts = true 313 return nil 314} 315 316func (e *SCTExtension) Len() int { 317 return 4 318} 319 320func (e *SCTExtension) Read(b []byte) (int, error) { 321 if len(b) < e.Len() { 322 return 0, io.ErrShortBuffer 323 } 324 // https://tools.ietf.org/html/rfc6962#section-3.3.1 325 b[0] = byte(extensionSCT >> 8) 326 b[1] = byte(extensionSCT) 327 // zero uint16 for the zero-length extension_data 328 return e.Len(), io.EOF 329} 330 331type SessionTicketExtension struct { 332 Session *ClientSessionState 333} 334 335func (e *SessionTicketExtension) writeToUConn(uc *UConn) error { 336 if e.Session != nil { 337 uc.HandshakeState.Session = e.Session 338 uc.HandshakeState.Hello.SessionTicket = e.Session.sessionTicket 339 } 340 return nil 341} 342 343func (e *SessionTicketExtension) Len() int { 344 if e.Session != nil { 345 return 4 + len(e.Session.sessionTicket) 346 } 347 return 4 348} 349 350func (e *SessionTicketExtension) Read(b []byte) (int, error) { 351 if len(b) < e.Len() { 352 return 0, io.ErrShortBuffer 353 } 354 355 extBodyLen := e.Len() - 4 356 357 b[0] = byte(extensionSessionTicket >> 8) 358 b[1] = byte(extensionSessionTicket) 359 b[2] = byte(extBodyLen >> 8) 360 b[3] = byte(extBodyLen) 361 if extBodyLen > 0 { 362 copy(b[4:], e.Session.sessionTicket) 363 } 364 return e.Len(), io.EOF 365} 366 367type GenericExtension struct { 368 id uint16 369 data []byte 370} 371 372func (e *GenericExtension) writeToUConn(uc *UConn) error { 373 return nil 374} 375 376func (e *GenericExtension) Len() int { 377 return 4 + len(e.data) 378} 379 380func (e *GenericExtension) Read(b []byte) (int, error) { 381 if len(b) < e.Len() { 382 return 0, io.ErrShortBuffer 383 } 384 385 b[0] = byte(e.id >> 8) 386 b[1] = byte(e.id) 387 b[2] = byte(len(e.data) >> 8) 388 b[3] = byte(len(e.data)) 389 if len(e.data) > 0 { 390 copy(b[4:], e.data) 391 } 392 return e.Len(), io.EOF 393} 394 395type UtlsExtendedMasterSecretExtension struct { 396} 397 398// TODO: update when this extension is implemented in crypto/tls 399// but we probably won't have to enable it in Config 400func (e *UtlsExtendedMasterSecretExtension) writeToUConn(uc *UConn) error { 401 uc.HandshakeState.Hello.Ems = true 402 return nil 403} 404 405func (e *UtlsExtendedMasterSecretExtension) Len() int { 406 return 4 407} 408 409func (e *UtlsExtendedMasterSecretExtension) Read(b []byte) (int, error) { 410 if len(b) < e.Len() { 411 return 0, io.ErrShortBuffer 412 } 413 // https://tools.ietf.org/html/rfc7627 414 b[0] = byte(utlsExtensionExtendedMasterSecret >> 8) 415 b[1] = byte(utlsExtensionExtendedMasterSecret) 416 // The length is 0 417 return e.Len(), io.EOF 418} 419 420var extendedMasterSecretLabel = []byte("extended master secret") 421 422// extendedMasterFromPreMasterSecret generates the master secret from the pre-master 423// secret and session hash. See https://tools.ietf.org/html/rfc7627#section-4 424func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, fh finishedHash) []byte { 425 sessionHash := fh.Sum() 426 masterSecret := make([]byte, masterSecretLength) 427 prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, sessionHash) 428 return masterSecret 429} 430 431// GREASE stinks with dead parrots, have to be super careful, and, if possible, not include GREASE 432// https://github.com/google/boringssl/blob/1c68fa2350936ca5897a66b430ebaf333a0e43f5/ssl/internal.h 433const ( 434 ssl_grease_cipher = iota 435 ssl_grease_group 436 ssl_grease_extension1 437 ssl_grease_extension2 438 ssl_grease_version 439 ssl_grease_ticket_extension 440 ssl_grease_last_index = ssl_grease_ticket_extension 441) 442 443// it is responsibility of user not to generate multiple grease extensions with same value 444type UtlsGREASEExtension struct { 445 Value uint16 446 Body []byte // in Chrome first grease has empty body, second grease has a single zero byte 447} 448 449func (e *UtlsGREASEExtension) writeToUConn(uc *UConn) error { 450 return nil 451} 452 453// will panic if ssl_grease_last_index[index] is out of bounds. 454func GetBoringGREASEValue(greaseSeed [ssl_grease_last_index]uint16, index int) uint16 { 455 // GREASE value is back from deterministic to random. 456 // https://github.com/google/boringssl/blob/a365138ac60f38b64bfc608b493e0f879845cb88/ssl/handshake_client.c#L530 457 ret := uint16(greaseSeed[index]) 458 /* This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16. */ 459 ret = (ret & 0xf0) | 0x0a 460 ret |= ret << 8 461 return ret 462} 463 464func (e *UtlsGREASEExtension) Len() int { 465 return 4 + len(e.Body) 466} 467 468func (e *UtlsGREASEExtension) Read(b []byte) (int, error) { 469 if len(b) < e.Len() { 470 return 0, io.ErrShortBuffer 471 } 472 473 b[0] = byte(e.Value >> 8) 474 b[1] = byte(e.Value) 475 b[2] = byte(len(e.Body) >> 8) 476 b[3] = byte(len(e.Body)) 477 if len(e.Body) > 0 { 478 copy(b[4:], e.Body) 479 } 480 return e.Len(), io.EOF 481} 482 483type UtlsPaddingExtension struct { 484 PaddingLen int 485 WillPad bool // set to false to disable extension 486 487 // Functor for deciding on padding length based on unpadded ClientHello length. 488 // If willPad is false, then this extension should not be included. 489 GetPaddingLen func(clientHelloUnpaddedLen int) (paddingLen int, willPad bool) 490} 491 492func (e *UtlsPaddingExtension) writeToUConn(uc *UConn) error { 493 return nil 494} 495 496func (e *UtlsPaddingExtension) Len() int { 497 if e.WillPad { 498 return 4 + e.PaddingLen 499 } else { 500 return 0 501 } 502} 503 504func (e *UtlsPaddingExtension) Update(clientHelloUnpaddedLen int) { 505 if e.GetPaddingLen != nil { 506 e.PaddingLen, e.WillPad = e.GetPaddingLen(clientHelloUnpaddedLen) 507 } 508} 509 510func (e *UtlsPaddingExtension) Read(b []byte) (int, error) { 511 if !e.WillPad { 512 return 0, io.EOF 513 } 514 if len(b) < e.Len() { 515 return 0, io.ErrShortBuffer 516 } 517 // https://tools.ietf.org/html/rfc7627 518 b[0] = byte(utlsExtensionPadding >> 8) 519 b[1] = byte(utlsExtensionPadding) 520 b[2] = byte(e.PaddingLen >> 8) 521 b[3] = byte(e.PaddingLen) 522 return e.Len(), io.EOF 523} 524 525// https://github.com/google/boringssl/blob/7d7554b6b3c79e707e25521e61e066ce2b996e4c/ssl/t1_lib.c#L2803 526func BoringPaddingStyle(unpaddedLen int) (int, bool) { 527 if unpaddedLen > 0xff && unpaddedLen < 0x200 { 528 paddingLen := 0x200 - unpaddedLen 529 if paddingLen >= 4+1 { 530 paddingLen -= 4 531 } else { 532 paddingLen = 1 533 } 534 return paddingLen, true 535 } 536 return 0, false 537} 538 539/* TLS 1.3 */ 540type KeyShareExtension struct { 541 KeyShares []KeyShare 542} 543 544func (e *KeyShareExtension) Len() int { 545 return 4 + 2 + e.keySharesLen() 546} 547 548func (e *KeyShareExtension) keySharesLen() int { 549 extLen := 0 550 for _, ks := range e.KeyShares { 551 extLen += 4 + len(ks.Data) 552 } 553 return extLen 554} 555 556func (e *KeyShareExtension) Read(b []byte) (int, error) { 557 if len(b) < e.Len() { 558 return 0, io.ErrShortBuffer 559 } 560 561 b[0] = byte(extensionKeyShare >> 8) 562 b[1] = byte(extensionKeyShare) 563 keySharesLen := e.keySharesLen() 564 b[2] = byte((keySharesLen + 2) >> 8) 565 b[3] = byte((keySharesLen + 2)) 566 b[4] = byte((keySharesLen) >> 8) 567 b[5] = byte((keySharesLen)) 568 569 i := 6 570 for _, ks := range e.KeyShares { 571 b[i] = byte(ks.Group >> 8) 572 b[i+1] = byte(ks.Group) 573 b[i+2] = byte(len(ks.Data) >> 8) 574 b[i+3] = byte(len(ks.Data)) 575 copy(b[i+4:], ks.Data) 576 i += 4 + len(ks.Data) 577 } 578 579 return e.Len(), io.EOF 580} 581 582func (e *KeyShareExtension) writeToUConn(uc *UConn) error { 583 uc.HandshakeState.Hello.KeyShares = e.KeyShares 584 return nil 585} 586 587type PSKKeyExchangeModesExtension struct { 588 Modes []uint8 589} 590 591func (e *PSKKeyExchangeModesExtension) Len() int { 592 return 4 + 1 + len(e.Modes) 593} 594 595func (e *PSKKeyExchangeModesExtension) Read(b []byte) (int, error) { 596 if len(b) < e.Len() { 597 return 0, io.ErrShortBuffer 598 } 599 600 if len(e.Modes) > 255 { 601 return 0, errors.New("too many PSK Key Exchange modes") 602 } 603 604 b[0] = byte(extensionPSKModes >> 8) 605 b[1] = byte(extensionPSKModes) 606 607 modesLen := len(e.Modes) 608 b[2] = byte((modesLen + 1) >> 8) 609 b[3] = byte((modesLen + 1)) 610 b[4] = byte(modesLen) 611 612 if len(e.Modes) > 0 { 613 copy(b[5:], e.Modes) 614 } 615 616 return e.Len(), io.EOF 617} 618 619func (e *PSKKeyExchangeModesExtension) writeToUConn(uc *UConn) error { 620 uc.HandshakeState.Hello.PskModes = e.Modes 621 return nil 622} 623 624type SupportedVersionsExtension struct { 625 Versions []uint16 626} 627 628func (e *SupportedVersionsExtension) writeToUConn(uc *UConn) error { 629 uc.HandshakeState.Hello.SupportedVersions = e.Versions 630 return nil 631} 632 633func (e *SupportedVersionsExtension) Len() int { 634 return 4 + 1 + (2 * len(e.Versions)) 635} 636 637func (e *SupportedVersionsExtension) Read(b []byte) (int, error) { 638 if len(b) < e.Len() { 639 return 0, io.ErrShortBuffer 640 } 641 extLen := 2 * len(e.Versions) 642 if extLen > 255 { 643 return 0, errors.New("too many supported versions") 644 } 645 646 b[0] = byte(extensionSupportedVersions >> 8) 647 b[1] = byte(extensionSupportedVersions) 648 b[2] = byte((extLen + 1) >> 8) 649 b[3] = byte((extLen + 1)) 650 b[4] = byte(extLen) 651 652 i := 5 653 for _, sv := range e.Versions { 654 b[i] = byte(sv >> 8) 655 b[i+1] = byte(sv) 656 i += 2 657 } 658 return e.Len(), io.EOF 659} 660 661// MUST NOT be part of initial ClientHello 662type CookieExtension struct { 663 Cookie []byte 664} 665 666func (e *CookieExtension) writeToUConn(uc *UConn) error { 667 return nil 668} 669 670func (e *CookieExtension) Len() int { 671 return 4 + len(e.Cookie) 672} 673 674func (e *CookieExtension) Read(b []byte) (int, error) { 675 if len(b) < e.Len() { 676 return 0, io.ErrShortBuffer 677 } 678 679 b[0] = byte(extensionCookie >> 8) 680 b[1] = byte(extensionCookie) 681 b[2] = byte(len(e.Cookie) >> 8) 682 b[3] = byte(len(e.Cookie)) 683 if len(e.Cookie) > 0 { 684 copy(b[4:], e.Cookie) 685 } 686 return e.Len(), io.EOF 687} 688 689/* 690FAKE EXTENSIONS 691*/ 692 693type FakeChannelIDExtension struct { 694} 695 696func (e *FakeChannelIDExtension) writeToUConn(uc *UConn) error { 697 return nil 698} 699 700func (e *FakeChannelIDExtension) Len() int { 701 return 4 702} 703 704func (e *FakeChannelIDExtension) Read(b []byte) (int, error) { 705 if len(b) < e.Len() { 706 return 0, io.ErrShortBuffer 707 } 708 // https://tools.ietf.org/html/draft-balfanz-tls-channelid-00 709 b[0] = byte(fakeExtensionChannelID >> 8) 710 b[1] = byte(fakeExtensionChannelID & 0xff) 711 // The length is 0 712 return e.Len(), io.EOF 713} 714 715type FakeRecordSizeLimitExtension struct { 716 Limit uint16 717} 718 719func (e *FakeRecordSizeLimitExtension) writeToUConn(uc *UConn) error { 720 return nil 721} 722 723func (e *FakeRecordSizeLimitExtension) Len() int { 724 return 6 725} 726 727func (e *FakeRecordSizeLimitExtension) Read(b []byte) (int, error) { 728 if len(b) < e.Len() { 729 return 0, io.ErrShortBuffer 730 } 731 // https://tools.ietf.org/html/draft-balfanz-tls-channelid-00 732 b[0] = byte(fakeRecordSizeLimit >> 8) 733 b[1] = byte(fakeRecordSizeLimit & 0xff) 734 735 b[2] = byte(0) 736 b[3] = byte(2) 737 738 b[4] = byte(e.Limit >> 8) 739 b[5] = byte(e.Limit & 0xff) 740 return e.Len(), io.EOF 741} 742