Alternative TLS implementation in Go
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

630 рядки
18 KiB

  1. // Copyright 2010 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. package tls
  5. import (
  6. "bytes"
  7. "crypto/ecdsa"
  8. "crypto/rsa"
  9. "crypto/x509"
  10. "encoding/base64"
  11. "encoding/binary"
  12. "encoding/pem"
  13. "fmt"
  14. "io"
  15. "net"
  16. "os"
  17. "os/exec"
  18. "path/filepath"
  19. "strconv"
  20. "testing"
  21. "time"
  22. )
  23. // Note: see comment in handshake_test.go for details of how the reference
  24. // tests work.
  25. // blockingSource is an io.Reader that blocks a Read call until it's closed.
  26. type blockingSource chan bool
  27. func (b blockingSource) Read([]byte) (n int, err error) {
  28. <-b
  29. return 0, io.EOF
  30. }
  31. // clientTest represents a test of the TLS client handshake against a reference
  32. // implementation.
  33. type clientTest struct {
  34. // name is a freeform string identifying the test and the file in which
  35. // the expected results will be stored.
  36. name string
  37. // command, if not empty, contains a series of arguments for the
  38. // command to run for the reference server.
  39. command []string
  40. // config, if not nil, contains a custom Config to use for this test.
  41. config *Config
  42. // cert, if not empty, contains a DER-encoded certificate for the
  43. // reference server.
  44. cert []byte
  45. // key, if not nil, contains either a *rsa.PrivateKey or
  46. // *ecdsa.PrivateKey which is the private key for the reference server.
  47. key interface{}
  48. // extensions, if not nil, contains a list of extension data to be returned
  49. // from the ServerHello. The data should be in standard TLS format with
  50. // a 2-byte uint16 type, 2-byte data length, followed by the extension data.
  51. extensions [][]byte
  52. // validate, if not nil, is a function that will be called with the
  53. // ConnectionState of the resulting connection. It returns a non-nil
  54. // error if the ConnectionState is unacceptable.
  55. validate func(ConnectionState) error
  56. }
  57. var defaultServerCommand = []string{"openssl", "s_server"}
  58. // connFromCommand starts the reference server process, connects to it and
  59. // returns a recordingConn for the connection. The stdin return value is a
  60. // blockingSource for the stdin of the child process. It must be closed before
  61. // Waiting for child.
  62. func (test *clientTest) connFromCommand() (conn *recordingConn, child *exec.Cmd, stdin blockingSource, err error) {
  63. cert := testRSACertificate
  64. if len(test.cert) > 0 {
  65. cert = test.cert
  66. }
  67. certPath := tempFile(string(cert))
  68. defer os.Remove(certPath)
  69. var key interface{} = testRSAPrivateKey
  70. if test.key != nil {
  71. key = test.key
  72. }
  73. var pemType string
  74. var derBytes []byte
  75. switch key := key.(type) {
  76. case *rsa.PrivateKey:
  77. pemType = "RSA"
  78. derBytes = x509.MarshalPKCS1PrivateKey(key)
  79. case *ecdsa.PrivateKey:
  80. pemType = "EC"
  81. var err error
  82. derBytes, err = x509.MarshalECPrivateKey(key)
  83. if err != nil {
  84. panic(err)
  85. }
  86. default:
  87. panic("unknown key type")
  88. }
  89. var pemOut bytes.Buffer
  90. pem.Encode(&pemOut, &pem.Block{Type: pemType + " PRIVATE KEY", Bytes: derBytes})
  91. keyPath := tempFile(string(pemOut.Bytes()))
  92. defer os.Remove(keyPath)
  93. var command []string
  94. if len(test.command) > 0 {
  95. command = append(command, test.command...)
  96. } else {
  97. command = append(command, defaultServerCommand...)
  98. }
  99. command = append(command, "-cert", certPath, "-certform", "DER", "-key", keyPath)
  100. // serverPort contains the port that OpenSSL will listen on. OpenSSL
  101. // can't take "0" as an argument here so we have to pick a number and
  102. // hope that it's not in use on the machine. Since this only occurs
  103. // when -update is given and thus when there's a human watching the
  104. // test, this isn't too bad.
  105. const serverPort = 24323
  106. command = append(command, "-accept", strconv.Itoa(serverPort))
  107. if len(test.extensions) > 0 {
  108. var serverInfo bytes.Buffer
  109. for _, ext := range test.extensions {
  110. pem.Encode(&serverInfo, &pem.Block{
  111. Type: fmt.Sprintf("SERVERINFO FOR EXTENSION %d", binary.BigEndian.Uint16(ext)),
  112. Bytes: ext,
  113. })
  114. }
  115. serverInfoPath := tempFile(serverInfo.String())
  116. defer os.Remove(serverInfoPath)
  117. command = append(command, "-serverinfo", serverInfoPath)
  118. }
  119. cmd := exec.Command(command[0], command[1:]...)
  120. stdin = blockingSource(make(chan bool))
  121. cmd.Stdin = stdin
  122. var out bytes.Buffer
  123. cmd.Stdout = &out
  124. cmd.Stderr = &out
  125. if err := cmd.Start(); err != nil {
  126. return nil, nil, nil, err
  127. }
  128. // OpenSSL does print an "ACCEPT" banner, but it does so *before*
  129. // opening the listening socket, so we can't use that to wait until it
  130. // has started listening. Thus we are forced to poll until we get a
  131. // connection.
  132. var tcpConn net.Conn
  133. for i := uint(0); i < 5; i++ {
  134. tcpConn, err = net.DialTCP("tcp", nil, &net.TCPAddr{
  135. IP: net.IPv4(127, 0, 0, 1),
  136. Port: serverPort,
  137. })
  138. if err == nil {
  139. break
  140. }
  141. time.Sleep((1 << i) * 5 * time.Millisecond)
  142. }
  143. if err != nil {
  144. close(stdin)
  145. out.WriteTo(os.Stdout)
  146. cmd.Process.Kill()
  147. return nil, nil, nil, cmd.Wait()
  148. }
  149. record := &recordingConn{
  150. Conn: tcpConn,
  151. }
  152. return record, cmd, stdin, nil
  153. }
  154. func (test *clientTest) dataPath() string {
  155. return filepath.Join("testdata", "Client-"+test.name)
  156. }
  157. func (test *clientTest) loadData() (flows [][]byte, err error) {
  158. in, err := os.Open(test.dataPath())
  159. if err != nil {
  160. return nil, err
  161. }
  162. defer in.Close()
  163. return parseTestData(in)
  164. }
  165. func (test *clientTest) run(t *testing.T, write bool) {
  166. var clientConn, serverConn net.Conn
  167. var recordingConn *recordingConn
  168. var childProcess *exec.Cmd
  169. var stdin blockingSource
  170. if write {
  171. var err error
  172. recordingConn, childProcess, stdin, err = test.connFromCommand()
  173. if err != nil {
  174. t.Fatalf("Failed to start subcommand: %s", err)
  175. }
  176. clientConn = recordingConn
  177. } else {
  178. clientConn, serverConn = net.Pipe()
  179. }
  180. config := test.config
  181. if config == nil {
  182. config = testConfig
  183. }
  184. client := Client(clientConn, config)
  185. doneChan := make(chan bool)
  186. go func() {
  187. if _, err := client.Write([]byte("hello\n")); err != nil {
  188. t.Errorf("Client.Write failed: %s", err)
  189. }
  190. if test.validate != nil {
  191. if err := test.validate(client.ConnectionState()); err != nil {
  192. t.Errorf("validate callback returned error: %s", err)
  193. }
  194. }
  195. client.Close()
  196. clientConn.Close()
  197. doneChan <- true
  198. }()
  199. if !write {
  200. flows, err := test.loadData()
  201. if err != nil {
  202. t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err)
  203. }
  204. for i, b := range flows {
  205. if i%2 == 1 {
  206. serverConn.Write(b)
  207. continue
  208. }
  209. bb := make([]byte, len(b))
  210. _, err := io.ReadFull(serverConn, bb)
  211. if err != nil {
  212. t.Fatalf("%s #%d: %s", test.name, i, err)
  213. }
  214. if !bytes.Equal(b, bb) {
  215. t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i, bb, b)
  216. }
  217. }
  218. serverConn.Close()
  219. }
  220. <-doneChan
  221. if write {
  222. path := test.dataPath()
  223. out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
  224. if err != nil {
  225. t.Fatalf("Failed to create output file: %s", err)
  226. }
  227. defer out.Close()
  228. recordingConn.Close()
  229. close(stdin)
  230. childProcess.Process.Kill()
  231. childProcess.Wait()
  232. if len(recordingConn.flows) < 3 {
  233. childProcess.Stdout.(*bytes.Buffer).WriteTo(os.Stdout)
  234. t.Fatalf("Client connection didn't work")
  235. }
  236. recordingConn.WriteTo(out)
  237. fmt.Printf("Wrote %s\n", path)
  238. }
  239. }
  240. func runClientTestForVersion(t *testing.T, template *clientTest, prefix, option string) {
  241. test := *template
  242. test.name = prefix + test.name
  243. if len(test.command) == 0 {
  244. test.command = defaultClientCommand
  245. }
  246. test.command = append([]string(nil), test.command...)
  247. test.command = append(test.command, option)
  248. test.run(t, *update)
  249. }
  250. func runClientTestTLS10(t *testing.T, template *clientTest) {
  251. runClientTestForVersion(t, template, "TLSv10-", "-tls1")
  252. }
  253. func runClientTestTLS11(t *testing.T, template *clientTest) {
  254. runClientTestForVersion(t, template, "TLSv11-", "-tls1_1")
  255. }
  256. func runClientTestTLS12(t *testing.T, template *clientTest) {
  257. runClientTestForVersion(t, template, "TLSv12-", "-tls1_2")
  258. }
  259. func TestHandshakeClientRSARC4(t *testing.T) {
  260. test := &clientTest{
  261. name: "RSA-RC4",
  262. command: []string{"openssl", "s_server", "-cipher", "RC4-SHA"},
  263. }
  264. runClientTestTLS10(t, test)
  265. runClientTestTLS11(t, test)
  266. runClientTestTLS12(t, test)
  267. }
  268. func TestHandshakeClientECDHERSAAES(t *testing.T) {
  269. test := &clientTest{
  270. name: "ECDHE-RSA-AES",
  271. command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES128-SHA"},
  272. }
  273. runClientTestTLS10(t, test)
  274. runClientTestTLS11(t, test)
  275. runClientTestTLS12(t, test)
  276. }
  277. func TestHandshakeClientECDHEECDSAAES(t *testing.T) {
  278. test := &clientTest{
  279. name: "ECDHE-ECDSA-AES",
  280. command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA"},
  281. cert: testECDSACertificate,
  282. key: testECDSAPrivateKey,
  283. }
  284. runClientTestTLS10(t, test)
  285. runClientTestTLS11(t, test)
  286. runClientTestTLS12(t, test)
  287. }
  288. func TestHandshakeClientECDHEECDSAAESGCM(t *testing.T) {
  289. test := &clientTest{
  290. name: "ECDHE-ECDSA-AES-GCM",
  291. command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-GCM-SHA256"},
  292. cert: testECDSACertificate,
  293. key: testECDSAPrivateKey,
  294. }
  295. runClientTestTLS12(t, test)
  296. }
  297. func TestHandshakeClientAES256GCMSHA384(t *testing.T) {
  298. test := &clientTest{
  299. name: "ECDHE-ECDSA-AES256-GCM-SHA384",
  300. command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES256-GCM-SHA384"},
  301. cert: testECDSACertificate,
  302. key: testECDSAPrivateKey,
  303. }
  304. runClientTestTLS12(t, test)
  305. }
  306. func TestHandshakeClientCertRSA(t *testing.T) {
  307. config := *testConfig
  308. cert, _ := X509KeyPair([]byte(clientCertificatePEM), []byte(clientKeyPEM))
  309. config.Certificates = []Certificate{cert}
  310. test := &clientTest{
  311. name: "ClientCert-RSA-RSA",
  312. command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
  313. config: &config,
  314. }
  315. runClientTestTLS10(t, test)
  316. runClientTestTLS12(t, test)
  317. test = &clientTest{
  318. name: "ClientCert-RSA-ECDSA",
  319. command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
  320. config: &config,
  321. cert: testECDSACertificate,
  322. key: testECDSAPrivateKey,
  323. }
  324. runClientTestTLS10(t, test)
  325. runClientTestTLS12(t, test)
  326. test = &clientTest{
  327. name: "ClientCert-RSA-AES256-GCM-SHA384",
  328. command: []string{"openssl", "s_server", "-cipher", "ECDHE-RSA-AES256-GCM-SHA384", "-verify", "1"},
  329. config: &config,
  330. cert: testRSACertificate,
  331. key: testRSAPrivateKey,
  332. }
  333. runClientTestTLS12(t, test)
  334. }
  335. func TestHandshakeClientCertECDSA(t *testing.T) {
  336. config := *testConfig
  337. cert, _ := X509KeyPair([]byte(clientECDSACertificatePEM), []byte(clientECDSAKeyPEM))
  338. config.Certificates = []Certificate{cert}
  339. test := &clientTest{
  340. name: "ClientCert-ECDSA-RSA",
  341. command: []string{"openssl", "s_server", "-cipher", "RC4-SHA", "-verify", "1"},
  342. config: &config,
  343. }
  344. runClientTestTLS10(t, test)
  345. runClientTestTLS12(t, test)
  346. test = &clientTest{
  347. name: "ClientCert-ECDSA-ECDSA",
  348. command: []string{"openssl", "s_server", "-cipher", "ECDHE-ECDSA-AES128-SHA", "-verify", "1"},
  349. config: &config,
  350. cert: testECDSACertificate,
  351. key: testECDSAPrivateKey,
  352. }
  353. runClientTestTLS10(t, test)
  354. runClientTestTLS12(t, test)
  355. }
  356. func TestClientResumption(t *testing.T) {
  357. serverConfig := &Config{
  358. CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
  359. Certificates: testConfig.Certificates,
  360. }
  361. issuer, err := x509.ParseCertificate(testRSACertificateIssuer)
  362. if err != nil {
  363. panic(err)
  364. }
  365. rootCAs := x509.NewCertPool()
  366. rootCAs.AddCert(issuer)
  367. clientConfig := &Config{
  368. CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
  369. ClientSessionCache: NewLRUClientSessionCache(32),
  370. RootCAs: rootCAs,
  371. ServerName: "example.golang",
  372. }
  373. testResumeState := func(test string, didResume bool) {
  374. _, hs, err := testHandshake(clientConfig, serverConfig)
  375. if err != nil {
  376. t.Fatalf("%s: handshake failed: %s", test, err)
  377. }
  378. if hs.DidResume != didResume {
  379. t.Fatalf("%s resumed: %v, expected: %v", test, hs.DidResume, didResume)
  380. }
  381. if didResume && (hs.PeerCertificates == nil || hs.VerifiedChains == nil) {
  382. t.Fatalf("expected non-nil certificates after resumption. Got peerCertificates: %#v, verifedCertificates: %#v", hs.PeerCertificates, hs.VerifiedChains)
  383. }
  384. }
  385. getTicket := func() []byte {
  386. return clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.sessionTicket
  387. }
  388. randomKey := func() [32]byte {
  389. var k [32]byte
  390. if _, err := io.ReadFull(serverConfig.rand(), k[:]); err != nil {
  391. t.Fatalf("Failed to read new SessionTicketKey: %s", err)
  392. }
  393. return k
  394. }
  395. testResumeState("Handshake", false)
  396. ticket := getTicket()
  397. testResumeState("Resume", true)
  398. if !bytes.Equal(ticket, getTicket()) {
  399. t.Fatal("first ticket doesn't match ticket after resumption")
  400. }
  401. key2 := randomKey()
  402. serverConfig.SetSessionTicketKeys([][32]byte{key2})
  403. testResumeState("InvalidSessionTicketKey", false)
  404. testResumeState("ResumeAfterInvalidSessionTicketKey", true)
  405. serverConfig.SetSessionTicketKeys([][32]byte{randomKey(), key2})
  406. ticket = getTicket()
  407. testResumeState("KeyChange", true)
  408. if bytes.Equal(ticket, getTicket()) {
  409. t.Fatal("new ticket wasn't included while resuming")
  410. }
  411. testResumeState("KeyChangeFinish", true)
  412. clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
  413. testResumeState("DifferentCipherSuite", false)
  414. testResumeState("DifferentCipherSuiteRecovers", true)
  415. clientConfig.ClientSessionCache = nil
  416. testResumeState("WithoutSessionCache", false)
  417. }
  418. func TestLRUClientSessionCache(t *testing.T) {
  419. // Initialize cache of capacity 4.
  420. cache := NewLRUClientSessionCache(4)
  421. cs := make([]ClientSessionState, 6)
  422. keys := []string{"0", "1", "2", "3", "4", "5", "6"}
  423. // Add 4 entries to the cache and look them up.
  424. for i := 0; i < 4; i++ {
  425. cache.Put(keys[i], &cs[i])
  426. }
  427. for i := 0; i < 4; i++ {
  428. if s, ok := cache.Get(keys[i]); !ok || s != &cs[i] {
  429. t.Fatalf("session cache failed lookup for added key: %s", keys[i])
  430. }
  431. }
  432. // Add 2 more entries to the cache. First 2 should be evicted.
  433. for i := 4; i < 6; i++ {
  434. cache.Put(keys[i], &cs[i])
  435. }
  436. for i := 0; i < 2; i++ {
  437. if s, ok := cache.Get(keys[i]); ok || s != nil {
  438. t.Fatalf("session cache should have evicted key: %s", keys[i])
  439. }
  440. }
  441. // Touch entry 2. LRU should evict 3 next.
  442. cache.Get(keys[2])
  443. cache.Put(keys[0], &cs[0])
  444. if s, ok := cache.Get(keys[3]); ok || s != nil {
  445. t.Fatalf("session cache should have evicted key 3")
  446. }
  447. // Update entry 0 in place.
  448. cache.Put(keys[0], &cs[3])
  449. if s, ok := cache.Get(keys[0]); !ok || s != &cs[3] {
  450. t.Fatalf("session cache failed update for key 0")
  451. }
  452. // Adding a nil entry is valid.
  453. cache.Put(keys[0], nil)
  454. if s, ok := cache.Get(keys[0]); !ok || s != nil {
  455. t.Fatalf("failed to add nil entry to cache")
  456. }
  457. }
  458. func TestHandshakeClientALPNMatch(t *testing.T) {
  459. config := *testConfig
  460. config.NextProtos = []string{"proto2", "proto1"}
  461. test := &clientTest{
  462. name: "ALPN",
  463. // Note that this needs OpenSSL 1.0.2 because that is the first
  464. // version that supports the -alpn flag.
  465. command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"},
  466. config: &config,
  467. validate: func(state ConnectionState) error {
  468. // The server's preferences should override the client.
  469. if state.NegotiatedProtocol != "proto1" {
  470. return fmt.Errorf("Got protocol %q, wanted proto1", state.NegotiatedProtocol)
  471. }
  472. return nil
  473. },
  474. }
  475. runClientTestTLS12(t, test)
  476. }
  477. func TestHandshakeClientALPNNoMatch(t *testing.T) {
  478. config := *testConfig
  479. config.NextProtos = []string{"proto3"}
  480. test := &clientTest{
  481. name: "ALPN-NoMatch",
  482. // Note that this needs OpenSSL 1.0.2 because that is the first
  483. // version that supports the -alpn flag.
  484. command: []string{"openssl", "s_server", "-alpn", "proto1,proto2"},
  485. config: &config,
  486. validate: func(state ConnectionState) error {
  487. // There's no overlap so OpenSSL will not select a protocol.
  488. if state.NegotiatedProtocol != "" {
  489. return fmt.Errorf("Got protocol %q, wanted ''", state.NegotiatedProtocol)
  490. }
  491. return nil
  492. },
  493. }
  494. runClientTestTLS12(t, test)
  495. }
  496. // sctsBase64 contains data from `openssl s_client -serverinfo 18 -connect ritter.vg:443`
  497. const sctsBase64 = "ABIBaQFnAHUApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFHl5nuFgAABAMARjBEAiAcS4JdlW5nW9sElUv2zvQyPoZ6ejKrGGB03gjaBZFMLwIgc1Qbbn+hsH0RvObzhS+XZhr3iuQQJY8S9G85D9KeGPAAdgBo9pj4H2SCvjqM7rkoHUz8cVFdZ5PURNEKZ6y7T0/7xAAAAUeX4bVwAAAEAwBHMEUCIDIhFDgG2HIuADBkGuLobU5a4dlCHoJLliWJ1SYT05z6AiEAjxIoZFFPRNWMGGIjskOTMwXzQ1Wh2e7NxXE1kd1J0QsAdgDuS723dc5guuFCaR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAUhcZIqHAAAEAwBHMEUCICmJ1rBT09LpkbzxtUC+Hi7nXLR0J+2PmwLp+sJMuqK+AiEAr0NkUnEVKVhAkccIFpYDqHOlZaBsuEhWWrYpg2RtKp0="
  498. func TestHandshakClientSCTs(t *testing.T) {
  499. config := *testConfig
  500. scts, err := base64.StdEncoding.DecodeString(sctsBase64)
  501. if err != nil {
  502. t.Fatal(err)
  503. }
  504. test := &clientTest{
  505. name: "SCT",
  506. // Note that this needs OpenSSL 1.0.2 because that is the first
  507. // version that supports the -serverinfo flag.
  508. command: []string{"openssl", "s_server"},
  509. config: &config,
  510. extensions: [][]byte{scts},
  511. validate: func(state ConnectionState) error {
  512. expectedSCTs := [][]byte{
  513. scts[8:125],
  514. scts[127:245],
  515. scts[247:],
  516. }
  517. if n := len(state.SignedCertificateTimestamps); n != len(expectedSCTs) {
  518. return fmt.Errorf("Got %d scts, wanted %d", n, len(expectedSCTs))
  519. }
  520. for i, expected := range expectedSCTs {
  521. if sct := state.SignedCertificateTimestamps[i]; !bytes.Equal(sct, expected) {
  522. return fmt.Errorf("SCT #%d contained %x, expected %x", i, sct, expected)
  523. }
  524. }
  525. return nil
  526. },
  527. }
  528. runClientTestTLS12(t, test)
  529. }
  530. func TestNoIPAddressesInSNI(t *testing.T) {
  531. for _, ipLiteral := range []string{"1.2.3.4", "::1"} {
  532. c, s := net.Pipe()
  533. go func() {
  534. client := Client(c, &Config{ServerName: ipLiteral})
  535. client.Handshake()
  536. }()
  537. var header [5]byte
  538. if _, err := io.ReadFull(s, header[:]); err != nil {
  539. t.Fatal(err)
  540. }
  541. recordLen := int(header[3])<<8 | int(header[4])
  542. record := make([]byte, recordLen)
  543. if _, err := io.ReadFull(s, record[:]); err != nil {
  544. t.Fatal(err)
  545. }
  546. s.Close()
  547. if bytes.Index(record, []byte(ipLiteral)) != -1 {
  548. t.Errorf("IP literal %q found in ClientHello: %x", ipLiteral, record)
  549. }
  550. }
  551. }