1// Copyright 2018 The Go Authors. 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	"bytes"
9	"crypto"
10	"crypto/hmac"
11	"crypto/rsa"
12	"errors"
13	"hash"
14	"sync/atomic"
15	"time"
16)
17
18type clientHandshakeStateTLS13 struct {
19	c           *Conn
20	serverHello *serverHelloMsg
21	hello       *clientHelloMsg
22	ecdheParams ecdheParameters
23
24	session     *ClientSessionState
25	earlySecret []byte
26	binderKey   []byte
27
28	certReq       *certificateRequestMsgTLS13
29	usingPSK      bool
30	sentDummyCCS  bool
31	suite         *cipherSuiteTLS13
32	transcript    hash.Hash
33	masterSecret  []byte
34	trafficSecret []byte // client_application_traffic_secret_0
35}
36
37// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
38// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
39func (hs *clientHandshakeStateTLS13) handshake() error {
40	c := hs.c
41
42	// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
43	// sections 4.1.2 and 4.1.3.
44	if c.handshakes > 0 {
45		c.sendAlert(alertProtocolVersion)
46		return errors.New("tls: server selected TLS 1.3 in a renegotiation")
47	}
48
49	// Consistency check on the presence of a keyShare and its parameters.
50	if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
51		return c.sendAlert(alertInternalError)
52	}
53
54	if err := hs.checkServerHelloOrHRR(); err != nil {
55		return err
56	}
57
58	hs.transcript = hs.suite.hash.New()
59	hs.transcript.Write(hs.hello.marshal())
60
61	if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
62		if err := hs.sendDummyChangeCipherSpec(); err != nil {
63			return err
64		}
65		if err := hs.processHelloRetryRequest(); err != nil {
66			return err
67		}
68	}
69
70	hs.transcript.Write(hs.serverHello.marshal())
71
72	c.buffering = true
73	if err := hs.processServerHello(); err != nil {
74		return err
75	}
76	if err := hs.sendDummyChangeCipherSpec(); err != nil {
77		return err
78	}
79	if err := hs.establishHandshakeKeys(); err != nil {
80		return err
81	}
82	if err := hs.readServerParameters(); err != nil {
83		return err
84	}
85	if err := hs.readServerCertificate(); err != nil {
86		return err
87	}
88	if err := hs.readServerFinished(); err != nil {
89		return err
90	}
91	if err := hs.sendClientCertificate(); err != nil {
92		return err
93	}
94	if err := hs.sendClientFinished(); err != nil {
95		return err
96	}
97	if _, err := c.flush(); err != nil {
98		return err
99	}
100
101	atomic.StoreUint32(&c.handshakeStatus, 1)
102
103	return nil
104}
105
106// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
107// HelloRetryRequest messages. It sets hs.suite.
108func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
109	c := hs.c
110
111	if hs.serverHello.supportedVersion == 0 {
112		c.sendAlert(alertMissingExtension)
113		return errors.New("tls: server selected TLS 1.3 using the legacy version field")
114	}
115
116	if hs.serverHello.supportedVersion != VersionTLS13 {
117		c.sendAlert(alertIllegalParameter)
118		return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
119	}
120
121	if hs.serverHello.vers != VersionTLS12 {
122		c.sendAlert(alertIllegalParameter)
123		return errors.New("tls: server sent an incorrect legacy version")
124	}
125
126	if hs.serverHello.nextProtoNeg ||
127		len(hs.serverHello.nextProtos) != 0 ||
128		hs.serverHello.ocspStapling ||
129		hs.serverHello.ticketSupported ||
130		hs.serverHello.secureRenegotiationSupported ||
131		len(hs.serverHello.secureRenegotiation) != 0 ||
132		len(hs.serverHello.alpnProtocol) != 0 ||
133		len(hs.serverHello.scts) != 0 {
134		c.sendAlert(alertUnsupportedExtension)
135		return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
136	}
137
138	if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
139		c.sendAlert(alertIllegalParameter)
140		return errors.New("tls: server did not echo the legacy session ID")
141	}
142
143	if hs.serverHello.compressionMethod != compressionNone {
144		c.sendAlert(alertIllegalParameter)
145		return errors.New("tls: server selected unsupported compression format")
146	}
147
148	selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
149	if hs.suite != nil && selectedSuite != hs.suite {
150		c.sendAlert(alertIllegalParameter)
151		return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
152	}
153	if selectedSuite == nil {
154		c.sendAlert(alertIllegalParameter)
155		return errors.New("tls: server chose an unconfigured cipher suite")
156	}
157	hs.suite = selectedSuite
158	c.cipherSuite = hs.suite.id
159
160	return nil
161}
162
163// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
164// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
165func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
166	if hs.sentDummyCCS {
167		return nil
168	}
169	hs.sentDummyCCS = true
170
171	_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
172	return err
173}
174
175// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
176// resends hs.hello, and reads the new ServerHello into hs.serverHello.
177func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
178	c := hs.c
179
180	// The first ClientHello gets double-hashed into the transcript upon a
181	// HelloRetryRequest. See RFC 8446, Section 4.4.1.
182	chHash := hs.transcript.Sum(nil)
183	hs.transcript.Reset()
184	hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
185	hs.transcript.Write(chHash)
186	hs.transcript.Write(hs.serverHello.marshal())
187
188	if hs.serverHello.serverShare.group != 0 {
189		c.sendAlert(alertDecodeError)
190		return errors.New("tls: received malformed key_share extension")
191	}
192
193	curveID := hs.serverHello.selectedGroup
194	if curveID == 0 {
195		c.sendAlert(alertMissingExtension)
196		return errors.New("tls: received HelloRetryRequest without selected group")
197	}
198	curveOK := false
199	for _, id := range hs.hello.supportedCurves {
200		if id == curveID {
201			curveOK = true
202			break
203		}
204	}
205	if !curveOK {
206		c.sendAlert(alertIllegalParameter)
207		return errors.New("tls: server selected unsupported group")
208	}
209	if hs.ecdheParams.CurveID() == curveID {
210		c.sendAlert(alertIllegalParameter)
211		return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
212	}
213	if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
214		c.sendAlert(alertInternalError)
215		return errors.New("tls: CurvePreferences includes unsupported curve")
216	}
217	params, err := generateECDHEParameters(c.config.rand(), curveID)
218	if err != nil {
219		c.sendAlert(alertInternalError)
220		return err
221	}
222	hs.ecdheParams = params
223	hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
224
225	hs.hello.cookie = hs.serverHello.cookie
226
227	hs.hello.raw = nil
228	if len(hs.hello.pskIdentities) > 0 {
229		pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
230		if pskSuite == nil {
231			return c.sendAlert(alertInternalError)
232		}
233		if pskSuite.hash == hs.suite.hash {
234			// Update binders and obfuscated_ticket_age.
235			ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
236			hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
237
238			transcript := hs.suite.hash.New()
239			transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
240			transcript.Write(chHash)
241			transcript.Write(hs.serverHello.marshal())
242			transcript.Write(hs.hello.marshalWithoutBinders())
243			pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
244			hs.hello.updateBinders(pskBinders)
245		} else {
246			// Server selected a cipher suite incompatible with the PSK.
247			hs.hello.pskIdentities = nil
248			hs.hello.pskBinders = nil
249		}
250	}
251
252	hs.transcript.Write(hs.hello.marshal())
253	if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
254		return err
255	}
256
257	msg, err := c.readHandshake()
258	if err != nil {
259		return err
260	}
261
262	serverHello, ok := msg.(*serverHelloMsg)
263	if !ok {
264		c.sendAlert(alertUnexpectedMessage)
265		return unexpectedMessageError(serverHello, msg)
266	}
267	hs.serverHello = serverHello
268
269	if err := hs.checkServerHelloOrHRR(); err != nil {
270		return err
271	}
272
273	return nil
274}
275
276func (hs *clientHandshakeStateTLS13) processServerHello() error {
277	c := hs.c
278
279	if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
280		c.sendAlert(alertUnexpectedMessage)
281		return errors.New("tls: server sent two HelloRetryRequest messages")
282	}
283
284	if len(hs.serverHello.cookie) != 0 {
285		c.sendAlert(alertUnsupportedExtension)
286		return errors.New("tls: server sent a cookie in a normal ServerHello")
287	}
288
289	if hs.serverHello.selectedGroup != 0 {
290		c.sendAlert(alertDecodeError)
291		return errors.New("tls: malformed key_share extension")
292	}
293
294	if hs.serverHello.serverShare.group == 0 {
295		c.sendAlert(alertIllegalParameter)
296		return errors.New("tls: server did not send a key share")
297	}
298	if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
299		c.sendAlert(alertIllegalParameter)
300		return errors.New("tls: server selected unsupported group")
301	}
302
303	if !hs.serverHello.selectedIdentityPresent {
304		return nil
305	}
306
307	if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
308		c.sendAlert(alertIllegalParameter)
309		return errors.New("tls: server selected an invalid PSK")
310	}
311
312	if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
313		return c.sendAlert(alertInternalError)
314	}
315	pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
316	if pskSuite == nil {
317		return c.sendAlert(alertInternalError)
318	}
319	if pskSuite.hash != hs.suite.hash {
320		c.sendAlert(alertIllegalParameter)
321		return errors.New("tls: server selected an invalid PSK and cipher suite pair")
322	}
323
324	hs.usingPSK = true
325	c.didResume = true
326	c.peerCertificates = hs.session.serverCertificates
327	c.verifiedChains = hs.session.verifiedChains
328	return nil
329}
330
331func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
332	c := hs.c
333
334	sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
335	if sharedKey == nil {
336		c.sendAlert(alertIllegalParameter)
337		return errors.New("tls: invalid server key share")
338	}
339
340	earlySecret := hs.earlySecret
341	if !hs.usingPSK {
342		earlySecret = hs.suite.extract(nil, nil)
343	}
344	handshakeSecret := hs.suite.extract(sharedKey,
345		hs.suite.deriveSecret(earlySecret, "derived", nil))
346
347	clientSecret := hs.suite.deriveSecret(handshakeSecret,
348		clientHandshakeTrafficLabel, hs.transcript)
349	c.out.setTrafficSecret(hs.suite, clientSecret)
350	serverSecret := hs.suite.deriveSecret(handshakeSecret,
351		serverHandshakeTrafficLabel, hs.transcript)
352	c.in.setTrafficSecret(hs.suite, serverSecret)
353
354	err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
355	if err != nil {
356		c.sendAlert(alertInternalError)
357		return err
358	}
359	err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
360	if err != nil {
361		c.sendAlert(alertInternalError)
362		return err
363	}
364
365	hs.masterSecret = hs.suite.extract(nil,
366		hs.suite.deriveSecret(handshakeSecret, "derived", nil))
367
368	return nil
369}
370
371func (hs *clientHandshakeStateTLS13) readServerParameters() error {
372	c := hs.c
373
374	msg, err := c.readHandshake()
375	if err != nil {
376		return err
377	}
378
379	encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
380	if !ok {
381		c.sendAlert(alertUnexpectedMessage)
382		return unexpectedMessageError(encryptedExtensions, msg)
383	}
384	hs.transcript.Write(encryptedExtensions.marshal())
385
386	if len(encryptedExtensions.alpnProtocol) != 0 && len(hs.hello.alpnProtocols) == 0 {
387		c.sendAlert(alertUnsupportedExtension)
388		return errors.New("tls: server advertised unrequested ALPN extension")
389	}
390	c.clientProtocol = encryptedExtensions.alpnProtocol
391
392	return nil
393}
394
395func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
396	c := hs.c
397
398	// Either a PSK or a certificate is always used, but not both.
399	// See RFC 8446, Section 4.1.1.
400	if hs.usingPSK {
401		return nil
402	}
403
404	msg, err := c.readHandshake()
405	if err != nil {
406		return err
407	}
408
409	certReq, ok := msg.(*certificateRequestMsgTLS13)
410	if ok {
411		hs.transcript.Write(certReq.marshal())
412
413		hs.certReq = certReq
414
415		msg, err = c.readHandshake()
416		if err != nil {
417			return err
418		}
419	}
420
421	certMsg, ok := msg.(*certificateMsgTLS13)
422	if !ok {
423		c.sendAlert(alertUnexpectedMessage)
424		return unexpectedMessageError(certMsg, msg)
425	}
426	if len(certMsg.certificate.Certificate) == 0 {
427		c.sendAlert(alertDecodeError)
428		return errors.New("tls: received empty certificates message")
429	}
430	hs.transcript.Write(certMsg.marshal())
431
432	c.scts = certMsg.certificate.SignedCertificateTimestamps
433	c.ocspResponse = certMsg.certificate.OCSPStaple
434
435	if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
436		return err
437	}
438
439	msg, err = c.readHandshake()
440	if err != nil {
441		return err
442	}
443
444	certVerify, ok := msg.(*certificateVerifyMsg)
445	if !ok {
446		c.sendAlert(alertUnexpectedMessage)
447		return unexpectedMessageError(certVerify, msg)
448	}
449
450	// See RFC 8446, Section 4.4.3.
451	if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
452		c.sendAlert(alertIllegalParameter)
453		return errors.New("tls: invalid certificate signature algorithm")
454	}
455	sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm)
456	sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm)
457	if sigType == 0 || err != nil {
458		c.sendAlert(alertInternalError)
459		return err
460	}
461	if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
462		c.sendAlert(alertIllegalParameter)
463		return errors.New("tls: invalid certificate signature algorithm")
464	}
465	h := sigHash.New()
466	writeSignedMessage(h, serverSignatureContext, hs.transcript)
467	if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
468		sigHash, h.Sum(nil), certVerify.signature); err != nil {
469		c.sendAlert(alertDecryptError)
470		return errors.New("tls: invalid certificate signature")
471	}
472
473	hs.transcript.Write(certVerify.marshal())
474
475	return nil
476}
477
478func (hs *clientHandshakeStateTLS13) readServerFinished() error {
479	c := hs.c
480
481	msg, err := c.readHandshake()
482	if err != nil {
483		return err
484	}
485
486	finished, ok := msg.(*finishedMsg)
487	if !ok {
488		c.sendAlert(alertUnexpectedMessage)
489		return unexpectedMessageError(finished, msg)
490	}
491
492	expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
493	if !hmac.Equal(expectedMAC, finished.verifyData) {
494		c.sendAlert(alertDecryptError)
495		return errors.New("tls: invalid server finished hash")
496	}
497
498	hs.transcript.Write(finished.marshal())
499
500	// Derive secrets that take context through the server Finished.
501
502	hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
503		clientApplicationTrafficLabel, hs.transcript)
504	serverSecret := hs.suite.deriveSecret(hs.masterSecret,
505		serverApplicationTrafficLabel, hs.transcript)
506	c.in.setTrafficSecret(hs.suite, serverSecret)
507
508	err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
509	if err != nil {
510		c.sendAlert(alertInternalError)
511		return err
512	}
513	err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
514	if err != nil {
515		c.sendAlert(alertInternalError)
516		return err
517	}
518
519	c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
520
521	return nil
522}
523
524func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
525	c := hs.c
526
527	if hs.certReq == nil {
528		return nil
529	}
530
531	cert, err := c.getClientCertificate(&CertificateRequestInfo{
532		AcceptableCAs:    hs.certReq.certificateAuthorities,
533		SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
534	})
535	if err != nil {
536		return err
537	}
538
539	certMsg := new(certificateMsgTLS13)
540
541	certMsg.certificate = *cert
542	certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
543	certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
544
545	hs.transcript.Write(certMsg.marshal())
546	if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
547		return err
548	}
549
550	// If we sent an empty certificate message, skip the CertificateVerify.
551	if len(cert.Certificate) == 0 {
552		return nil
553	}
554
555	certVerifyMsg := new(certificateVerifyMsg)
556	certVerifyMsg.hasSignatureAlgorithm = true
557
558	supportedAlgs := signatureSchemesForCertificate(c.vers, cert)
559	if supportedAlgs == nil {
560		c.sendAlert(alertInternalError)
561		return unsupportedCertificateError(cert)
562	}
563	// Pick signature scheme in server preference order, as the client
564	// preference order is not configurable.
565	for _, preferredAlg := range hs.certReq.supportedSignatureAlgorithms {
566		if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
567			certVerifyMsg.signatureAlgorithm = preferredAlg
568			break
569		}
570	}
571	if certVerifyMsg.signatureAlgorithm == 0 {
572		// getClientCertificate returned a certificate incompatible with the
573		// CertificateRequestInfo supported signature algorithms.
574		c.sendAlert(alertHandshakeFailure)
575		return errors.New("tls: server doesn't support selected certificate")
576	}
577
578	sigType := signatureFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
579	sigHash, err := hashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
580	if sigType == 0 || err != nil {
581		return c.sendAlert(alertInternalError)
582	}
583	h := sigHash.New()
584	writeSignedMessage(h, clientSignatureContext, hs.transcript)
585
586	signOpts := crypto.SignerOpts(sigHash)
587	if sigType == signatureRSAPSS {
588		signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
589	}
590	sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts)
591	if err != nil {
592		c.sendAlert(alertInternalError)
593		return errors.New("tls: failed to sign handshake: " + err.Error())
594	}
595	certVerifyMsg.signature = sig
596
597	hs.transcript.Write(certVerifyMsg.marshal())
598	if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
599		return err
600	}
601
602	return nil
603}
604
605func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
606	c := hs.c
607
608	finished := &finishedMsg{
609		verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
610	}
611
612	hs.transcript.Write(finished.marshal())
613	if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
614		return err
615	}
616
617	c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
618
619	if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
620		c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
621			resumptionLabel, hs.transcript)
622	}
623
624	return nil
625}
626
627func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
628	if !c.isClient {
629		c.sendAlert(alertUnexpectedMessage)
630		return errors.New("tls: received new session ticket from a client")
631	}
632
633	if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
634		return nil
635	}
636
637	// See RFC 8446, Section 4.6.1.
638	if msg.lifetime == 0 {
639		return nil
640	}
641	lifetime := time.Duration(msg.lifetime) * time.Second
642	if lifetime > maxSessionTicketLifetime {
643		c.sendAlert(alertIllegalParameter)
644		return errors.New("tls: received a session ticket with invalid lifetime")
645	}
646
647	cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
648	if cipherSuite == nil || c.resumptionSecret == nil {
649		return c.sendAlert(alertInternalError)
650	}
651
652	// Save the resumption_master_secret and nonce instead of deriving the PSK
653	// to do the least amount of work on NewSessionTicket messages before we
654	// know if the ticket will be used. Forward secrecy of resumed connections
655	// is guaranteed by the requirement for pskModeDHE.
656	session := &ClientSessionState{
657		sessionTicket:      msg.label,
658		vers:               c.vers,
659		cipherSuite:        c.cipherSuite,
660		masterSecret:       c.resumptionSecret,
661		serverCertificates: c.peerCertificates,
662		verifiedChains:     c.verifiedChains,
663		receivedAt:         c.config.time(),
664		nonce:              msg.nonce,
665		useBy:              c.config.time().Add(lifetime),
666		ageAdd:             msg.ageAdd,
667	}
668
669	cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
670	c.config.ClientSessionCache.Put(cacheKey, session)
671
672	return nil
673}
674