github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/internal/handshake/header_protector.go (about)

     1  package handshake
     2  
     3  import (
     4  	"crypto/aes"
     5  	"crypto/cipher"
     6  	"crypto/tls"
     7  	"encoding/binary"
     8  	"fmt"
     9  
    10  	"golang.org/x/crypto/chacha20"
    11  
    12  	"github.com/danielpfeifer02/quic-go-prio-packs/crypto_turnoff"
    13  	"github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol"
    14  )
    15  
    16  type headerProtector interface {
    17  	EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
    18  	DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte)
    19  }
    20  
    21  func hkdfHeaderProtectionLabel(v protocol.Version) string {
    22  	if v == protocol.Version2 {
    23  		return "quicv2 hp"
    24  	}
    25  	return "quic hp"
    26  }
    27  
    28  func newHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, v protocol.Version) headerProtector {
    29  	hkdfLabel := hkdfHeaderProtectionLabel(v)
    30  	switch suite.ID {
    31  	case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
    32  		return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
    33  	case tls.TLS_CHACHA20_POLY1305_SHA256:
    34  		return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel)
    35  
    36  	// NO_CRYPTO_TAG
    37  	// based on https://pkg.go.dev/crypto/tls#pkg-constants 0x0000 is not used for any other cipher suite
    38  	case 0x0000:
    39  		return newNoHeaderProtector()
    40  
    41  	default:
    42  		panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID))
    43  	}
    44  }
    45  
    46  type aesHeaderProtector struct {
    47  	mask         [16]byte // AES always has a 16 byte block size
    48  	block        cipher.Block
    49  	isLongHeader bool
    50  }
    51  
    52  var _ headerProtector = &aesHeaderProtector{}
    53  
    54  func newAESHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
    55  	hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
    56  	block, err := aes.NewCipher(hpKey)
    57  	if err != nil {
    58  		panic(fmt.Sprintf("error creating new AES cipher: %s", err))
    59  	}
    60  	return &aesHeaderProtector{
    61  		block:        block,
    62  		isLongHeader: isLongHeader,
    63  	}
    64  }
    65  
    66  func (p *aesHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
    67  	// NO_CRYPTO_TAG
    68  	if crypto_turnoff.CRYPTO_TURNED_OFF {
    69  		return
    70  	}
    71  	p.apply(sample, firstByte, hdrBytes)
    72  }
    73  
    74  func (p *aesHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
    75  	// NO_CRYPTO_TAG
    76  	if crypto_turnoff.CRYPTO_TURNED_OFF {
    77  		return
    78  	}
    79  	p.apply(sample, firstByte, hdrBytes)
    80  }
    81  
    82  func (p *aesHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) {
    83  	if len(sample) != len(p.mask) {
    84  		panic("invalid sample size")
    85  	}
    86  	p.block.Encrypt(p.mask[:], sample)
    87  	if p.isLongHeader {
    88  		*firstByte ^= p.mask[0] & 0xf
    89  	} else {
    90  		*firstByte ^= p.mask[0] & 0x1f
    91  	}
    92  	for i := range hdrBytes {
    93  		hdrBytes[i] ^= p.mask[i+1]
    94  	}
    95  }
    96  
    97  type chachaHeaderProtector struct {
    98  	mask [5]byte
    99  
   100  	key          [32]byte
   101  	isLongHeader bool
   102  }
   103  
   104  var _ headerProtector = &chachaHeaderProtector{}
   105  
   106  func newChaChaHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector {
   107  	hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen)
   108  
   109  	p := &chachaHeaderProtector{
   110  		isLongHeader: isLongHeader,
   111  	}
   112  	copy(p.key[:], hpKey)
   113  	return p
   114  }
   115  
   116  func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
   117  	// NO_CRYPTO_TAG
   118  	if crypto_turnoff.CRYPTO_TURNED_OFF {
   119  		return
   120  	}
   121  	p.apply(sample, firstByte, hdrBytes)
   122  }
   123  
   124  func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
   125  	// NO_CRYPTO_TAG
   126  	if crypto_turnoff.CRYPTO_TURNED_OFF {
   127  		return
   128  	}
   129  	p.apply(sample, firstByte, hdrBytes)
   130  }
   131  
   132  func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) {
   133  	if len(sample) != 16 {
   134  		panic("invalid sample size")
   135  	}
   136  	for i := 0; i < 5; i++ {
   137  		p.mask[i] = 0
   138  	}
   139  	cipher, err := chacha20.NewUnauthenticatedCipher(p.key[:], sample[4:])
   140  	if err != nil {
   141  		panic(err)
   142  	}
   143  	cipher.SetCounter(binary.LittleEndian.Uint32(sample[:4]))
   144  	cipher.XORKeyStream(p.mask[:], p.mask[:])
   145  	p.applyMask(firstByte, hdrBytes)
   146  }
   147  
   148  func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) {
   149  	if p.isLongHeader {
   150  		*firstByte ^= p.mask[0] & 0xf
   151  	} else {
   152  		*firstByte ^= p.mask[0] & 0x1f
   153  	}
   154  	for i := range hdrBytes {
   155  		hdrBytes[i] ^= p.mask[i+1]
   156  	}
   157  }
   158  
   159  // NO_CRYPTO_TAG
   160  // noHeaderProtector type to omit the header protection.
   161  // TODOME even necessary?
   162  
   163  type noHeaderProtector struct{}
   164  
   165  var _ headerProtector = &noHeaderProtector{}
   166  
   167  func newNoHeaderProtector() headerProtector {
   168  	p := &chachaHeaderProtector{}
   169  	return p
   170  }
   171  
   172  func (p *noHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
   173  	p.apply(sample, firstByte, hdrBytes)
   174  }
   175  
   176  func (p *noHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) {
   177  	p.apply(sample, firstByte, hdrBytes)
   178  }
   179  
   180  func (p *noHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) {}