Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FrameBuilder for building frames with target packet length #39

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions u_quic_frames.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,59 @@ type QUICFrameBuilder interface {
// It could be used to deterministically build QUIC Frames from crypto data.
type QUICFrames []QUICFrame

// QUICPacket is a struct that contains a slice of QUICFrame and the total length
// of all frames including PADDING frames. It can be used to deterministically
// build QUIC Packets with a target length.
type QUICPacket struct {
Frames QUICFrames

Length uint16
}

// Build ingests data from crypto frames without the crypto frame header
// and returns the byte representation of all frames as specified in
// the Frames slice. It then calculates the padding sizes needed to
// reach the specified Length and updates the PADDING frames accordingly.
func (qp QUICPacket) Build(cryptoData []byte) (payload []byte, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (qp QUICPacket) Build(cryptoData []byte) (payload []byte, err error) {
func (qp *QUICPacket) Build(cryptoData []byte) (payload []byte, err error) {

Let's make *QUICPacket instead of QUICPacket the one interfacing QUICFrameBuilder, just to align with the style of *QUICRandomFrames. (QUICFrames is an exception as it is a slice instead of a ground-truth struct)

// dry-run to determine the total length of all frames so far
dryrunPayload, err := qp.Frames.Build(cryptoData)
if err != nil {
return nil, err
}

// determine length of PADDING frames to append
lenPADDINGsigned := int64(qp.Length) - int64(len(dryrunPayload))
if lenPADDINGsigned > 0 {
lenPADDING := uint64(lenPADDINGsigned)
// determine number of PADDING frames to append
numPADDING := 0
for _, frame := range qp.Frames {
if _, ok := frame.(QUICFramePadding); ok {
numPADDING++
}
}

// create a list of values made from dividing lenPADDING into equal sizes of length numPADDING
paddingSizes := make([]uint64, numPADDING)
paddingSize := lenPADDING / uint64(numPADDING)
for i := 0; i < numPADDING; i++ {
paddingSizes[i] = paddingSize
}
// distribute the remaining lenPADDING into the list of values
remaining := lenPADDING % uint64(numPADDING)
paddingSizes[len(paddingSizes)-1] += remaining

// update the padding frames with the calculated sizes
for i, frame := range qp.Frames {
if _, ok := frame.(QUICFramePadding); ok {
qp.Frames[i] = QUICFramePadding{Length: int(paddingSizes[0])}
paddingSizes = paddingSizes[1:]
}
}
}
return qp.Frames.Build(cryptoData)
}

// Build ingests data from crypto frames without the crypto frame header
// and returns the byte representation of all frames as specified in
// the slice.
Expand Down