1
15
16 package gmtls
17
18 import (
19 "crypto"
20 "crypto/ecdsa"
21 "crypto/rsa"
22 "crypto/subtle"
23 "errors"
24 "fmt"
25 "io"
26 "sync/atomic"
27
28 "github.com/tjfoc/gmsm/x509"
29 )
30
31
32
33 type serverHandshakeState struct {
34 c *Conn
35 clientHello *clientHelloMsg
36 hello *serverHelloMsg
37 suite *cipherSuite
38 ellipticOk bool
39 ecdsaOk bool
40 rsaDecryptOk bool
41 rsaSignOk bool
42 sessionState *sessionState
43 finishedHash finishedHash
44 masterSecret []byte
45 certsFromClient [][]byte
46 cert *Certificate
47 cachedClientHelloInfo *ClientHelloInfo
48 }
49
50
51
52 func (c *Conn) serverHandshake() error {
53
54
55 c.config.serverInitOnce.Do(func() { c.config.serverInit(nil) })
56
57 hs := serverHandshakeState{
58 c: c,
59 }
60 isResume, err := hs.readClientHello()
61 if err != nil {
62 return err
63 }
64
65
66 c.buffering = true
67 if isResume {
68
69 if err := hs.doResumeHandshake(); err != nil {
70 return err
71 }
72 if err := hs.establishKeys(); err != nil {
73 return err
74 }
75
76
77
78 if hs.hello.ticketSupported {
79 if err := hs.sendSessionTicket(); err != nil {
80 return err
81 }
82 }
83 if err := hs.sendFinished(c.serverFinished[:]); err != nil {
84 return err
85 }
86 if _, err := c.flush(); err != nil {
87 return err
88 }
89 c.clientFinishedIsFirst = false
90 if err := hs.readFinished(nil); err != nil {
91 return err
92 }
93 c.didResume = true
94 } else {
95
96
97 if err := hs.doFullHandshake(); err != nil {
98 return err
99 }
100 if err := hs.establishKeys(); err != nil {
101 return err
102 }
103 if err := hs.readFinished(c.clientFinished[:]); err != nil {
104 return err
105 }
106 c.clientFinishedIsFirst = true
107 c.buffering = true
108 if err := hs.sendSessionTicket(); err != nil {
109 return err
110 }
111 if err := hs.sendFinished(nil); err != nil {
112 return err
113 }
114 if _, err := c.flush(); err != nil {
115 return err
116 }
117 }
118 c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
119 atomic.StoreUint32(&c.handshakeStatus, 1)
120
121 return nil
122 }
123
124
125
126 func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
127 c := hs.c
128
129 msg, err := c.readHandshake()
130 if err != nil {
131 return false, err
132 }
133 var ok bool
134 hs.clientHello, ok = msg.(*clientHelloMsg)
135 if !ok {
136 c.sendAlert(alertUnexpectedMessage)
137 return false, unexpectedMessageError(hs.clientHello, msg)
138 }
139
140 if c.config.GetConfigForClient != nil {
141 if newConfig, err := c.config.GetConfigForClient(hs.clientHelloInfo()); err != nil {
142 c.sendAlert(alertInternalError)
143 return false, err
144 } else if newConfig != nil {
145 newConfig.serverInitOnce.Do(func() { newConfig.serverInit(c.config) })
146 c.config = newConfig
147 }
148 }
149
150 c.vers, ok = c.config.mutualVersion(hs.clientHello.vers)
151 if !ok {
152 c.sendAlert(alertProtocolVersion)
153 return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
154 }
155 c.haveVers = true
156
157 hs.hello = new(serverHelloMsg)
158
159 supportedCurve := false
160 preferredCurves := c.config.curvePreferences()
161 Curves:
162 for _, curve := range hs.clientHello.supportedCurves {
163 for _, supported := range preferredCurves {
164 if supported == curve {
165 supportedCurve = true
166 break Curves
167 }
168 }
169 }
170
171 supportedPointFormat := false
172 for _, pointFormat := range hs.clientHello.supportedPoints {
173 if pointFormat == pointFormatUncompressed {
174 supportedPointFormat = true
175 break
176 }
177 }
178 hs.ellipticOk = supportedCurve && supportedPointFormat
179
180 foundCompression := false
181
182 for _, compression := range hs.clientHello.compressionMethods {
183 if compression == compressionNone {
184 foundCompression = true
185 break
186 }
187 }
188
189 if !foundCompression {
190 c.sendAlert(alertHandshakeFailure)
191 return false, errors.New("tls: client does not support uncompressed connections")
192 }
193
194 hs.hello.vers = c.vers
195 hs.hello.random = make([]byte, 32)
196 _, err = io.ReadFull(c.config.rand(), hs.hello.random)
197 if err != nil {
198 c.sendAlert(alertInternalError)
199 return false, err
200 }
201
202 if len(hs.clientHello.secureRenegotiation) != 0 {
203 c.sendAlert(alertHandshakeFailure)
204 return false, errors.New("tls: initial handshake had non-empty renegotiation extension")
205 }
206
207 hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
208 hs.hello.compressionMethod = compressionNone
209 if len(hs.clientHello.serverName) > 0 {
210 c.serverName = hs.clientHello.serverName
211 }
212
213 if len(hs.clientHello.alpnProtocols) > 0 {
214 if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
215 hs.hello.alpnProtocol = selectedProto
216 c.clientProtocol = selectedProto
217 }
218 } else {
219
220
221
222
223 if hs.clientHello.nextProtoNeg && len(c.config.NextProtos) > 0 {
224 hs.hello.nextProtoNeg = true
225 hs.hello.nextProtos = c.config.NextProtos
226 }
227 }
228
229 hs.cert, err = c.config.getCertificate(hs.clientHelloInfo())
230 if err != nil {
231 c.sendAlert(alertInternalError)
232 return false, err
233 }
234 if hs.clientHello.scts {
235 hs.hello.scts = hs.cert.SignedCertificateTimestamps
236 }
237
238 if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
239 switch priv.Public().(type) {
240 case *ecdsa.PublicKey:
241 hs.ecdsaOk = true
242 case *rsa.PublicKey:
243 hs.rsaSignOk = true
244 default:
245 c.sendAlert(alertInternalError)
246 return false, fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
247 }
248 }
249 if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
250 switch priv.Public().(type) {
251 case *rsa.PublicKey:
252 hs.rsaDecryptOk = true
253 default:
254 c.sendAlert(alertInternalError)
255 return false, fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
256 }
257 }
258
259 if hs.checkForResumption() {
260 return true, nil
261 }
262
263 var preferenceList, supportedList []uint16
264 if c.config.PreferServerCipherSuites {
265 preferenceList = c.config.cipherSuites()
266 supportedList = hs.clientHello.cipherSuites
267 } else {
268 preferenceList = hs.clientHello.cipherSuites
269 supportedList = c.config.cipherSuites()
270 }
271
272 for _, id := range preferenceList {
273 if hs.setCipherSuite(id, supportedList, c.vers) {
274 break
275 }
276 }
277
278 if hs.suite == nil {
279 c.sendAlert(alertHandshakeFailure)
280 return false, errors.New("tls: no cipher suite supported by both client and server")
281 }
282
283
284 for _, id := range hs.clientHello.cipherSuites {
285 if id == TLS_FALLBACK_SCSV {
286
287 if hs.clientHello.vers < c.config.maxVersion() {
288 c.sendAlert(alertInappropriateFallback)
289 return false, errors.New("tls: client using inappropriate protocol fallback")
290 }
291 break
292 }
293 }
294
295 return false, nil
296 }
297
298
299 func (hs *serverHandshakeState) checkForResumption() bool {
300 c := hs.c
301
302 if c.config.SessionTicketsDisabled {
303 return false
304 }
305
306 var ok bool
307 var sessionTicket = append([]uint8{}, hs.clientHello.sessionTicket...)
308 if hs.sessionState, ok = c.decryptTicket(sessionTicket); !ok {
309 return false
310 }
311
312
313 if c.vers != hs.sessionState.vers {
314 return false
315 }
316
317 cipherSuiteOk := false
318
319 for _, id := range hs.clientHello.cipherSuites {
320 if id == hs.sessionState.cipherSuite {
321 cipherSuiteOk = true
322 break
323 }
324 }
325 if !cipherSuiteOk {
326 return false
327 }
328
329
330 if !hs.setCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers) {
331 return false
332 }
333
334 sessionHasClientCerts := len(hs.sessionState.certificates) != 0
335 needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert
336 if needClientCerts && !sessionHasClientCerts {
337 return false
338 }
339 if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
340 return false
341 }
342
343 return true
344 }
345
346 func (hs *serverHandshakeState) doResumeHandshake() error {
347 c := hs.c
348
349 hs.hello.cipherSuite = hs.suite.id
350
351
352 hs.hello.sessionId = hs.clientHello.sessionId
353 hs.hello.ticketSupported = hs.sessionState.usedOldKey
354 hs.finishedHash = newFinishedHash(c.vers, hs.suite)
355 hs.finishedHash.discardHandshakeBuffer()
356 hs.finishedHash.Write(hs.clientHello.marshal())
357 hs.finishedHash.Write(hs.hello.marshal())
358 if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
359 return err
360 }
361
362 if len(hs.sessionState.certificates) > 0 {
363 if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil {
364 return err
365 }
366 }
367
368 hs.masterSecret = hs.sessionState.masterSecret
369
370 return nil
371 }
372
373 func (hs *serverHandshakeState) doFullHandshake() error {
374 c := hs.c
375
376 if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
377 hs.hello.ocspStapling = true
378 }
379
380 hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
381 hs.hello.cipherSuite = hs.suite.id
382
383 hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
384 if c.config.ClientAuth == NoClientCert {
385
386
387 hs.finishedHash.discardHandshakeBuffer()
388 }
389 hs.finishedHash.Write(hs.clientHello.marshal())
390 hs.finishedHash.Write(hs.hello.marshal())
391 if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
392 return err
393 }
394
395 certMsg := new(certificateMsg)
396 certMsg.certificates = hs.cert.Certificate
397 hs.finishedHash.Write(certMsg.marshal())
398 if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
399 return err
400 }
401
402 if hs.hello.ocspStapling {
403 certStatus := new(certificateStatusMsg)
404 certStatus.statusType = statusTypeOCSP
405 certStatus.response = hs.cert.OCSPStaple
406 hs.finishedHash.Write(certStatus.marshal())
407 if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
408 return err
409 }
410 }
411
412 keyAgreement := hs.suite.ka(c.vers)
413 skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.cert, hs.clientHello, hs.hello)
414 if err != nil {
415 c.sendAlert(alertHandshakeFailure)
416 return err
417 }
418 if skx != nil {
419 hs.finishedHash.Write(skx.marshal())
420 if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
421 return err
422 }
423 }
424
425 if c.config.ClientAuth >= RequestClientCert {
426
427 certReq := new(certificateRequestMsg)
428 certReq.certificateTypes = []byte{
429 byte(certTypeRSASign),
430 byte(certTypeECDSASign),
431 }
432 if c.vers >= VersionTLS12 {
433 certReq.hasSignatureAndHash = true
434 certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
435 }
436
437
438
439
440
441
442 if c.config.ClientCAs != nil {
443 certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
444 }
445 hs.finishedHash.Write(certReq.marshal())
446 if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
447 return err
448 }
449 }
450
451 helloDone := new(serverHelloDoneMsg)
452 hs.finishedHash.Write(helloDone.marshal())
453 if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
454 return err
455 }
456
457 if _, err := c.flush(); err != nil {
458 return err
459 }
460
461 var pub crypto.PublicKey
462
463 msg, err := c.readHandshake()
464 if err != nil {
465 return err
466 }
467
468 var ok bool
469
470
471 if c.config.ClientAuth >= RequestClientCert {
472 if certMsg, ok = msg.(*certificateMsg); !ok {
473 c.sendAlert(alertUnexpectedMessage)
474 return unexpectedMessageError(certMsg, msg)
475 }
476 hs.finishedHash.Write(certMsg.marshal())
477
478 if len(certMsg.certificates) == 0 {
479
480 switch c.config.ClientAuth {
481 case RequireAnyClientCert, RequireAndVerifyClientCert:
482 c.sendAlert(alertBadCertificate)
483 return errors.New("tls: client didn't provide a certificate")
484 }
485 }
486
487 pub, err = hs.processCertsFromClient(certMsg.certificates)
488 if err != nil {
489 return err
490 }
491
492 msg, err = c.readHandshake()
493 if err != nil {
494 return err
495 }
496 }
497
498
499 ckx, ok := msg.(*clientKeyExchangeMsg)
500 if !ok {
501 c.sendAlert(alertUnexpectedMessage)
502 return unexpectedMessageError(ckx, msg)
503 }
504 hs.finishedHash.Write(ckx.marshal())
505
506 preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
507 if err != nil {
508 c.sendAlert(alertHandshakeFailure)
509 return err
510 }
511 hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
512 if err := c.config.writeKeyLog(hs.clientHello.random, hs.masterSecret); err != nil {
513 c.sendAlert(alertInternalError)
514 return err
515 }
516
517
518
519
520
521
522
523 if len(c.peerCertificates) > 0 {
524 msg, err = c.readHandshake()
525 if err != nil {
526 return err
527 }
528 certVerify, ok := msg.(*certificateVerifyMsg)
529 if !ok {
530 c.sendAlert(alertUnexpectedMessage)
531 return unexpectedMessageError(certVerify, msg)
532 }
533
534
535 _, sigType, hashFunc, err := pickSignatureAlgorithm(pub, []SignatureScheme{certVerify.signatureAlgorithm}, supportedSignatureAlgorithms, c.vers)
536 if err != nil {
537 c.sendAlert(alertIllegalParameter)
538 return err
539 }
540
541 var digest []byte
542 if digest, err = hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret); err == nil {
543 err = verifyHandshakeSignature(sigType, pub, hashFunc, digest, certVerify.signature)
544 }
545 if err != nil {
546 c.sendAlert(alertBadCertificate)
547 return errors.New("tls: could not validate signature of connection nonces: " + err.Error())
548 }
549
550 hs.finishedHash.Write(certVerify.marshal())
551 }
552
553 hs.finishedHash.discardHandshakeBuffer()
554
555 return nil
556 }
557
558 func (hs *serverHandshakeState) establishKeys() error {
559 c := hs.c
560
561 clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
562 keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
563
564 var clientCipher, serverCipher interface{}
565 var clientHash, serverHash macFunction
566
567 if hs.suite.aead == nil {
568 clientCipher = hs.suite.cipher(clientKey, clientIV, true )
569 clientHash = hs.suite.mac(c.vers, clientMAC)
570 serverCipher = hs.suite.cipher(serverKey, serverIV, false )
571 serverHash = hs.suite.mac(c.vers, serverMAC)
572 } else {
573 clientCipher = hs.suite.aead(clientKey, clientIV)
574 serverCipher = hs.suite.aead(serverKey, serverIV)
575 }
576
577 c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
578 c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
579
580 return nil
581 }
582
583 func (hs *serverHandshakeState) readFinished(out []byte) error {
584 c := hs.c
585
586 c.readRecord(recordTypeChangeCipherSpec)
587 if c.in.err != nil {
588 return c.in.err
589 }
590
591 if hs.hello.nextProtoNeg {
592 msg, err := c.readHandshake()
593 if err != nil {
594 return err
595 }
596 nextProto, ok := msg.(*nextProtoMsg)
597 if !ok {
598 c.sendAlert(alertUnexpectedMessage)
599 return unexpectedMessageError(nextProto, msg)
600 }
601 hs.finishedHash.Write(nextProto.marshal())
602 c.clientProtocol = nextProto.proto
603 }
604
605 msg, err := c.readHandshake()
606 if err != nil {
607 return err
608 }
609 clientFinished, ok := msg.(*finishedMsg)
610 if !ok {
611 c.sendAlert(alertUnexpectedMessage)
612 return unexpectedMessageError(clientFinished, msg)
613 }
614
615 verify := hs.finishedHash.clientSum(hs.masterSecret)
616 if len(verify) != len(clientFinished.verifyData) ||
617 subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
618 c.sendAlert(alertHandshakeFailure)
619 return errors.New("tls: client's Finished message is incorrect")
620 }
621
622 hs.finishedHash.Write(clientFinished.marshal())
623 copy(out, verify)
624 return nil
625 }
626
627 func (hs *serverHandshakeState) sendSessionTicket() error {
628 if !hs.hello.ticketSupported {
629 return nil
630 }
631
632 c := hs.c
633 m := new(newSessionTicketMsg)
634
635 var err error
636 state := sessionState{
637 vers: c.vers,
638 cipherSuite: hs.suite.id,
639 masterSecret: hs.masterSecret,
640 certificates: hs.certsFromClient,
641 }
642 m.ticket, err = c.encryptTicket(&state)
643 if err != nil {
644 return err
645 }
646
647 hs.finishedHash.Write(m.marshal())
648 if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
649 return err
650 }
651
652 return nil
653 }
654
655 func (hs *serverHandshakeState) sendFinished(out []byte) error {
656 c := hs.c
657
658 if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
659 return err
660 }
661
662 finished := new(finishedMsg)
663 finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
664 hs.finishedHash.Write(finished.marshal())
665 if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
666 return err
667 }
668
669 c.cipherSuite = hs.suite.id
670 copy(out, finished.verifyData)
671
672 return nil
673 }
674
675
676
677
678 func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) {
679 c := hs.c
680
681 hs.certsFromClient = certificates
682 certs := make([]*x509.Certificate, len(certificates))
683 var err error
684 for i, asn1Data := range certificates {
685 if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
686 c.sendAlert(alertBadCertificate)
687 return nil, errors.New("tls: failed to parse client certificate: " + err.Error())
688 }
689 }
690
691 if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
692 opts := x509.VerifyOptions{
693 Roots: c.config.ClientCAs,
694 CurrentTime: c.config.time(),
695 Intermediates: x509.NewCertPool(),
696 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
697 }
698
699 for _, cert := range certs[1:] {
700 opts.Intermediates.AddCert(cert)
701 }
702
703 chains, err := certs[0].Verify(opts)
704 if err != nil {
705 c.sendAlert(alertBadCertificate)
706 return nil, errors.New("tls: failed to verify client's certificate: " + err.Error())
707 }
708
709 c.verifiedChains = chains
710 }
711
712 if c.config.VerifyPeerCertificate != nil {
713 if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
714 c.sendAlert(alertBadCertificate)
715 return nil, err
716 }
717 }
718
719 if len(certs) == 0 {
720 return nil, nil
721 }
722
723 var pub crypto.PublicKey
724 switch key := certs[0].PublicKey.(type) {
725 case *ecdsa.PublicKey, *rsa.PublicKey:
726 pub = key
727 default:
728 c.sendAlert(alertUnsupportedCertificate)
729 return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey)
730 }
731 c.peerCertificates = certs
732 return pub, nil
733 }
734
735
736
737
738 func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16) bool {
739 for _, supported := range supportedCipherSuites {
740 if id == supported {
741 var candidate *cipherSuite
742
743 for _, s := range cipherSuites {
744 if s.id == id {
745 candidate = s
746 break
747 }
748 }
749 if candidate == nil {
750 continue
751 }
752
753
754 if candidate.flags&suiteECDHE != 0 {
755 if !hs.ellipticOk {
756 continue
757 }
758 if candidate.flags&suiteECDSA != 0 {
759 if !hs.ecdsaOk {
760 continue
761 }
762 } else if !hs.rsaSignOk {
763 continue
764 }
765 } else if !hs.rsaDecryptOk {
766 continue
767 }
768 if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
769 continue
770 }
771 hs.suite = candidate
772 return true
773 }
774 }
775 return false
776 }
777
778
779 var suppVersArray = [...]uint16{VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30}
780
781 func (hs *serverHandshakeState) clientHelloInfo() *ClientHelloInfo {
782 if hs.cachedClientHelloInfo != nil {
783 return hs.cachedClientHelloInfo
784 }
785
786 var supportedVersions []uint16
787 if hs.clientHello.vers > VersionTLS12 {
788 supportedVersions = suppVersArray[:]
789 } else if hs.clientHello.vers >= VersionSSL30 {
790 supportedVersions = suppVersArray[VersionTLS12-hs.clientHello.vers:]
791 }
792
793 hs.cachedClientHelloInfo = &ClientHelloInfo{
794 CipherSuites: hs.clientHello.cipherSuites,
795 ServerName: hs.clientHello.serverName,
796 SupportedCurves: hs.clientHello.supportedCurves,
797 SupportedPoints: hs.clientHello.supportedPoints,
798 SignatureSchemes: hs.clientHello.supportedSignatureAlgorithms,
799 SupportedProtos: hs.clientHello.alpnProtocols,
800 SupportedVersions: supportedVersions,
801 Conn: hs.c.conn,
802 }
803
804 return hs.cachedClientHelloInfo
805 }
806
View as plain text