github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/quic-go/internal/wire/version_negotiation.go (about)

     1  package wire
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"errors"
     7  
     8  	"github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/protocol"
     9  	"github.com/ooni/psiphon/tunnel-core/oovendor/quic-go/internal/utils"
    10  )
    11  
    12  // ParseVersionNegotiationPacket parses a Version Negotiation packet.
    13  func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.VersionNumber, error) {
    14  	hdr, err := parseHeader(b, 0)
    15  	if err != nil {
    16  		return nil, nil, err
    17  	}
    18  	if b.Len() == 0 {
    19  		//nolint:stylecheck
    20  		return nil, nil, errors.New("Version Negotiation packet has empty version list")
    21  	}
    22  	if b.Len()%4 != 0 {
    23  		//nolint:stylecheck
    24  		return nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length")
    25  	}
    26  	versions := make([]protocol.VersionNumber, b.Len()/4)
    27  	for i := 0; b.Len() > 0; i++ {
    28  		v, err := utils.BigEndian.ReadUint32(b)
    29  		if err != nil {
    30  			return nil, nil, err
    31  		}
    32  		versions[i] = protocol.VersionNumber(v)
    33  	}
    34  	return hdr, versions, nil
    35  }
    36  
    37  // ComposeVersionNegotiation composes a Version Negotiation
    38  func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) {
    39  	greasedVersions := protocol.GetGreasedVersions(versions)
    40  	expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4
    41  	buf := bytes.NewBuffer(make([]byte, 0, expectedLen))
    42  	r := make([]byte, 1)
    43  	_, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here.
    44  	buf.WriteByte(r[0] | 0x80)
    45  	utils.BigEndian.WriteUint32(buf, 0) // version 0
    46  	buf.WriteByte(uint8(destConnID.Len()))
    47  	buf.Write(destConnID)
    48  	buf.WriteByte(uint8(srcConnID.Len()))
    49  	buf.Write(srcConnID)
    50  	for _, v := range greasedVersions {
    51  		utils.BigEndian.WriteUint32(buf, uint32(v))
    52  	}
    53  	return buf.Bytes(), nil
    54  }