Alpha 2 release. Includes Traverse message according to latest Whitepaper 0.8.

Send and traverse code needs some cleanup.
This commit is contained in:
Kleissner
2021-05-11 10:33:06 +02:00
parent db1c2e358c
commit 9eb1127b40
7 changed files with 339 additions and 16 deletions

View File

@@ -144,6 +144,23 @@ type MessageResponse struct {
HashesNotFound [][]byte // Hashes that were reported back as not found
}
// 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).
}
// ---- message decoding ----
// Minimum length of Announcement payload header without User Agent
@@ -783,6 +800,141 @@ createPacketLoop:
}
}
// ---- Traverse ----
const traversePayloadHeaderSize = 76 + 65 + 28
// msgDecodeTraverse 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 msgDecodeTraverse(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
}
// msgEncodeTraverse encodes a traverse message
func msgEncodeTraverse(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
}
// msgEncodeTraverseSetAddress sets the IP and Port
//func msgEncodeTraverseSetAddress(raw []byte, IPv4 net.IP, PortIPv4, PortIPv4ReportedExternal uint16, IPv6 net.IP, PortIPv6, PortIPv6ReportedExternal uint16) (err error) {
func msgEncodeTraverseSetAddress(raw []byte, connectionIPv4, connectionIPv6 *Connection) (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 connectionIPv4 != nil && connectionIPv4.IsIPv4() {
copy(raw[76+sizePacketEmbed+65:76+sizePacketEmbed+65+4], connectionIPv4.Address.IP.To4())
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+4:76+sizePacketEmbed+65+4+2], uint16(connectionIPv4.Address.Port))
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+6:76+sizePacketEmbed+65+6+2], connectionIPv4.PortExternal)
}
// IPv6
if connectionIPv6 != nil && connectionIPv6.IsIPv6() {
copy(raw[76+sizePacketEmbed+65+8:76+sizePacketEmbed+65+8+16], connectionIPv6.Address.IP.To16())
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+24:76+sizePacketEmbed+65+24+2], uint16(connectionIPv6.Address.Port))
binary.LittleEndian.PutUint16(raw[76+sizePacketEmbed+65+26:76+sizePacketEmbed+65+26+2], connectionIPv6.PortExternal)
}
return nil
}
// ---- messages sending ----
// pingConnection sends a ping to the target peer via the specified connection
@@ -822,3 +974,13 @@ func (peer *PeerInfo) sendResponse(sequence uint32, sendUA bool, hash2Peers []Ha
return err
}
// sendTraverse sends a traverse message
func (peer *PeerInfo) sendTraverse(embeddedPacketRaw []byte, receiverEnd *btcec.PublicKey) (err error) {
packetRaw, err := msgEncodeTraverse(peerPrivateKey, embeddedPacketRaw, receiverEnd, peer.PublicKey)
if err != nil {
return err
}
return peer.send(&PacketRaw{Command: CommandTraverse, Payload: packetRaw})
}