git.prognetwork.ru/x0r/utls@v1.3.3/ticket.go (about)

     1  // Copyright 2012 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  
     5  package tls
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/aes"
    10  	"crypto/cipher"
    11  	"crypto/hmac"
    12  	"crypto/sha256"
    13  	"crypto/subtle"
    14  	"errors"
    15  	"io"
    16  
    17  	"golang.org/x/crypto/cryptobyte"
    18  )
    19  
    20  // sessionState contains the information that is serialized into a session
    21  // ticket in order to later resume a connection.
    22  type sessionState struct {
    23  	vers         uint16
    24  	cipherSuite  uint16
    25  	createdAt    uint64
    26  	masterSecret []byte // opaque master_secret<1..2^16-1>;
    27  	// struct { opaque certificate<1..2^24-1> } Certificate;
    28  	certificates [][]byte // Certificate certificate_list<0..2^24-1>;
    29  
    30  	// usedOldKey is true if the ticket from which this session came from
    31  	// was encrypted with an older key and thus should be refreshed.
    32  	usedOldKey bool
    33  }
    34  
    35  func (m *sessionState) marshal() ([]byte, error) {
    36  	var b cryptobyte.Builder
    37  	b.AddUint16(m.vers)
    38  	b.AddUint16(m.cipherSuite)
    39  	addUint64(&b, m.createdAt)
    40  	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
    41  		b.AddBytes(m.masterSecret)
    42  	})
    43  	b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
    44  		for _, cert := range m.certificates {
    45  			b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
    46  				b.AddBytes(cert)
    47  			})
    48  		}
    49  	})
    50  	return b.Bytes()
    51  }
    52  
    53  func (m *sessionState) unmarshal(data []byte) bool {
    54  	*m = sessionState{usedOldKey: m.usedOldKey}
    55  	s := cryptobyte.String(data)
    56  	if ok := s.ReadUint16(&m.vers) &&
    57  		s.ReadUint16(&m.cipherSuite) &&
    58  		readUint64(&s, &m.createdAt) &&
    59  		readUint16LengthPrefixed(&s, &m.masterSecret) &&
    60  		len(m.masterSecret) != 0; !ok {
    61  		return false
    62  	}
    63  	var certList cryptobyte.String
    64  	if !s.ReadUint24LengthPrefixed(&certList) {
    65  		return false
    66  	}
    67  	for !certList.Empty() {
    68  		var cert []byte
    69  		if !readUint24LengthPrefixed(&certList, &cert) {
    70  			return false
    71  		}
    72  		m.certificates = append(m.certificates, cert)
    73  	}
    74  	return s.Empty()
    75  }
    76  
    77  // sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
    78  // version (revision = 0) doesn't carry any of the information needed for 0-RTT
    79  // validation and the nonce is always empty.
    80  type sessionStateTLS13 struct {
    81  	// uint8 version  = 0x0304;
    82  	// uint8 revision = 0;
    83  	cipherSuite      uint16
    84  	createdAt        uint64
    85  	resumptionSecret []byte      // opaque resumption_master_secret<1..2^8-1>;
    86  	certificate      Certificate // CertificateEntry certificate_list<0..2^24-1>;
    87  }
    88  
    89  func (m *sessionStateTLS13) marshal() ([]byte, error) {
    90  	var b cryptobyte.Builder
    91  	b.AddUint16(VersionTLS13)
    92  	b.AddUint8(0) // revision
    93  	b.AddUint16(m.cipherSuite)
    94  	addUint64(&b, m.createdAt)
    95  	b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
    96  		b.AddBytes(m.resumptionSecret)
    97  	})
    98  	marshalCertificate(&b, m.certificate)
    99  	return b.Bytes()
   100  }
   101  
   102  func (m *sessionStateTLS13) unmarshal(data []byte) bool {
   103  	*m = sessionStateTLS13{}
   104  	s := cryptobyte.String(data)
   105  	var version uint16
   106  	var revision uint8
   107  	return s.ReadUint16(&version) &&
   108  		version == VersionTLS13 &&
   109  		s.ReadUint8(&revision) &&
   110  		revision == 0 &&
   111  		s.ReadUint16(&m.cipherSuite) &&
   112  		readUint64(&s, &m.createdAt) &&
   113  		readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
   114  		len(m.resumptionSecret) != 0 &&
   115  		unmarshalCertificate(&s, &m.certificate) &&
   116  		s.Empty()
   117  }
   118  
   119  func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
   120  	if len(c.ticketKeys) == 0 {
   121  		return nil, errors.New("tls: internal error: session ticket keys unavailable")
   122  	}
   123  
   124  	encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
   125  	keyName := encrypted[:ticketKeyNameLen]
   126  	iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
   127  	macBytes := encrypted[len(encrypted)-sha256.Size:]
   128  
   129  	if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
   130  		return nil, err
   131  	}
   132  	key := c.ticketKeys[0]
   133  	copy(keyName, key.keyName[:])
   134  	block, err := aes.NewCipher(key.aesKey[:])
   135  	if err != nil {
   136  		return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
   137  	}
   138  	cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
   139  
   140  	mac := hmac.New(sha256.New, key.hmacKey[:])
   141  	mac.Write(encrypted[:len(encrypted)-sha256.Size])
   142  	mac.Sum(macBytes[:0])
   143  
   144  	return encrypted, nil
   145  }
   146  
   147  // [uTLS] added exported DecryptTicketWith func below
   148  func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
   149  	if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
   150  		return nil, false
   151  	}
   152  
   153  	keyName := encrypted[:ticketKeyNameLen]
   154  	iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
   155  	macBytes := encrypted[len(encrypted)-sha256.Size:]
   156  	ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
   157  
   158  	keyIndex := -1
   159  	for i, candidateKey := range c.ticketKeys {
   160  		if bytes.Equal(keyName, candidateKey.keyName[:]) {
   161  			keyIndex = i
   162  			break
   163  		}
   164  	}
   165  	if keyIndex == -1 {
   166  		return nil, false
   167  	}
   168  	key := &c.ticketKeys[keyIndex]
   169  
   170  	mac := hmac.New(sha256.New, key.hmacKey[:])
   171  	mac.Write(encrypted[:len(encrypted)-sha256.Size])
   172  	expected := mac.Sum(nil)
   173  
   174  	if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
   175  		return nil, false
   176  	}
   177  
   178  	block, err := aes.NewCipher(key.aesKey[:])
   179  	if err != nil {
   180  		return nil, false
   181  	}
   182  	plaintext = make([]byte, len(ciphertext))
   183  	cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
   184  
   185  	return plaintext, keyIndex > 0
   186  }
   187  
   188  // DecryptTicketWith decrypts an encrypted session ticket
   189  // using a TicketKeys (ie []TicketKey) struct
   190  //
   191  // usedOldKey will be true if the key used for decryption is
   192  // not the first in the []TicketKey slice
   193  //
   194  // [uTLS] changed to be made public and take a TicketKeys and use a fake conn receiver
   195  func DecryptTicketWith(encrypted []byte, tks TicketKeys) (plaintext []byte, usedOldKey bool) {
   196  	// create fake conn
   197  	c := &Conn{
   198  		ticketKeys: tks.ToPrivate(),
   199  	}
   200  
   201  	return c.decryptTicket(encrypted)
   202  }