u_public.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  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. package tls
  5. import (
  6. "crypto"
  7. "crypto/x509"
  8. "hash"
  9. )
  10. // ClientHandshakeState includes both TLS 1.3-only and TLS 1.2-only states,
  11. // only one of them will be used, depending on negotiated version.
  12. //
  13. // ClientHandshakeState will be converted into and from either
  14. // - clientHandshakeState (TLS 1.2)
  15. // - clientHandshakeStateTLS13 (TLS 1.3)
  16. // uTLS will call .handshake() on one of these private internal states,
  17. // to perform TLS handshake using standard crypto/tls implementation.
  18. type ClientHandshakeState struct {
  19. C *Conn
  20. ServerHello *ServerHelloMsg
  21. Hello *ClientHelloMsg
  22. MasterSecret []byte
  23. Session *ClientSessionState
  24. State12 TLS12OnlyState
  25. State13 TLS13OnlyState
  26. uconn *UConn
  27. }
  28. // TLS 1.3 only
  29. type TLS13OnlyState struct {
  30. Suite *CipherSuiteTLS13
  31. EcdheParams EcdheParameters
  32. EarlySecret []byte
  33. BinderKey []byte
  34. CertReq *CertificateRequestMsgTLS13
  35. UsingPSK bool
  36. SentDummyCCS bool
  37. Transcript hash.Hash
  38. TrafficSecret []byte // client_application_traffic_secret_0
  39. }
  40. // TLS 1.2 and before only
  41. type TLS12OnlyState struct {
  42. FinishedHash FinishedHash
  43. Suite CipherSuite
  44. }
  45. func (chs *ClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 {
  46. if chs == nil {
  47. return nil
  48. } else {
  49. return &clientHandshakeStateTLS13{
  50. c: chs.C,
  51. serverHello: chs.ServerHello.getPrivatePtr(),
  52. hello: chs.Hello.getPrivatePtr(),
  53. ecdheParams: chs.State13.EcdheParams,
  54. session: chs.Session,
  55. earlySecret: chs.State13.EarlySecret,
  56. binderKey: chs.State13.BinderKey,
  57. certReq: chs.State13.CertReq.toPrivate(),
  58. usingPSK: chs.State13.UsingPSK,
  59. sentDummyCCS: chs.State13.SentDummyCCS,
  60. suite: chs.State13.Suite.toPrivate(),
  61. transcript: chs.State13.Transcript,
  62. masterSecret: chs.MasterSecret,
  63. trafficSecret: chs.State13.TrafficSecret,
  64. uconn: chs.uconn,
  65. }
  66. }
  67. }
  68. func (chs13 *clientHandshakeStateTLS13) toPublic13() *ClientHandshakeState {
  69. if chs13 == nil {
  70. return nil
  71. } else {
  72. tls13State := TLS13OnlyState{
  73. EcdheParams: chs13.ecdheParams,
  74. EarlySecret: chs13.earlySecret,
  75. BinderKey: chs13.binderKey,
  76. CertReq: chs13.certReq.toPublic(),
  77. UsingPSK: chs13.usingPSK,
  78. SentDummyCCS: chs13.sentDummyCCS,
  79. Suite: chs13.suite.toPublic(),
  80. TrafficSecret: chs13.trafficSecret,
  81. Transcript: chs13.transcript,
  82. }
  83. return &ClientHandshakeState{
  84. C: chs13.c,
  85. ServerHello: chs13.serverHello.getPublicPtr(),
  86. Hello: chs13.hello.getPublicPtr(),
  87. Session: chs13.session,
  88. MasterSecret: chs13.masterSecret,
  89. State13: tls13State,
  90. uconn: chs13.uconn,
  91. }
  92. }
  93. }
  94. func (chs *ClientHandshakeState) toPrivate12() *clientHandshakeState {
  95. if chs == nil {
  96. return nil
  97. } else {
  98. return &clientHandshakeState{
  99. c: chs.C,
  100. serverHello: chs.ServerHello.getPrivatePtr(),
  101. hello: chs.Hello.getPrivatePtr(),
  102. suite: chs.State12.Suite.getPrivatePtr(),
  103. session: chs.Session,
  104. masterSecret: chs.MasterSecret,
  105. finishedHash: chs.State12.FinishedHash.getPrivateObj(),
  106. uconn: chs.uconn,
  107. }
  108. }
  109. }
  110. func (chs12 *clientHandshakeState) toPublic12() *ClientHandshakeState {
  111. if chs12 == nil {
  112. return nil
  113. } else {
  114. tls12State := TLS12OnlyState{
  115. Suite: chs12.suite.getPublicObj(),
  116. FinishedHash: chs12.finishedHash.getPublicObj(),
  117. }
  118. return &ClientHandshakeState{
  119. C: chs12.c,
  120. ServerHello: chs12.serverHello.getPublicPtr(),
  121. Hello: chs12.hello.getPublicPtr(),
  122. Session: chs12.session,
  123. MasterSecret: chs12.masterSecret,
  124. State12: tls12State,
  125. uconn: chs12.uconn,
  126. }
  127. }
  128. }
  129. type EcdheParameters interface {
  130. ecdheParameters
  131. }
  132. type CertificateRequestMsgTLS13 struct {
  133. Raw []byte
  134. OcspStapling bool
  135. Scts bool
  136. SupportedSignatureAlgorithms []SignatureScheme
  137. SupportedSignatureAlgorithmsCert []SignatureScheme
  138. CertificateAuthorities [][]byte
  139. }
  140. func (crm *certificateRequestMsgTLS13) toPublic() *CertificateRequestMsgTLS13 {
  141. if crm == nil {
  142. return nil
  143. } else {
  144. return &CertificateRequestMsgTLS13{
  145. Raw: crm.raw,
  146. OcspStapling: crm.ocspStapling,
  147. Scts: crm.scts,
  148. SupportedSignatureAlgorithms: crm.supportedSignatureAlgorithms,
  149. SupportedSignatureAlgorithmsCert: crm.supportedSignatureAlgorithmsCert,
  150. CertificateAuthorities: crm.certificateAuthorities,
  151. }
  152. }
  153. }
  154. func (crm *CertificateRequestMsgTLS13) toPrivate() *certificateRequestMsgTLS13 {
  155. if crm == nil {
  156. return nil
  157. } else {
  158. return &certificateRequestMsgTLS13{
  159. raw: crm.Raw,
  160. ocspStapling: crm.OcspStapling,
  161. scts: crm.Scts,
  162. supportedSignatureAlgorithms: crm.SupportedSignatureAlgorithms,
  163. supportedSignatureAlgorithmsCert: crm.SupportedSignatureAlgorithmsCert,
  164. certificateAuthorities: crm.CertificateAuthorities,
  165. }
  166. }
  167. }
  168. type CipherSuiteTLS13 struct {
  169. Id uint16
  170. KeyLen int
  171. Aead func(key, fixedNonce []byte) aead
  172. Hash crypto.Hash
  173. }
  174. func (c *cipherSuiteTLS13) toPublic() *CipherSuiteTLS13 {
  175. if c == nil {
  176. return nil
  177. } else {
  178. return &CipherSuiteTLS13{
  179. Id: c.id,
  180. KeyLen: c.keyLen,
  181. Aead: c.aead,
  182. Hash: c.hash,
  183. }
  184. }
  185. }
  186. func (c *CipherSuiteTLS13) toPrivate() *cipherSuiteTLS13 {
  187. if c == nil {
  188. return nil
  189. } else {
  190. return &cipherSuiteTLS13{
  191. id: c.Id,
  192. keyLen: c.KeyLen,
  193. aead: c.Aead,
  194. hash: c.Hash,
  195. }
  196. }
  197. }
  198. type ServerHelloMsg struct {
  199. Raw []byte
  200. Vers uint16
  201. Random []byte
  202. SessionId []byte
  203. CipherSuite uint16
  204. CompressionMethod uint8
  205. NextProtoNeg bool
  206. NextProtos []string
  207. OcspStapling bool
  208. Scts [][]byte
  209. Ems bool
  210. TicketSupported bool
  211. SecureRenegotiation []byte
  212. SecureRenegotiationSupported bool
  213. AlpnProtocol string
  214. // 1.3
  215. SupportedVersion uint16
  216. ServerShare keyShare
  217. SelectedIdentityPresent bool
  218. SelectedIdentity uint16
  219. Cookie []byte // HelloRetryRequest extension
  220. SelectedGroup CurveID // HelloRetryRequest extension
  221. }
  222. func (shm *ServerHelloMsg) getPrivatePtr() *serverHelloMsg {
  223. if shm == nil {
  224. return nil
  225. } else {
  226. return &serverHelloMsg{
  227. raw: shm.Raw,
  228. vers: shm.Vers,
  229. random: shm.Random,
  230. sessionId: shm.SessionId,
  231. cipherSuite: shm.CipherSuite,
  232. compressionMethod: shm.CompressionMethod,
  233. nextProtoNeg: shm.NextProtoNeg,
  234. nextProtos: shm.NextProtos,
  235. ocspStapling: shm.OcspStapling,
  236. scts: shm.Scts,
  237. ems: shm.Ems,
  238. ticketSupported: shm.TicketSupported,
  239. secureRenegotiation: shm.SecureRenegotiation,
  240. secureRenegotiationSupported: shm.SecureRenegotiationSupported,
  241. alpnProtocol: shm.AlpnProtocol,
  242. supportedVersion: shm.SupportedVersion,
  243. serverShare: shm.ServerShare,
  244. selectedIdentityPresent: shm.SelectedIdentityPresent,
  245. selectedIdentity: shm.SelectedIdentity,
  246. cookie: shm.Cookie,
  247. selectedGroup: shm.SelectedGroup,
  248. }
  249. }
  250. }
  251. func (shm *serverHelloMsg) getPublicPtr() *ServerHelloMsg {
  252. if shm == nil {
  253. return nil
  254. } else {
  255. return &ServerHelloMsg{
  256. Raw: shm.raw,
  257. Vers: shm.vers,
  258. Random: shm.random,
  259. SessionId: shm.sessionId,
  260. CipherSuite: shm.cipherSuite,
  261. CompressionMethod: shm.compressionMethod,
  262. NextProtoNeg: shm.nextProtoNeg,
  263. NextProtos: shm.nextProtos,
  264. OcspStapling: shm.ocspStapling,
  265. Scts: shm.scts,
  266. Ems: shm.ems,
  267. TicketSupported: shm.ticketSupported,
  268. SecureRenegotiation: shm.secureRenegotiation,
  269. SecureRenegotiationSupported: shm.secureRenegotiationSupported,
  270. AlpnProtocol: shm.alpnProtocol,
  271. SupportedVersion: shm.supportedVersion,
  272. ServerShare: shm.serverShare,
  273. SelectedIdentityPresent: shm.selectedIdentityPresent,
  274. SelectedIdentity: shm.selectedIdentity,
  275. Cookie: shm.cookie,
  276. SelectedGroup: shm.selectedGroup,
  277. }
  278. }
  279. }
  280. type ClientHelloMsg struct {
  281. Raw []byte
  282. Vers uint16
  283. Random []byte
  284. SessionId []byte
  285. CipherSuites []uint16
  286. CompressionMethods []uint8
  287. NextProtoNeg bool
  288. ServerName string
  289. OcspStapling bool
  290. Scts bool
  291. Ems bool // [UTLS] actually implemented due to its prevalence
  292. SupportedCurves []CurveID
  293. SupportedPoints []uint8
  294. TicketSupported bool
  295. SessionTicket []uint8
  296. SupportedSignatureAlgorithms []SignatureScheme
  297. SecureRenegotiation []byte
  298. SecureRenegotiationSupported bool
  299. AlpnProtocols []string
  300. // 1.3
  301. SupportedSignatureAlgorithmsCert []SignatureScheme
  302. SupportedVersions []uint16
  303. Cookie []byte
  304. KeyShares []KeyShare
  305. EarlyData bool
  306. PskModes []uint8
  307. PskIdentities []pskIdentity
  308. PskBinders [][]byte
  309. }
  310. func (chm *ClientHelloMsg) getPrivatePtr() *clientHelloMsg {
  311. if chm == nil {
  312. return nil
  313. } else {
  314. return &clientHelloMsg{
  315. raw: chm.Raw,
  316. vers: chm.Vers,
  317. random: chm.Random,
  318. sessionId: chm.SessionId,
  319. cipherSuites: chm.CipherSuites,
  320. compressionMethods: chm.CompressionMethods,
  321. nextProtoNeg: chm.NextProtoNeg,
  322. serverName: chm.ServerName,
  323. ocspStapling: chm.OcspStapling,
  324. scts: chm.Scts,
  325. ems: chm.Ems,
  326. supportedCurves: chm.SupportedCurves,
  327. supportedPoints: chm.SupportedPoints,
  328. ticketSupported: chm.TicketSupported,
  329. sessionTicket: chm.SessionTicket,
  330. supportedSignatureAlgorithms: chm.SupportedSignatureAlgorithms,
  331. secureRenegotiation: chm.SecureRenegotiation,
  332. secureRenegotiationSupported: chm.SecureRenegotiationSupported,
  333. alpnProtocols: chm.AlpnProtocols,
  334. supportedSignatureAlgorithmsCert: chm.SupportedSignatureAlgorithmsCert,
  335. supportedVersions: chm.SupportedVersions,
  336. cookie: chm.Cookie,
  337. keyShares: KeyShares(chm.KeyShares).ToPrivate(),
  338. earlyData: chm.EarlyData,
  339. pskModes: chm.PskModes,
  340. pskIdentities: chm.PskIdentities,
  341. pskBinders: chm.PskBinders,
  342. }
  343. }
  344. }
  345. func (chm *clientHelloMsg) getPublicPtr() *ClientHelloMsg {
  346. if chm == nil {
  347. return nil
  348. } else {
  349. return &ClientHelloMsg{
  350. Raw: chm.raw,
  351. Vers: chm.vers,
  352. Random: chm.random,
  353. SessionId: chm.sessionId,
  354. CipherSuites: chm.cipherSuites,
  355. CompressionMethods: chm.compressionMethods,
  356. NextProtoNeg: chm.nextProtoNeg,
  357. ServerName: chm.serverName,
  358. OcspStapling: chm.ocspStapling,
  359. Scts: chm.scts,
  360. Ems: chm.ems,
  361. SupportedCurves: chm.supportedCurves,
  362. SupportedPoints: chm.supportedPoints,
  363. TicketSupported: chm.ticketSupported,
  364. SessionTicket: chm.sessionTicket,
  365. SupportedSignatureAlgorithms: chm.supportedSignatureAlgorithms,
  366. SecureRenegotiation: chm.secureRenegotiation,
  367. SecureRenegotiationSupported: chm.secureRenegotiationSupported,
  368. AlpnProtocols: chm.alpnProtocols,
  369. SupportedSignatureAlgorithmsCert: chm.supportedSignatureAlgorithmsCert,
  370. SupportedVersions: chm.supportedVersions,
  371. Cookie: chm.cookie,
  372. KeyShares: keyShares(chm.keyShares).ToPublic(),
  373. EarlyData: chm.earlyData,
  374. PskModes: chm.pskModes,
  375. PskIdentities: chm.pskIdentities,
  376. PskBinders: chm.pskBinders,
  377. }
  378. }
  379. }
  380. // UnmarshalClientHello allows external code to parse raw client hellos.
  381. // It returns nil on failure.
  382. func UnmarshalClientHello(data []byte) *ClientHelloMsg {
  383. m := &clientHelloMsg{}
  384. if m.unmarshal(data) {
  385. return m.getPublicPtr()
  386. }
  387. return nil
  388. }
  389. // A CipherSuite is a specific combination of key agreement, cipher and MAC
  390. // function. All cipher suites currently assume RSA key agreement.
  391. type CipherSuite struct {
  392. Id uint16
  393. // the lengths, in bytes, of the key material needed for each component.
  394. KeyLen int
  395. MacLen int
  396. IvLen int
  397. Ka func(version uint16) keyAgreement
  398. // flags is a bitmask of the suite* values, above.
  399. Flags int
  400. Cipher func(key, iv []byte, isRead bool) interface{}
  401. Mac func(version uint16, macKey []byte) macFunction
  402. Aead func(key, fixedNonce []byte) aead
  403. }
  404. func (cs *CipherSuite) getPrivatePtr() *cipherSuite {
  405. if cs == nil {
  406. return nil
  407. } else {
  408. return &cipherSuite{
  409. id: cs.Id,
  410. keyLen: cs.KeyLen,
  411. macLen: cs.MacLen,
  412. ivLen: cs.IvLen,
  413. ka: cs.Ka,
  414. flags: cs.Flags,
  415. cipher: cs.Cipher,
  416. mac: cs.Mac,
  417. aead: cs.Aead,
  418. }
  419. }
  420. }
  421. func (cs *cipherSuite) getPublicObj() CipherSuite {
  422. if cs == nil {
  423. return CipherSuite{}
  424. } else {
  425. return CipherSuite{
  426. Id: cs.id,
  427. KeyLen: cs.keyLen,
  428. MacLen: cs.macLen,
  429. IvLen: cs.ivLen,
  430. Ka: cs.ka,
  431. Flags: cs.flags,
  432. Cipher: cs.cipher,
  433. Mac: cs.mac,
  434. Aead: cs.aead,
  435. }
  436. }
  437. }
  438. // A FinishedHash calculates the hash of a set of handshake messages suitable
  439. // for including in a Finished message.
  440. type FinishedHash struct {
  441. Client hash.Hash
  442. Server hash.Hash
  443. // Prior to TLS 1.2, an additional MD5 hash is required.
  444. ClientMD5 hash.Hash
  445. ServerMD5 hash.Hash
  446. // In TLS 1.2, a full buffer is sadly required.
  447. Buffer []byte
  448. Version uint16
  449. Prf func(result, secret, label, seed []byte)
  450. }
  451. func (fh *FinishedHash) getPrivateObj() finishedHash {
  452. if fh == nil {
  453. return finishedHash{}
  454. } else {
  455. return finishedHash{
  456. client: fh.Client,
  457. server: fh.Server,
  458. clientMD5: fh.ClientMD5,
  459. serverMD5: fh.ServerMD5,
  460. buffer: fh.Buffer,
  461. version: fh.Version,
  462. prf: fh.Prf,
  463. }
  464. }
  465. }
  466. func (fh *finishedHash) getPublicObj() FinishedHash {
  467. if fh == nil {
  468. return FinishedHash{}
  469. } else {
  470. return FinishedHash{
  471. Client: fh.client,
  472. Server: fh.server,
  473. ClientMD5: fh.clientMD5,
  474. ServerMD5: fh.serverMD5,
  475. Buffer: fh.buffer,
  476. Version: fh.version,
  477. Prf: fh.prf}
  478. }
  479. }
  480. // TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
  481. type KeyShare struct {
  482. Group CurveID
  483. Data []byte
  484. }
  485. type KeyShares []KeyShare
  486. type keyShares []keyShare
  487. func (kss keyShares) ToPublic() []KeyShare {
  488. var KSS []KeyShare
  489. for _, ks := range kss {
  490. KSS = append(KSS, KeyShare{Data: ks.data, Group: ks.group})
  491. }
  492. return KSS
  493. }
  494. func (KSS KeyShares) ToPrivate() []keyShare {
  495. var kss []keyShare
  496. for _, KS := range KSS {
  497. kss = append(kss, keyShare{data: KS.Data, group: KS.Group})
  498. }
  499. return kss
  500. }
  501. // ClientSessionState is public, but all its fields are private. Let's add setters, getters and constructor
  502. // ClientSessionState contains the state needed by clients to resume TLS sessions.
  503. func MakeClientSessionState(
  504. SessionTicket []uint8,
  505. Vers uint16,
  506. CipherSuite uint16,
  507. MasterSecret []byte,
  508. ServerCertificates []*x509.Certificate,
  509. VerifiedChains [][]*x509.Certificate) *ClientSessionState {
  510. css := ClientSessionState{sessionTicket: SessionTicket,
  511. vers: Vers,
  512. cipherSuite: CipherSuite,
  513. masterSecret: MasterSecret,
  514. serverCertificates: ServerCertificates,
  515. verifiedChains: VerifiedChains}
  516. return &css
  517. }
  518. // Encrypted ticket used for session resumption with server
  519. func (css *ClientSessionState) SessionTicket() []uint8 {
  520. return css.sessionTicket
  521. }
  522. // SSL/TLS version negotiated for the session
  523. func (css *ClientSessionState) Vers() uint16 {
  524. return css.vers
  525. }
  526. // Ciphersuite negotiated for the session
  527. func (css *ClientSessionState) CipherSuite() uint16 {
  528. return css.cipherSuite
  529. }
  530. // MasterSecret generated by client on a full handshake
  531. func (css *ClientSessionState) MasterSecret() []byte {
  532. return css.masterSecret
  533. }
  534. // Certificate chain presented by the server
  535. func (css *ClientSessionState) ServerCertificates() []*x509.Certificate {
  536. return css.serverCertificates
  537. }
  538. // Certificate chains we built for verification
  539. func (css *ClientSessionState) VerifiedChains() [][]*x509.Certificate {
  540. return css.verifiedChains
  541. }
  542. func (css *ClientSessionState) SetSessionTicket(SessionTicket []uint8) {
  543. css.sessionTicket = SessionTicket
  544. }
  545. func (css *ClientSessionState) SetVers(Vers uint16) {
  546. css.vers = Vers
  547. }
  548. func (css *ClientSessionState) SetCipherSuite(CipherSuite uint16) {
  549. css.cipherSuite = CipherSuite
  550. }
  551. func (css *ClientSessionState) SetMasterSecret(MasterSecret []byte) {
  552. css.masterSecret = MasterSecret
  553. }
  554. func (css *ClientSessionState) SetServerCertificates(ServerCertificates []*x509.Certificate) {
  555. css.serverCertificates = ServerCertificates
  556. }
  557. func (css *ClientSessionState) SetVerifiedChains(VerifiedChains [][]*x509.Certificate) {
  558. css.verifiedChains = VerifiedChains
  559. }
  560. // TicketKey is the internal representation of a session ticket key.
  561. type TicketKey struct {
  562. // KeyName is an opaque byte string that serves to identify the session
  563. // ticket key. It's exposed as plaintext in every session ticket.
  564. KeyName [ticketKeyNameLen]byte
  565. AesKey [16]byte
  566. HmacKey [16]byte
  567. }
  568. type TicketKeys []TicketKey
  569. type ticketKeys []ticketKey
  570. func TicketKeyFromBytes(b [32]byte) TicketKey {
  571. tk := ticketKeyFromBytes(b)
  572. return tk.ToPublic()
  573. }
  574. func (tk ticketKey) ToPublic() TicketKey {
  575. return TicketKey{
  576. KeyName: tk.keyName,
  577. AesKey: tk.aesKey,
  578. HmacKey: tk.hmacKey,
  579. }
  580. }
  581. func (TK TicketKey) ToPrivate() ticketKey {
  582. return ticketKey{
  583. keyName: TK.KeyName,
  584. aesKey: TK.AesKey,
  585. hmacKey: TK.HmacKey,
  586. }
  587. }
  588. func (tks ticketKeys) ToPublic() []TicketKey {
  589. var TKS []TicketKey
  590. for _, ks := range tks {
  591. TKS = append(TKS, ks.ToPublic())
  592. }
  593. return TKS
  594. }
  595. func (TKS TicketKeys) ToPrivate() []ticketKey {
  596. var tks []ticketKey
  597. for _, TK := range TKS {
  598. tks = append(tks, TK.ToPrivate())
  599. }
  600. return tks
  601. }