| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // Copyright (C) 2017. See AUTHORS.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package openssl
- // #include "shim.h"
- import "C"
- import (
- "os"
- "unsafe"
- )
- const (
- KeyNameSize = 16
- )
- // TicketCipherCtx describes the cipher that will be used by the ticket store
- // for encrypting the tickets. Engine may be nil if no engine is desired.
- type TicketCipherCtx struct {
- Cipher *Cipher
- Engine *Engine
- }
- // TicketDigestCtx describes the digest that will be used by the ticket store
- // to authenticate the data. Engine may be nil if no engine is desired.
- type TicketDigestCtx struct {
- Digest *Digest
- Engine *Engine
- }
- // TicketName is an identifier for the key material for a ticket.
- type TicketName [KeyNameSize]byte
- // TicketKey is the key material for a ticket. If this is lost, forward secrecy
- // is lost as it allows decrypting TLS sessions retroactively.
- type TicketKey struct {
- Name TicketName
- CipherKey []byte
- HMACKey []byte
- IV []byte
- }
- // TicketKeyManager is a manager for TicketKeys. It allows one to control the
- // lifetime of tickets, causing renewals and expirations for keys that are
- // created. Calls to the manager are serialized.
- type TicketKeyManager interface {
- // New should create a brand new TicketKey with a new name.
- New() *TicketKey
- // Current should return a key that is still valid.
- Current() *TicketKey
- // Lookup should return a key with the given name, or nil if no name
- // exists.
- Lookup(name TicketName) *TicketKey
- // Expired should return if the key with the given name is expired and
- // should not be used any more.
- Expired(name TicketName) bool
- // ShouldRenew should return if the key is still ok to use for the current
- // session, but we should send a new key for the client.
- ShouldRenew(name TicketName) bool
- }
- // TicketStore descibes the encryption and authentication methods the tickets
- // will use along with a key manager for generating and keeping track of the
- // secrets.
- type TicketStore struct {
- CipherCtx TicketCipherCtx
- DigestCtx TicketDigestCtx
- Keys TicketKeyManager
- }
- func (t *TicketStore) cipherEngine() *C.ENGINE {
- if t.CipherCtx.Engine == nil {
- return nil
- }
- return t.CipherCtx.Engine.e
- }
- func (t *TicketStore) digestEngine() *C.ENGINE {
- if t.DigestCtx.Engine == nil {
- return nil
- }
- return t.DigestCtx.Engine.e
- }
- const (
- // instruct to do a handshake
- ticket_resp_requireHandshake = 0
- // crypto context is set up correctly
- ticket_resp_sessionOk = 1
- // crypto context is ok, but the ticket should be reissued
- ticket_resp_renewSession = 2
- // we had a problem that shouldn't fall back to doing a handshake
- ticket_resp_error = -1
- // asked to create session crypto context
- ticket_req_newSession = 1
- // asked to load crypto context for a previous session
- ticket_req_lookupSession = 0
- )
- //export go_ticket_key_cb_thunk
- func go_ticket_key_cb_thunk(p unsafe.Pointer, s *C.SSL, key_name *C.uchar,
- iv *C.uchar, cctx *C.EVP_CIPHER_CTX, hctx *C.HMAC_CTX, enc C.int) C.int {
- // no panic's allowed. it's super hard to guarantee any state at this point
- // so just abort everything.
- defer func() {
- if err := recover(); err != nil {
- logger.Critf("openssl: ticket key callback panic'd: %v", err)
- os.Exit(1)
- }
- }()
- ctx := (*Ctx)(p)
- store := ctx.ticket_store
- if store == nil {
- // TODO(jeff): should this be an error condition? it doesn't make sense
- // to be called if we don't have a store I believe, but that's probably
- // not worth aborting the handshake which is what I believe returning
- // an error would do.
- return ticket_resp_requireHandshake
- }
- ctx.ticket_store_mu.Lock()
- defer ctx.ticket_store_mu.Unlock()
- switch enc {
- case ticket_req_newSession:
- key := store.Keys.Current()
- if key == nil {
- key = store.Keys.New()
- if key == nil {
- return ticket_resp_requireHandshake
- }
- }
- C.memcpy(
- unsafe.Pointer(key_name),
- unsafe.Pointer(&key.Name[0]),
- KeyNameSize)
- C.EVP_EncryptInit_ex(
- cctx,
- store.CipherCtx.Cipher.ptr,
- store.cipherEngine(),
- (*C.uchar)(&key.CipherKey[0]),
- (*C.uchar)(&key.IV[0]))
- C.HMAC_Init_ex(
- hctx,
- unsafe.Pointer(&key.HMACKey[0]),
- C.int(len(key.HMACKey)),
- store.DigestCtx.Digest.ptr,
- store.digestEngine())
- return ticket_resp_sessionOk
- case ticket_req_lookupSession:
- var name TicketName
- C.memcpy(
- unsafe.Pointer(&name[0]),
- unsafe.Pointer(key_name),
- KeyNameSize)
- key := store.Keys.Lookup(name)
- if key == nil {
- return ticket_resp_requireHandshake
- }
- if store.Keys.Expired(name) {
- return ticket_resp_requireHandshake
- }
- C.EVP_DecryptInit_ex(
- cctx,
- store.CipherCtx.Cipher.ptr,
- store.cipherEngine(),
- (*C.uchar)(&key.CipherKey[0]),
- (*C.uchar)(&key.IV[0]))
- C.HMAC_Init_ex(
- hctx,
- unsafe.Pointer(&key.HMACKey[0]),
- C.int(len(key.HMACKey)),
- store.DigestCtx.Digest.ptr,
- store.digestEngine())
- if store.Keys.ShouldRenew(name) {
- return ticket_resp_renewSession
- }
- return ticket_resp_sessionOk
- default:
- return ticket_resp_error
- }
- }
- // SetTicketStore sets the ticket store for the context so that clients can do
- // ticket based session resumption. If the store is nil, the
- func (c *Ctx) SetTicketStore(store *TicketStore) {
- c.ticket_store = store
- if store == nil {
- C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, nil)
- } else {
- C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx,
- (*[0]byte)(C.X_SSL_CTX_ticket_key_cb))
- }
- }
|