tickets.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright (C) 2017. See AUTHORS.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package openssl
  15. // #include "shim.h"
  16. import "C"
  17. import (
  18. "os"
  19. "unsafe"
  20. )
  21. const (
  22. KeyNameSize = 16
  23. )
  24. // TicketCipherCtx describes the cipher that will be used by the ticket store
  25. // for encrypting the tickets. Engine may be nil if no engine is desired.
  26. type TicketCipherCtx struct {
  27. Cipher *Cipher
  28. Engine *Engine
  29. }
  30. // TicketDigestCtx describes the digest that will be used by the ticket store
  31. // to authenticate the data. Engine may be nil if no engine is desired.
  32. type TicketDigestCtx struct {
  33. Digest *Digest
  34. Engine *Engine
  35. }
  36. // TicketName is an identifier for the key material for a ticket.
  37. type TicketName [KeyNameSize]byte
  38. // TicketKey is the key material for a ticket. If this is lost, forward secrecy
  39. // is lost as it allows decrypting TLS sessions retroactively.
  40. type TicketKey struct {
  41. Name TicketName
  42. CipherKey []byte
  43. HMACKey []byte
  44. IV []byte
  45. }
  46. // TicketKeyManager is a manager for TicketKeys. It allows one to control the
  47. // lifetime of tickets, causing renewals and expirations for keys that are
  48. // created. Calls to the manager are serialized.
  49. type TicketKeyManager interface {
  50. // New should create a brand new TicketKey with a new name.
  51. New() *TicketKey
  52. // Current should return a key that is still valid.
  53. Current() *TicketKey
  54. // Lookup should return a key with the given name, or nil if no name
  55. // exists.
  56. Lookup(name TicketName) *TicketKey
  57. // Expired should return if the key with the given name is expired and
  58. // should not be used any more.
  59. Expired(name TicketName) bool
  60. // ShouldRenew should return if the key is still ok to use for the current
  61. // session, but we should send a new key for the client.
  62. ShouldRenew(name TicketName) bool
  63. }
  64. // TicketStore descibes the encryption and authentication methods the tickets
  65. // will use along with a key manager for generating and keeping track of the
  66. // secrets.
  67. type TicketStore struct {
  68. CipherCtx TicketCipherCtx
  69. DigestCtx TicketDigestCtx
  70. Keys TicketKeyManager
  71. }
  72. func (t *TicketStore) cipherEngine() *C.ENGINE {
  73. if t.CipherCtx.Engine == nil {
  74. return nil
  75. }
  76. return t.CipherCtx.Engine.e
  77. }
  78. func (t *TicketStore) digestEngine() *C.ENGINE {
  79. if t.DigestCtx.Engine == nil {
  80. return nil
  81. }
  82. return t.DigestCtx.Engine.e
  83. }
  84. const (
  85. // instruct to do a handshake
  86. ticket_resp_requireHandshake = 0
  87. // crypto context is set up correctly
  88. ticket_resp_sessionOk = 1
  89. // crypto context is ok, but the ticket should be reissued
  90. ticket_resp_renewSession = 2
  91. // we had a problem that shouldn't fall back to doing a handshake
  92. ticket_resp_error = -1
  93. // asked to create session crypto context
  94. ticket_req_newSession = 1
  95. // asked to load crypto context for a previous session
  96. ticket_req_lookupSession = 0
  97. )
  98. //export go_ticket_key_cb_thunk
  99. func go_ticket_key_cb_thunk(p unsafe.Pointer, s *C.SSL, key_name *C.uchar,
  100. iv *C.uchar, cctx *C.EVP_CIPHER_CTX, hctx *C.HMAC_CTX, enc C.int) C.int {
  101. // no panic's allowed. it's super hard to guarantee any state at this point
  102. // so just abort everything.
  103. defer func() {
  104. if err := recover(); err != nil {
  105. logger.Critf("openssl: ticket key callback panic'd: %v", err)
  106. os.Exit(1)
  107. }
  108. }()
  109. ctx := (*Ctx)(p)
  110. store := ctx.ticket_store
  111. if store == nil {
  112. // TODO(jeff): should this be an error condition? it doesn't make sense
  113. // to be called if we don't have a store I believe, but that's probably
  114. // not worth aborting the handshake which is what I believe returning
  115. // an error would do.
  116. return ticket_resp_requireHandshake
  117. }
  118. ctx.ticket_store_mu.Lock()
  119. defer ctx.ticket_store_mu.Unlock()
  120. switch enc {
  121. case ticket_req_newSession:
  122. key := store.Keys.Current()
  123. if key == nil {
  124. key = store.Keys.New()
  125. if key == nil {
  126. return ticket_resp_requireHandshake
  127. }
  128. }
  129. C.memcpy(
  130. unsafe.Pointer(key_name),
  131. unsafe.Pointer(&key.Name[0]),
  132. KeyNameSize)
  133. C.EVP_EncryptInit_ex(
  134. cctx,
  135. store.CipherCtx.Cipher.ptr,
  136. store.cipherEngine(),
  137. (*C.uchar)(&key.CipherKey[0]),
  138. (*C.uchar)(&key.IV[0]))
  139. C.HMAC_Init_ex(
  140. hctx,
  141. unsafe.Pointer(&key.HMACKey[0]),
  142. C.int(len(key.HMACKey)),
  143. store.DigestCtx.Digest.ptr,
  144. store.digestEngine())
  145. return ticket_resp_sessionOk
  146. case ticket_req_lookupSession:
  147. var name TicketName
  148. C.memcpy(
  149. unsafe.Pointer(&name[0]),
  150. unsafe.Pointer(key_name),
  151. KeyNameSize)
  152. key := store.Keys.Lookup(name)
  153. if key == nil {
  154. return ticket_resp_requireHandshake
  155. }
  156. if store.Keys.Expired(name) {
  157. return ticket_resp_requireHandshake
  158. }
  159. C.EVP_DecryptInit_ex(
  160. cctx,
  161. store.CipherCtx.Cipher.ptr,
  162. store.cipherEngine(),
  163. (*C.uchar)(&key.CipherKey[0]),
  164. (*C.uchar)(&key.IV[0]))
  165. C.HMAC_Init_ex(
  166. hctx,
  167. unsafe.Pointer(&key.HMACKey[0]),
  168. C.int(len(key.HMACKey)),
  169. store.DigestCtx.Digest.ptr,
  170. store.digestEngine())
  171. if store.Keys.ShouldRenew(name) {
  172. return ticket_resp_renewSession
  173. }
  174. return ticket_resp_sessionOk
  175. default:
  176. return ticket_resp_error
  177. }
  178. }
  179. // SetTicketStore sets the ticket store for the context so that clients can do
  180. // ticket based session resumption. If the store is nil, the
  181. func (c *Ctx) SetTicketStore(store *TicketStore) {
  182. c.ticket_store = store
  183. if store == nil {
  184. C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, nil)
  185. } else {
  186. C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx,
  187. (*[0]byte)(C.X_SSL_CTX_ticket_key_cb))
  188. }
  189. }