Skip to content

Commit

Permalink
crypto/tls: add support for session ticket key rotation
Browse files Browse the repository at this point in the history
This change adds a new method to tls.Config, SetSessionTicketKeys, that
changes the key used to encrypt session tickets while the server is
running. Additional keys may be provided that will be used to maintain
continuity while rotating keys. If a ticket encrypted with an old key is
provided by the client, the server will resume the session and provide
the client with a ticket encrypted using the new key.

Fixes golang#9994

Change-Id: Idbc16b10ff39616109a51ed39a6fa208faad5b4e
Reviewed-on: https://go-review.googlesource.com/9072
Reviewed-by: Jonathan Rudenberg <jonathan@titanous.com>
Reviewed-by: Adam Langley <agl@golang.org>
  • Loading branch information
titanous authored and agl committed Apr 26, 2015
1 parent cf04082 commit 7576470
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 240 deletions.
74 changes: 71 additions & 3 deletions common.go
Expand Up @@ -8,6 +8,7 @@ import (
"container/list"
"crypto"
"crypto/rand"
"crypto/sha512"
"crypto/x509"
"fmt"
"io"
Expand Down Expand Up @@ -347,23 +348,90 @@ type Config struct {
CurvePreferences []CurveID

serverInitOnce sync.Once // guards calling (*Config).serverInit

// mutex protects sessionTicketKeys
mutex sync.RWMutex
// sessionTicketKeys contains zero or more ticket keys. If the length
// is zero, SessionTicketsDisabled must be true. The first key is used
// for new tickets and any subsequent keys can be used to decrypt old
// tickets.
sessionTicketKeys []ticketKey
}

// ticketKeyNameLen is the number of bytes of identifier that is prepended to
// an encrypted session ticket in order to identify the key used to encrypt it.
const ticketKeyNameLen = 16

// ticketKey is the internal representation of a session ticket key.
type ticketKey struct {
// keyName is an opaque byte string that serves to identify the session
// ticket key. It's exposed as plaintext in every session ticket.
keyName [ticketKeyNameLen]byte
aesKey [16]byte
hmacKey [16]byte
}

// ticketKeyFromBytes converts from the external representation of a session
// ticket key to a ticketKey. Externally, session ticket keys are 32 random
// bytes and this function expands that into sufficient name and key material.
func ticketKeyFromBytes(b [32]byte) (key ticketKey) {
hashed := sha512.Sum512(b[:])
copy(key.keyName[:], hashed[:ticketKeyNameLen])
copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16])
copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32])
return key
}

func (c *Config) serverInit() {
if c.SessionTicketsDisabled {
return
}

// If the key has already been set then we have nothing to do.
alreadySet := false
for _, b := range c.SessionTicketKey {
if b != 0 {
alreadySet = true
break
}
}

if !alreadySet {
if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
c.SessionTicketsDisabled = true
return
}
}

if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil {
c.SessionTicketsDisabled = true
c.sessionTicketKeys = []ticketKey{ticketKeyFromBytes(c.SessionTicketKey)}
}

func (c *Config) ticketKeys() []ticketKey {
c.mutex.RLock()
// c.sessionTicketKeys is constant once created. SetSessionTicketKeys
// will only update it by replacing it with a new value.
ret := c.sessionTicketKeys
c.mutex.RUnlock()
return ret
}

// SetSessionTicketKeys updates the session ticket keys for a server. The first
// key will be used when creating new tickets, while all keys can be used for
// decrypting tickets. It is safe to call this function while the server is
// running in order to rotate the session ticket keys. The function will panic
// if keys is empty.
func (c *Config) SetSessionTicketKeys(keys [][32]byte) {
if len(keys) == 0 {
panic("tls: keys must have at least one key")
}

newKeys := make([]ticketKey, len(keys))
for i, bytes := range keys {
newKeys[i] = ticketKeyFromBytes(bytes)
}

c.mutex.Lock()
c.sessionTicketKeys = newKeys
c.mutex.Unlock()
}

