github.com/Carcraftz/utls@v0.0.0-20220413235215-6b7c52fd78b6/y_certificate_compression.go (about) 1 // Copyright (c) 2019 Yawning Angel <yawning at schwanenlied dot me> 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package tls 17 18 import ( 19 "bytes" 20 "compress/zlib" 21 "errors" 22 "fmt" 23 "io" 24 25 "github.com/dsnet/compress/brotli" 26 "golang.org/x/crypto/cryptobyte" 27 ) 28 29 const ( 30 // TEMPORARY: draft-ietf-tls-certificate-compression-04 31 typeCompressedCertificate uint8 = 25 32 extensionCompressCertificate uint16 = 27 33 ) 34 35 type CompressCertificateExtension struct { 36 Algorithms []CertCompressionAlgo 37 } 38 39 func (e *CompressCertificateExtension) writeToUConn(uc *UConn) error { 40 uc.extCompressCerts = true 41 return nil 42 } 43 44 func (e *CompressCertificateExtension) Len() int { 45 return 4 + 1 + (2 * len(e.Algorithms)) 46 } 47 48 func (e *CompressCertificateExtension) Read(b []byte) (int, error) { 49 if len(b) < e.Len() { 50 return 0, io.ErrShortBuffer 51 } 52 extLen := 2 * len(e.Algorithms) 53 if extLen > 255 { 54 return 0, errors.New("too many supported algorithms") 55 } 56 57 b[0] = byte(extensionCompressCertificate >> 8) 58 b[1] = byte(extensionCompressCertificate) 59 b[2] = byte((extLen + 1) >> 8) 60 b[3] = byte((extLen + 1)) 61 b[4] = byte(extLen) 62 63 i := 5 64 for _, alg := range e.Algorithms { 65 b[i] = byte(alg >> 8) 66 b[i+1] = byte(alg) 67 i += 2 68 } 69 return e.Len(), io.EOF 70 } 71 72 type compressedCertificateMsg struct { 73 raw []byte 74 75 algorithm CertCompressionAlgo 76 uncompressedLength uint32 77 compressedCertificateMessage []byte 78 } 79 80 func (m *compressedCertificateMsg) marshal() []byte { 81 if m.raw != nil { 82 return m.raw 83 } 84 85 panic("utls: compressedCertificateMsg.marshal() not actually implemented") 86 } 87 88 func (m *compressedCertificateMsg) unmarshal(data []byte) bool { 89 m.raw = append([]byte{}, data...) 90 91 s := cryptobyte.String(data[4:]) 92 93 var algID uint16 94 if !s.ReadUint16(&algID) { 95 return false 96 } 97 if !s.ReadUint24(&m.uncompressedLength) { 98 return false 99 } 100 if !readUint24LengthPrefixed(&s, &m.compressedCertificateMessage) { 101 return false 102 } 103 m.algorithm = CertCompressionAlgo(algID) 104 105 return true 106 } 107 108 func (m *compressedCertificateMsg) toCertificateMsg() (*certificateMsgTLS13, error) { 109 var ( 110 decompressed []byte 111 rd io.ReadCloser 112 err error 113 ) 114 115 if m.uncompressedLength > 1<<24 { 116 return nil, fmt.Errorf("utls: oversized decompressed certificate length") 117 } 118 119 compressed := bytes.NewBuffer(m.compressedCertificateMessage) 120 switch m.algorithm { 121 case CertCompressionZlib: 122 rd, err = zlib.NewReader(compressed) 123 case CertCompressionBrotli: 124 rd, err = brotli.NewReader(compressed, nil) 125 default: 126 return nil, fmt.Errorf("utls: unknown certificate compression algorithm: %v", m.algorithm) 127 } 128 if err != nil { 129 return nil, err 130 } 131 defer rd.Close() 132 133 decompressed = make([]byte, m.uncompressedLength) 134 if _, err = io.ReadFull(rd, decompressed); err != nil { 135 return nil, err 136 } 137 138 // Enforce the length just to be sure. 139 length := len(decompressed) 140 if length != int(m.uncompressedLength) { 141 return nil, fmt.Errorf("utls: invalid decompressed certificate length: %v", length) 142 } 143 144 // Prepend the type and record length to the synthetic Certificate message. 145 // Technically this can be 4 bytes of 0x00 since nothing examines it, but 146 // being correct doesn't hurt. 147 decompressed = append([]byte{ 148 typeCertificate, 149 byte(length >> 16), 150 byte(length >> 8), 151 byte(length), 152 }, decompressed...) 153 154 var mm certificateMsgTLS13 155 if !mm.unmarshal(decompressed) { 156 return nil, fmt.Errorf("utls: failed to unmarshal decompressed certificateMsgTLS13") 157 } 158 159 return &mm, nil 160 }