Files
go-i2p/lib/tunnel/message.go

157 lines
4.3 KiB
Go

package tunnel
import (
"encoding/binary"
log "github.com/Sirupsen/logrus"
"github.com/hkparker/go-i2p/lib/crypto"
)
/*
I2P Tunnel Message
https://geti2p.net/spec/tunnel-message
Accurate for version 0.9.11
+----+----+----+----+----+----+----+----+
| Tunnel ID | IV |
+----+----+----+----+ +
| |
+ +----+----+----+----+
| | Checksum |
+----+----+----+----+----+----+----+----+
| nonzero padding... |
~ ~
| |
+ +----+
| |zero|
+----+----+----+----+----+----+----+----+
| |
| Delivery Instructions 1 |
~ ~
| |
+----+----+----+----+----+----+----+----+
| |
+ I2NP Message Fragment 1 +
| |
~ ~
| |
+----+----+----+----+----+----+----+----+
| |
| Delivery Instructions 2... |
~ ~
| |
+----+----+----+----+----+----+----+----+
| |
+ I2NP Message Fragment 2... +
| |
~ ~
| |
+ +-------------------+
| |
+----+----+----+----+
Tunnel ID :: TunnelId
4 bytes
the ID of the next hop
IV ::
16 bytes
the initialization vector
Checksum ::
4 bytes
the first 4 bytes of the SHA256 hash of (the contents of the message
(after the zero byte) + IV)
Nonzero padding ::
0 or more bytes
random nonzero data for padding
Zero ::
1 byte
the value 0x00
Delivery Instructions :: TunnelMessageDeliveryInstructions
length varies but is typically 7, 39, 43, or 47 bytes
Indicates the fragment and the routing for the fragment
Message Fragment ::
1 to 996 bytes, actual maximum depends on delivery instruction size
A partial or full I2NP Message
total size: 1028 Bytes
*/
type TunnelID uint32
type EncryptedTunnelMessage crypto.TunnelData
type DeliveryInstructionsWithFragment struct {
DeliveryInstructions DeliveryInstructions
MessageFragment []byte
}
func (tm EncryptedTunnelMessage) ID() (tid TunnelID) {
tid = TunnelID(binary.BigEndian.Uint32(tm[:4]))
return
}
func (tm EncryptedTunnelMessage) IV() crypto.TunnelIV {
return tm[4:20]
}
func (tm EncryptedTunnelMessage) Data() crypto.TunnelIV {
return tm[24:]
}
type DecryptedTunnelMessage [1028]byte
func (decrypted_tunnel_message DecryptedTunnelMessage) ID() TunnelID {
return TunnelID(binary.BigEndian.Uint32(
decrypted_tunnel_message[:4],
))
}
func (decrypted_tunnel_message DecryptedTunnelMessage) IV() crypto.TunnelIV {
return decrypted_tunnel_message[4:20]
}
func (decrypted_tunnel_message DecryptedTunnelMessage) Checksum() crypto.TunnelIV {
return decrypted_tunnel_message[24:28]
}
func (decrypted_tunnel_message DecryptedTunnelMessage) deliveryInstructionData() []byte {
data_area := decrypted_tunnel_message[28:]
padding_size := 0
for i, _ := range data_area {
if i == 0x00 {
padding_size = i
}
}
return data_area[padding_size:]
}
func (decrypted_tunnel_message DecryptedTunnelMessage) DeliveryInstructionsWithFragments() []DeliveryInstructionsWithFragment {
set := make([]DeliveryInstructionsWithFragment, 0)
data := decrypted_tunnel_message.deliveryInstructionData()
for {
instructions, remainder, err := readDeliveryInstructions(data)
if err != nil {
log.WithFields(log.Fields{
"at": "(DecryptedTunnelMessage) DeliveryInstructionsWithFragments",
"err": err.Error(),
}).Error("error reading delivery instructions")
break
}
fragment_data := remainder[:instructions.FragmentSize()]
pair := DeliveryInstructionsWithFragment{
DeliveryInstructions: instructions,
MessageFragment: fragment_data,
}
data = remainder[instructions.FragmentSize():]
set = append(set, pair)
}
return set
}