func (c *Config) rand() io.Reader {
Expand Down
29 changes: 26 additions & 3 deletions handshake_client_test.go
Expand Up @@ -422,15 +422,38 @@ func TestClientResumption(t *testing.T) {
}
}

getTicket := func() []byte {
return clientConfig.ClientSessionCache.(*lruSessionCache).q.Front().Value.(*lruSessionCacheEntry).state.sessionTicket
}
randomKey := func() [32]byte {
var k [32]byte
if _, err := io.ReadFull(serverConfig.rand(), k[:]); err != nil {
t.Fatalf("Failed to read new SessionTicketKey: %s", err)
}
return k
}

testResumeState("Handshake", false)
ticket := getTicket()
testResumeState("Resume", true)

if _, err := io.ReadFull(serverConfig.rand(), serverConfig.SessionTicketKey[:]); err != nil {
t.Fatalf("Failed to invalidate SessionTicketKey")
if !bytes.Equal(ticket, getTicket()) {
t.Fatal("first ticket doesn't match ticket after resumption")
}

key2 := randomKey()
serverConfig.SetSessionTicketKeys([][32]byte{key2})

testResumeState("InvalidSessionTicketKey", false)
testResumeState("ResumeAfterInvalidSessionTicketKey", true)

serverConfig.SetSessionTicketKeys([][32]byte{randomKey(), key2})
ticket = getTicket()
testResumeState("KeyChange", true)
if bytes.Equal(ticket, getTicket()) {
t.Fatal("new ticket wasn't included while resuming")
}
testResumeState("KeyChangeFinish", true)

clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
testResumeState("DifferentCipherSuite", false)
testResumeState("DifferentCipherSuiteRecovers", true)
Expand Down
9 changes: 9 additions & 0 deletions handshake_server.go
Expand Up @@ -61,6 +61,14 @@ func (c *Conn) serverHandshake() error {
if err := hs.establishKeys(); err != nil {
return err
}
// ticketSupported is set in a resumption handshake if the
// ticket from the client was encrypted with an old session
// ticket key and thus a refreshed ticket should be sent.
if hs.hello.ticketSupported {
if err := hs.sendSessionTicket(); err != nil {
return err
}
}
if err := hs.sendFinished(c.firstFinished[:]); err != nil {
return err
}
Expand Down Expand Up @@ -319,6 +327,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error {
// We echo the client's session ID in the ServerHello to let it know
// that we're doing a resumption.
hs.hello.sessionId = hs.clientHello.sessionId
hs.hello.ticketSupported = hs.sessionState.usedOldKey
hs.finishedHash.Write(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())

Expand Down
92 changes: 46 additions & 46 deletions testdata/Server-TLSv12-ALPN
@@ -1,7 +1,7 @@
>>> Flow 1 (client to server)
00000000 16 03 01 01 78 01 00 01 74 03 03 73 99 93 cd 3d |....x...t..s...=|
00000010 e8 60 23 0d 6a e8 f5 e3 46 ca 38 44 85 ca 79 c8 |.`#.j...F.8D..y.|
00000020 96 be 94 bd 43 d5 14 2b 20 da 5c 00 00 c4 c0 30 |....C..+ .\....0|
00000000 16 03 01 01 6b 01 00 01 67 03 03 e4 b0 a0 f0 85 |....k...g.......|
00000010 a5 8c 96 5d 78 c5 a5 f4 f2 d5 01 68 5c f3 c5 7d |...]x......h\..}|
00000020 00 d9 7c 0d b6 ca b4 6c c0 0e 79 00 00 b6 c0 30 |..|....l..y....0|
00000030 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5 00 a3 00 a1 |.,.(.$..........|
00000040 00 9f 00 6b 00 6a 00 69 00 68 00 39 00 38 00 37 |...k.j.i.h.9.8.7|
00000050 00 36 00 88 00 87 00 86 00 85 c0 32 c0 2e c0 2a |.6.........2...*|
Expand All @@ -13,16 +13,15 @@
000000b0 00 3c 00 2f 00 96 00 41 00 07 c0 11 c0 07 c0 0c |.<./...A........|
000000c0 c0 02 00 05 00 04 c0 12 c0 08 00 16 00 13 00 10 |................|
000000d0 00 0d c0 0d c0 03 00 0a 00 15 00 12 00 0f 00 0c |................|
000000e0 00 09 00 14 00 11 00 0e 00 0b 00 08 00 06 00 03 |................|
000000f0 00 ff 01 00 00 87 00 0b 00 04 03 00 01 02 00 0a |................|
00000100 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 0b 00 0c |.:.8............|
00000110 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 17 00 08 |................|
00000120 00 06 00 07 00 14 00 15 00 04 00 05 00 12 00 13 |................|
00000130 00 01 00 02 00 03 00 0f 00 10 00 11 00 23 00 00 |.............#..|
00000140 00 0d 00 20 00 1e 06 01 06 02 06 03 05 01 05 02 |... ............|
00000150 05 03 04 01 04 02 04 03 03 01 03 02 03 03 02 01 |................|
00000160 02 02 02 03 00 0f 00 01 01 00 10 00 10 00 0e 06 |................|
00000170 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |proto2.proto1|
000000e0 00 09 00 ff 02 01 00 00 87 00 0b 00 04 03 00 01 |................|
000000f0 02 00 0a 00 3a 00 38 00 0e 00 0d 00 19 00 1c 00 |....:.8.........|
00000100 0b 00 0c 00 1b 00 18 00 09 00 0a 00 1a 00 16 00 |................|
00000110 17 00 08 00 06 00 07 00 14 00 15 00 04 00 05 00 |................|
00000120 12 00 13 00 01 00 02 00 03 00 0f 00 10 00 11 00 |................|
00000130 23 00 00 00 0d 00 20 00 1e 06 01 06 02 06 03 05 |#..... .........|
00000140 01 05 02 05 03 04 01 04 02 04 03 03 01 03 02 03 |................|
00000150 03 02 01 02 02 02 03 00 0f 00 01 01 00 10 00 10 |................|
00000160 00 0e 06 70 72 6f 74 6f 32 06 70 72 6f 74 6f 31 |...proto2.proto1|
>>> Flow 2 (server to client)
00000000 16 03 03 00 42 02 00 00 3e 03 03 00 00 00 00 00 |....B...>.......|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
Expand Down Expand Up @@ -77,39 +76,40 @@
00000320 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd a7 24 20 |5uq..T[....g..$ |
00000330 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e f1 07 9f |>.V...(^.+-O....|
00000340 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 a6 b5 68 |lK[.V.2B.X..I..h|
00000350 1a 41 03 56 6b dc 5a 89 04 01 00 80 52 f3 4c 3f |.A.Vk.Z.....R.L?|
00000360 c4 82 3c 4f 8f dc f5 33 c5 12 41 80 dc ea f2 84 |..<O...3..A.....|
00000370 cf e4 50 f6 27 90 bb d0 09 ef 9c 9a 34 58 5c 38 |..P.'.......4X\8|
00000380 53 27 72 e5 07 86 bb 4d 6c 17 6f 79 60 bd ca cb |S'r....Ml.oy`...|
00000390 be 05 f1 0c 46 4b 1f 19 74 67 cd d9 64 2a fa 5f |....FK..tg..d*._|
000003a0 b8 47 fb 98 47 a9 1f d5 20 95 19 48 70 1a 1c 57 |.G..G... ..Hp..W|
000003b0 81 46 2a 8c 56 35 69 48 c9 23 a0 4e 7f f0 c0 fc |.F*.V5iH.#.N....|
000003c0 eb 28 8a d3 99 45 39 cc 2b 2a 93 1f c3 0b 68 60 |.(...E9.+*....h`|
000003d0 91 14 5e 6d be e6 40 19 38 76 d1 4c 16 03 03 00 |..^m..@.8v.L....|
00000350 1a 41 03 56 6b dc 5a 89 04 01 00 80 b6 f9 b6 2b |.A.Vk.Z........+|
00000360 15 b8 ef 70 37 61 64 f3 f3 a5 d9 da ce 13 b5 e1 |...p7ad.........|
00000370 0b 24 eb 11 a7 df 86 a9 ef 88 ef af 17 7d 02 56 |.$...........}.V|
00000380 ec 59 32 c9 5c 06 a4 ce 10 c7 6f 6a f3 e0 43 6a |.Y2.\.....oj..Cj|
00000390 02 99 f4 7b 14 65 dc a5 a0 af 10 3e a8 40 35 2b |...{.e.....>.@5+|
000003a0 c6 a1 31 b6 26 e9 89 0f 06 61 6f 2e 6c f4 70 69 |..1.&....ao.l.pi|
000003b0 e5 01 80 3d fe 4d 59 ad cb 2f b8 c1 df 5f 36 f7 |...=.MY../..._6.|
000003c0 cc a6 31 84 61 c0 e8 c5 95 37 9c e6 0d 2b 78 0c |..1.a....7...+x.|
000003d0 45 cf 69 5d fa 3a 8b 31 ea 22 60 31 16 03 03 00 |E.i].:.1."`1....|
000003e0 04 0e 00 00 00 |.....|
>>> Flow 3 (client to server)
00000000 16 03 03 00 46 10 00 00 42 41 04 e2 86 c1 a0 c0 |....F...BA......|
00000010 45 9a da 1a 70 a1 3e b6 9c b7 2e ec dd 2b 0a c6 |E...p.>......+..|
00000020 50 59 95 fe 8e 54 83 06 b6 68 42 60 56 de b2 b3 |PY...T...hB`V...|
00000030 b9 14 f0 e0 e2 2e a3 7f ec 01 4d 10 8a 43 ab 33 |..........M..C.3|
00000040 18 f4 b9 5d 6c ae cd 90 3e f4 64 14 03 03 00 01 |...]l...>.d.....|
00000050 01 16 03 03 00 28 47 e5 15 81 5b f4 a0 6a 61 d6 |.....(G...[..ja.|
00000060 df 5e 60 f1 d4 dc 55 45 84 0b ef 56 42 0b 42 1d |.^`...UE...VB.B.|
00000070 28 b4 90 a6 2a 47 41 97 3b 91 5c 74 ab 02 |(...*GA.;.\t..|
00000000 16 03 03 00 46 10 00 00 42 41 04 8d 5a 5d 91 04 |....F...BA..Z]..|
00000010 79 46 1b f1 12 3f d5 ca 57 18 5f 4d 71 d9 eb f8 |yF...?..W._Mq...|
00000020 90 f6 ed 75 b9 0c 2b 6e 67 cb 3a ae cc 6d 61 af |...u..+ng.:..ma.|
00000030 30 87 1b a6 21 d6 90 16 84 b0 65 3d 7f cc 96 ed |0...!.....e=....|
00000040 9e 68 38 e5 10 27 c3 23 48 40 f9 14 03 03 00 01 |.h8..'.#H@......|
00000050 01 16 03 03 00 28 7b a4 d0 fd 15 36 9b 1f 6e 4f |.....({....6..nO|
00000060 a9 d7 61 3f 58 93 5e 1b 10 be a1 8c c9 2f 39 74 |..a?X.^....../9t|
00000070 23 9a 1e ba 5b 3b e7 f0 32 b7 14 2e ae 0b |#...[;..2.....|
>>> Flow 4 (server to client)
00000000 16 03 03 00 72 04 00 00 6e 00 00 00 00 00 68 00 |....r...n.....h.|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 |...............e|
00000020 ea 8b e4 ef ba 19 39 3a 95 90 2b 6d 0d 59 ac 36 |......9:..+m.Y.6|
00000030 be 71 eb b4 25 51 86 cc 80 43 ea 60 e0 53 30 ba |.q..%Q...C.`.S0.|
00000040 3e b9 c3 29 9b 26 94 5a 43 36 d0 65 be a7 f1 06 |>..).&.ZC6.e....|
00000050 99 e3 c5 d7 f2 59 23 11 c5 99 27 5c 7f 43 94 0e |.....Y#...'\.C..|
00000060 b3 35 7a 66 d9 c4 49 53 2a 28 b6 3d e7 0f c5 d5 |.5zf..IS*(.=....|
00000070 a2 d8 15 a8 3a 88 f7 14 03 03 00 01 01 16 03 03 |....:...........|
00000080 00 28 00 00 00 00 00 00 00 00 07 2e 75 1d 9a 12 |.(..........u...|
00000090 9f e9 7e 0b 42 dd 7b 8e ae 58 ac 49 78 8d fb 3f |..~.B.{..X.Ix..?|
000000a0 21 e8 ef 91 3c 02 a6 23 d5 cc 17 03 03 00 25 00 |!...<..#......%.|
000000b0 00 00 00 00 00 00 01 bb 04 db f2 86 63 96 01 60 |............c..`|
000000c0 bb f4 68 f9 50 2a f0 15 82 f8 a1 73 bf cd 5f 4d |..h.P*.....s.._M|
000000d0 1a 73 67 91 15 03 03 00 1a 00 00 00 00 00 00 00 |.sg.............|
000000e0 02 02 79 34 67 e2 67 d5 52 59 91 76 90 10 c8 41 |..y4g.g.RY.v...A|
000000f0 c5 56 c9 |.V.|
00000000 16 03 03 00 82 04 00 00 7e 00 00 00 00 00 78 50 |........~.....xP|
00000010 46 ad c1 db a8 38 86 7b 2b bb fd d0 c3 42 3e 00 |F....8.{+....B>.|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 94 |................|
00000030 6f ec 80 83 61 dc ee 0e 43 06 28 f4 47 1a d7 25 |o...a...C.(.G..%|
00000040 f2 fa 66 d5 81 21 51 81 a8 47 2d a5 db e1 f2 84 |..f..!Q..G-.....|
00000050 ea 55 da 3e cf 97 fd 7e 63 68 50 e3 2d 48 5a 58 |.U.>...~chP.-HZX|
00000060 77 36 a2 9f 3f 33 94 65 de 9e e6 65 22 6f 1d c8 |w6..?3.e...e"o..|
00000070 46 80 2d 0b 83 41 5e c6 20 f6 c3 22 5f bb 7a 9b |F.-..A^. .."_.z.|
00000080 28 07 9c 5e b7 30 35 14 03 03 00 01 01 16 03 03 |(..^.05.........|
00000090 00 28 00 00 00 00 00 00 00 00 4a 1c a6 1e 78 e1 |.(........J...x.|
000000a0 4c 58 56 f5 6e 78 ae 11 7a dc 93 65 4b 46 6e b8 |LXV.nx..z..eKFn.|
000000b0 b6 2e 42 bc 71 81 61 3c 14 95 17 03 03 00 25 00 |..B.q.a<......%.|
000000c0 00 00 00 00 00 00 01 6e af 22 60 44 9b 18 e7 21 |.......n."`D...!|
000000d0 d9 c3 8d 48 8c 94 f1 aa cc 9d a4 11 ba b7 f2 0f |...H............|
000000e0 a2 91 e6 50 15 03 03 00 1a 00 00 00 00 00 00 00 |...P............|
000000f0 02 65 58 88 05 97 4a 2a 72 f5 03 da 53 24 4c b0 |.eX...J*r...S$L.|
00000100 01 4e 02 |.N.|

0 comments on commit 7576470

Please sign in to comment.