diff --git a/communication/u2fhid/u2fhid.go b/communication/u2fhid/u2fhid.go index 2b58b4a..65af17e 100644 --- a/communication/u2fhid/u2fhid.go +++ b/communication/u2fhid/u2fhid.go @@ -83,6 +83,15 @@ func (communication *Communication) sendFrame(msg string) error { if dataLen == 0 { return nil } + const ( + initPayloadSize = writeReportSize - 7 + contPayloadSize = writeReportSize - 5 + // Continuation sequence numbers range from 0 to 127. + maxPayloadSize = initPayloadSize + 128*contPayloadSize + ) + if dataLen > maxPayloadSize { + return errp.Newf("message too large: %d bytes exceeds maximum of %d", dataLen, maxPayloadSize) + } readBuffer := bytes.NewBufferString(msg) out := newBuffer() @@ -90,8 +99,6 @@ func (communication *Communication) sendFrame(msg string) error { // Calculate how large the `out` buffer should be. Round up to an equal // number of writeReportSize sized bytes outLen := writeReportSize - initPayloadSize := writeReportSize - 7 - contPayloadSize := writeReportSize - 5 if dataLen > initPayloadSize { contLen := dataLen - initPayloadSize outLen += ((contLen + contPayloadSize - 1) / contPayloadSize) * writeReportSize diff --git a/communication/u2fhid/u2fhid_test.go b/communication/u2fhid/u2fhid_test.go index fa2ce1f..8943cec 100644 --- a/communication/u2fhid/u2fhid_test.go +++ b/communication/u2fhid/u2fhid_test.go @@ -108,6 +108,12 @@ func TestSendFrame(t *testing.T) { chunkSize: 1, expectedFrames: strings.Repeat("d", 64*3), }, + { + name: "maximum payload", + input: strings.Repeat("e", 7609), + chunkSize: 64, + expectedFrames: strings.Repeat("e", 7609), + }, } for _, tt := range tests { @@ -177,3 +183,24 @@ func TestSendFrame(t *testing.T) { }) } } + +func TestSendFrameRejectsOversizedMessages(t *testing.T) { + for _, tt := range []struct { + name string + size int + }{ + {name: "one byte over maximum", size: 7610}, + {name: "length exceeds uint16", size: 1 << 16}, + } { + t.Run(tt.name, func(t *testing.T) { + rw := &testRW{} + comm := NewCommunication(rw, 0xab) + if err := comm.SendFrame(strings.Repeat("a", tt.size)); err == nil { + t.Error("expected oversized message to be rejected") + } + if rw.writeBuffer.Len() != 0 { + t.Error("oversized message wrote packets to the device") + } + }) + } +}