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  }