github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/quic-go/internal/protocol" 13 ) 14 15 type headerProtector interface { 16 EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) 17 DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) 18 } 19 20 func hkdfHeaderProtectionLabel(v protocol.VersionNumber) string { 21 if v == protocol.Version2 { 22 return "quicv2 hp" 23 } 24 return "quic hp" 25 } 26 27 func newHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector { 28 hkdfLabel := hkdfHeaderProtectionLabel(v) 29 switch suite.ID { 30 case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: 31 return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) 32 case tls.TLS_CHACHA20_POLY1305_SHA256: 33 return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) 34 default: 35 panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID)) 36 } 37 } 38 39 type aesHeaderProtector struct { 40 mask []byte 41 block cipher.Block 42 isLongHeader bool 43 } 44 45 var _ headerProtector = &aesHeaderProtector{} 46 47 func newAESHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { 48 hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) 49 block, err := aes.NewCipher(hpKey) 50 if err != nil { 51 panic(fmt.Sprintf("error creating new AES cipher: %s", err)) 52 } 53 return &aesHeaderProtector{ 54 block: block, 55 mask: make([]byte, block.BlockSize()), 56 isLongHeader: isLongHeader, 57 } 58 } 59 60 func (p *aesHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { 61 p.apply(sample, firstByte, hdrBytes) 62 } 63 64 func (p *aesHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { 65 p.apply(sample, firstByte, hdrBytes) 66 } 67 68 func (p *aesHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { 69 if len(sample) != len(p.mask) { 70 panic("invalid sample size") 71 } 72 p.block.Encrypt(p.mask, sample) 73 if p.isLongHeader { 74 *firstByte ^= p.mask[0] & 0xf 75 } else { 76 *firstByte ^= p.mask[0] & 0x1f 77 } 78 for i := range hdrBytes { 79 hdrBytes[i] ^= p.mask[i+1] 80 } 81 } 82 83 type chachaHeaderProtector struct { 84 mask [5]byte 85 86 key [32]byte 87 isLongHeader bool 88 } 89 90 var _ headerProtector = &chachaHeaderProtector{} 91 92 func newChaChaHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { 93 hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) 94 95 p := &chachaHeaderProtector{ 96 isLongHeader: isLongHeader, 97 } 98 copy(p.key[:], hpKey) 99 return p 100 } 101 102 func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { 103 p.apply(sample, firstByte, hdrBytes) 104 } 105 106 func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { 107 p.apply(sample, firstByte, hdrBytes) 108 } 109 110 func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { 111 if len(sample) != 16 { 112 panic("invalid sample size") 113 } 114 for i := 0; i < 5; i++ { 115 p.mask[i] = 0 116 } 117 cipher, err := chacha20.NewUnauthenticatedCipher(p.key[:], sample[4:]) 118 if err != nil { 119 panic(err) 120 } 121 cipher.SetCounter(binary.LittleEndian.Uint32(sample[:4])) 122 cipher.XORKeyStream(p.mask[:], p.mask[:]) 123 p.applyMask(firstByte, hdrBytes) 124 } 125 126 func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) { 127 if p.isLongHeader { 128 *firstByte ^= p.mask[0] & 0xf 129 } else { 130 *firstByte ^= p.mask[0] & 0x1f 131 } 132 for i := range hdrBytes { 133 hdrBytes[i] ^= p.mask[i+1] 134 } 135 }