github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/protocol/connection_id.go (about) 1 package protocol 2 3 import ( 4 "crypto/rand" 5 "errors" 6 "fmt" 7 "io" 8 ) 9 10 var ErrInvalidConnectionIDLen = errors.New("invalid Connection ID length") 11 12 // An ArbitraryLenConnectionID is a QUIC Connection ID able to represent Connection IDs according to RFC 8999. 13 // Future QUIC versions might allow connection ID lengths up to 255 bytes, while QUIC v1 14 // restricts the length to 20 bytes. 15 type ArbitraryLenConnectionID []byte 16 17 func (c ArbitraryLenConnectionID) Len() int { 18 return len(c) 19 } 20 21 func (c ArbitraryLenConnectionID) Bytes() []byte { 22 return c 23 } 24 25 func (c ArbitraryLenConnectionID) String() string { 26 if c.Len() == 0 { 27 return "(empty)" 28 } 29 return fmt.Sprintf("%x", c.Bytes()) 30 } 31 32 const maxConnectionIDLen = 20 33 34 // A ConnectionID in QUIC 35 type ConnectionID struct { 36 b [20]byte 37 l uint8 38 } 39 40 // GenerateConnectionID generates a connection ID using cryptographic random 41 func GenerateConnectionID(l int) (ConnectionID, error) { 42 var c ConnectionID 43 c.l = uint8(l) 44 _, err := rand.Read(c.b[:l]) 45 return c, err 46 } 47 48 // ParseConnectionID interprets b as a Connection ID. 49 // It panics if b is longer than 20 bytes. 50 func ParseConnectionID(b []byte) ConnectionID { 51 if len(b) > maxConnectionIDLen { 52 panic("invalid conn id length") 53 } 54 var c ConnectionID 55 c.l = uint8(len(b)) 56 copy(c.b[:c.l], b) 57 return c 58 } 59 60 // GenerateConnectionIDForInitial generates a connection ID for the Initial packet. 61 // It uses a length randomly chosen between 8 and 20 bytes. 62 func GenerateConnectionIDForInitial() (ConnectionID, error) { 63 r := make([]byte, 1) 64 if _, err := rand.Read(r); err != nil { 65 return ConnectionID{}, err 66 } 67 l := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1) 68 return GenerateConnectionID(l) 69 } 70 71 // ReadConnectionID reads a connection ID of length len from the given io.Reader. 72 // It returns io.EOF if there are not enough bytes to read. 73 func ReadConnectionID(r io.Reader, l int) (ConnectionID, error) { 74 var c ConnectionID 75 if l == 0 { 76 return c, nil 77 } 78 if l > maxConnectionIDLen { 79 return c, ErrInvalidConnectionIDLen 80 } 81 c.l = uint8(l) 82 _, err := io.ReadFull(r, c.b[:l]) 83 if err == io.ErrUnexpectedEOF { 84 return c, io.EOF 85 } 86 return c, err 87 } 88 89 // Len returns the length of the connection ID in bytes 90 func (c ConnectionID) Len() int { 91 return int(c.l) 92 } 93 94 // Bytes returns the byte representation 95 func (c ConnectionID) Bytes() []byte { 96 return c.b[:c.l] 97 } 98 99 func (c ConnectionID) String() string { 100 if c.Len() == 0 { 101 return "(empty)" 102 } 103 return fmt.Sprintf("%x", c.Bytes()) 104 } 105 106 type DefaultConnectionIDGenerator struct { 107 ConnLen int 108 } 109 110 func (d *DefaultConnectionIDGenerator) GenerateConnectionID() (ConnectionID, error) { 111 return GenerateConnectionID(d.ConnLen) 112 } 113 114 func (d *DefaultConnectionIDGenerator) ConnectionIDLen() int { 115 return d.ConnLen 116 }