1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
package dispatcher
import (
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"github.com/fxamacker/cbor/v2"
)
const (
ProtocolVersion = 1
MaxSphinxPayload = 2000
FrameDataBytes = 1500
MaxFrames = (MaxEnvelopeBytes + FrameDataBytes - 1) / FrameDataBytes
)
type Frame struct {
Version uint8 `cbor:"1,keyasint"`
ID [16]byte `cbor:"2,keyasint"`
Digest [32]byte `cbor:"3,keyasint"`
Index uint16 `cbor:"4,keyasint"`
Total uint16 `cbor:"5,keyasint"`
Data []byte `cbor:"6,keyasint"`
}
func SplitEnvelope(encoded []byte) ([][]byte, error) {
if len(encoded) == 0 || len(encoded) > MaxEnvelopeBytes {
return nil, errors.New("invalid encoded envelope size")
}
var id [16]byte
if _, err := rand.Read(id[:]); err != nil {
return nil, fmt.Errorf("generate message id: %w", err)
}
digest := sha256.Sum256(encoded)
total := (len(encoded) + FrameDataBytes - 1) / FrameDataBytes
if total > MaxFrames {
return nil, errors.New("encoded envelope requires too many frames")
}
frames := make([][]byte, 0, total)
for index, start := 0, 0; start < len(encoded); index, start = index+1, start+FrameDataBytes {
end := min(start+FrameDataBytes, len(encoded))
frame := Frame{
Version: ProtocolVersion,
ID: id,
Digest: digest,
Index: uint16(index),
Total: uint16(total),
Data: encoded[start:end],
}
payload, err := cbor.Marshal(frame)
if err != nil {
return nil, fmt.Errorf("encode frame %d: %w", index, err)
}
if len(payload) > MaxSphinxPayload {
return nil, fmt.Errorf("frame %d exceeds Sphinx payload limit", index)
}
frames = append(frames, payload)
}
return frames, nil
}
func DecodeFrame(payload []byte) (Frame, error) {
if len(payload) == 0 || len(payload) > MaxSphinxPayload {
return Frame{}, errors.New("invalid frame size")
}
var frame Frame
if err := cbor.Unmarshal(payload, &frame); err != nil {
return Frame{}, fmt.Errorf("decode frame: %w", err)
}
if frame.Version != ProtocolVersion {
return Frame{}, errors.New("unsupported frame version")
}
if frame.Total == 0 || int(frame.Total) > MaxFrames || frame.Index >= frame.Total {
return Frame{}, errors.New("invalid frame sequence")
}
if len(frame.Data) == 0 || len(frame.Data) > FrameDataBytes {
return Frame{}, errors.New("invalid frame data size")
}
return frame, nil
}
|