/
Message Encoding Traverse.go
165 lines (131 loc) · 7.16 KB
/
Message Encoding Traverse.go
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
File Username: Message Encoding Traverse.go
Copyright: 2021 Peernet s.r.o.
Author: Peter Kleissner
*/
package protocol
import (
"encoding/binary"
"errors"
"net"
"time"
"github.com/newinfoOffical/core/btcec"
)
// MessageTraverse is the decoded traverse message.
// It is sent by an original sender to a relay, to a final receiver (targert peer).
type MessageTraverse struct {
*MessageRaw // Underlying raw message.
TargetPeer *btcec.PublicKey // End receiver peer ID.
AuthorizedRelayPeer *btcec.PublicKey // Peer ID that is authorized to relay this message to the end receiver.
Expires time.Time // Expiration time when this forwarded message becomes invalid.
EmbeddedPacketRaw []byte // Embedded packet.
SignerPublicKey *btcec.PublicKey // Public key that signed this message, ECDSA (secp256k1) 257-bit
IPv4 net.IP // IPv4 address of the original sender. Set by authorized relay. 0 if not set.
PortIPv4 uint16 // Port (actual one used for connection) of the original sender. Set by authorized relay.
PortIPv4ReportedExternal uint16 // External port as reported by the original sender. This is used in case of port forwarding (manual or automated).
IPv6 net.IP // IPv6 address of the original sender. Set by authorized relay. 0 if not set.
PortIPv6 uint16 // Port (actual one used for connection) of the original sender. Set by authorized relay.
PortIPv6ReportedExternal uint16 // External port as reported by the original sender. This is used in case of port forwarding (manual or automated).
}
const traversePayloadHeaderSize = 76 + 65 + 28
// DecodeTraverse decodes a traverse message.
// It does not verify if the receiver is authorized to read or forward this message.
// It validates the signature, but does not validate the signer.
func DecodeTraverse(msg *MessageRaw) (result *MessageTraverse, err error) {
result = &MessageTraverse{
MessageRaw: msg,
}
if len(msg.Payload) < traversePayloadHeaderSize {
return nil, errors.New("traverse: invalid minimum length")
}
targetPeerIDcompressed := msg.Payload[0:33]
authorizedRelayPeerIDcompressed := msg.Payload[33:66]
if result.TargetPeer, err = btcec.ParsePubKey(targetPeerIDcompressed, btcec.S256()); err != nil {
return nil, err
}
if result.AuthorizedRelayPeer, err = btcec.ParsePubKey(authorizedRelayPeerIDcompressed, btcec.S256()); err != nil {
return nil, err
}
// receiver and target must not be the same
if result.TargetPeer.IsEqual(result.AuthorizedRelayPeer) {
return nil, errors.New("traverse: target and relay invalid")
}
expires64 := binary.LittleEndian.Uint64(msg.Payload[66 : 66+8])
result.Expires = time.Unix(int64(expires64), 0)
sizePacketEmbed := binary.LittleEndian.Uint16(msg.Payload[74 : 74+2])
if int(sizePacketEmbed) != len(msg.Payload)-traversePayloadHeaderSize {
return nil, errors.New("traverse: size embedded packet mismatch")
}
result.EmbeddedPacketRaw = msg.Payload[76 : 76+sizePacketEmbed]
signature := msg.Payload[76+sizePacketEmbed : 76+sizePacketEmbed+65]
result.SignerPublicKey, _, err = btcec.RecoverCompact(btcec.S256(), signature, HashData(msg.Payload[:76+sizePacketEmbed]))
if err != nil {
return nil, err
}
// IPv4
ipv4B := make([]byte, 4)
copy(ipv4B[:], msg.Payload[76+sizePacketEmbed+65:76+sizePacketEmbed+65+4])
result.IPv4 = ipv4B
result.PortIPv4 = binary.LittleEndian.Uint16(msg.Payload[76+sizePacketEmbed+65+4 : 76+sizePacketEmbed+65+4+2])
result.PortIPv4ReportedExternal = binary.LittleEndian.Uint16(msg.Payload[76+sizePacketEmbed+65+6 : 76+sizePacketEmbed+65+6+2])
// IPv6
ipv6B := make([]byte, 16)
copy(ipv6B[:], msg.Payload[76+sizePacketEmbed+65+8:76+sizePacketEmbed+65+8+16])
result.IPv6 = ipv6B
result.PortIPv6 = binary.LittleEndian.Uint16(msg.Payload[76+sizePacketEmbed+65+24 : 76+sizePacketEmbed+65+24+2])
result.PortIPv6ReportedExternal = binary.LittleEndian.Uint16(msg.Payload[76+sizePacketEmbed+65+26 : 76+sizePacketEmbed+65+26+2])
// TODO: Validate IPv4 and IPv6. Only external ones allowed.
if result.IPv6.To4() != nil {
return nil, errors.New("traverse: ipv6 address mismatch")
}
return result, nil
}
// EncodeTraverse encodes a traverse message
func EncodeTraverse(senderPrivateKey *btcec.PrivateKey, embeddedPacketRaw []byte, receiverEnd *btcec.PublicKey, relayPeer *btcec.PublicKey) (packetRaw []byte, err error) {
sizePacketEmbed := len(embeddedPacketRaw)
if isPacketSizeExceed(traversePayloadHeaderSize, sizePacketEmbed) {
return nil, errors.New("traverse encode: embedded packet too big")
}
raw := make([]byte, traversePayloadHeaderSize+sizePacketEmbed)
targetPeerID := receiverEnd.SerializeCompressed()
copy(raw[0:33], targetPeerID)
authorizedRelayPeerID := relayPeer.SerializeCompressed()
copy(raw[33:66], authorizedRelayPeerID)
expires64 := time.Now().Add(time.Hour).UTC().Unix()
binary.LittleEndian.PutUint64(raw[66:66+8], uint64(expires64))
binary.LittleEndian.PutUint16(raw[74:74+2], uint16(sizePacketEmbed))
copy(raw[76:76+sizePacketEmbed], embeddedPacketRaw)
// add signature
signature, err := btcec.SignCompact(btcec.S256(), senderPrivateKey, HashData(raw[:76+sizePacketEmbed]), true)
if err != nil {
return nil, err
}
copy(raw[76+sizePacketEmbed:76+sizePacketEmbed+65], signature)
// IP and ports are to be filled by authorized relay peer
return raw, nil
}
// EncodeTraverseSetAddress sets the IP and Port in a traverse message that shall be forwarded to another peer
func EncodeTraverseSetAddress(raw []byte, IPv4 net.IP, PortIPv4, PortIPv4ReportedExternal uint16, IPv6 net.IP, PortIPv6, PortIPv6ReportedExternal uint16) (err error) {
if isPacketSizeExceed(len(raw), 0) {
return errors.New("traverse encode 2: embedded packet too big")
} else if len(raw) < traversePayloadHeaderSize {
return errors.New("traverse encode 2: invalid packet")
}
sizePacketEmbed := binary.LittleEndian.Uint16(raw[74 : 74+2])
if int(sizePacketEmbed) != len(raw)-traversePayloadHeaderSize {
return errors.New("traverse encode 2: size embedded packet mismatch")
}
// IPv4
if IPv4 != nil && len(IPv4) == net.IPv4len {
copy(raw[76+sizePacketEmbed+65:76+sizePacketEmbed+65+4], IPv4.To4())
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+4:76+sizePacketEmbed+65+4+2], PortIPv4)
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+6:76+sizePacketEmbed+65+6+2], PortIPv4ReportedExternal)
}
// IPv6
if IPv6 != nil && len(IPv6) == net.IPv6len {
copy(raw[76+sizePacketEmbed+65+8:76+sizePacketEmbed+65+8+16], IPv6.To16())
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+24:76+sizePacketEmbed+65+24+2], PortIPv6)
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+26:76+sizePacketEmbed+65+26+2], PortIPv6ReportedExternal)
}
return nil
}