github.com/mikelsr/quic-go@v0.36.1-0.20230701132136-1d9415b66898/internal/handshake/token_protector.go (about)

     1  package handshake
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/sha256"
     7  	"fmt"
     8  	"io"
     9  
    10  	"golang.org/x/crypto/hkdf"
    11  )
    12  
    13  // TokenProtector is used to create and verify a token
    14  type tokenProtector interface {
    15  	// NewToken creates a new token
    16  	NewToken([]byte) ([]byte, error)
    17  	// DecodeToken decodes a token
    18  	DecodeToken([]byte) ([]byte, error)
    19  }
    20  
    21  const (
    22  	tokenSecretSize = 32
    23  	tokenNonceSize  = 32
    24  )
    25  
    26  // tokenProtector is used to create and verify a token
    27  type tokenProtectorImpl struct {
    28  	rand   io.Reader
    29  	secret []byte
    30  }
    31  
    32  // newTokenProtector creates a source for source address tokens
    33  func newTokenProtector(rand io.Reader) (tokenProtector, error) {
    34  	secret := make([]byte, tokenSecretSize)
    35  	if _, err := rand.Read(secret); err != nil {
    36  		return nil, err
    37  	}
    38  	return &tokenProtectorImpl{
    39  		rand:   rand,
    40  		secret: secret,
    41  	}, nil
    42  }
    43  
    44  // NewToken encodes data into a new token.
    45  func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) {
    46  	nonce := make([]byte, tokenNonceSize)
    47  	if _, err := s.rand.Read(nonce); err != nil {
    48  		return nil, err
    49  	}
    50  	aead, aeadNonce, err := s.createAEAD(nonce)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return append(nonce, aead.Seal(nil, aeadNonce, data, nil)...), nil
    55  }
    56  
    57  // DecodeToken decodes a token.
    58  func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) {
    59  	if len(p) < tokenNonceSize {
    60  		return nil, fmt.Errorf("token too short: %d", len(p))
    61  	}
    62  	nonce := p[:tokenNonceSize]
    63  	aead, aeadNonce, err := s.createAEAD(nonce)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	return aead.Open(nil, aeadNonce, p[tokenNonceSize:], nil)
    68  }
    69  
    70  func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) {
    71  	h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go token source"))
    72  	key := make([]byte, 32) // use a 32 byte key, in order to select AES-256
    73  	if _, err := io.ReadFull(h, key); err != nil {
    74  		return nil, nil, err
    75  	}
    76  	aeadNonce := make([]byte, 12)
    77  	if _, err := io.ReadFull(h, aeadNonce); err != nil {
    78  		return nil, nil, err
    79  	}
    80  	c, err := aes.NewCipher(key)
    81  	if err != nil {
    82  		return nil, nil, err
    83  	}
    84  	aead, err := cipher.NewGCM(c)
    85  	if err != nil {
    86  		return nil, nil, err
    87  	}
    88  	return aead, aeadNonce, nil
    89  }