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

208 lines
5.7 KiB
Go

package tunnel
import (
"encoding/binary"
log "github.com/sirupsen/logrus"
"github.com/go-i2p/go-i2p/lib/crypto"
)
/*
I2P Encrypted Tunnel Message
https://geti2p.net/spec/tunnel-message
Accurate for version 0.9.11
+----+----+----+----+----+----+----+----+
| Tunnel ID | IV |
+----+----+----+----+ +
| |
+ +----+----+----+----+
| | |
+----+----+----+----+ +
| |
+ Encrypted Data +
~ ~
| |
+ +-------------------+
| |
+----+----+----+----+
Tunnel ID :: TunnelId
4 bytes
the ID of the next hop
IV ::
16 bytes
the initialization vector
Encrypted Data ::
1008 bytes
the encrypted tunnel message
total size: 1028 Bytes
I2P Decrypted 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 : 4+16]
}
func (decrypted_tunnel_message DecryptedTunnelMessage) Checksum() crypto.TunnelIV {
return decrypted_tunnel_message[4+16 : 4+4+16]
}
//
// Returns the contents of a decrypted tunnel message that contain the data for the
// DeliveryInstructions.
//
func (decrypted_tunnel_message DecryptedTunnelMessage) deliveryInstructionData() []byte {
data_area := decrypted_tunnel_message[4+4+16:]
for i := 0; i < len(data_area); i++ {
if data_area[i] == 0x00 {
return data_area[i+1:]
}
}
return []byte{}
}
//
// Returns a slice of DeliveryInstructionWithFragment structures, which all of the Delivery Instructions
// in the tunnel message and their corresponding MessageFragment structures.
//
//
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_size, err := instructions.FragmentSize()
if err != nil {
log.WithFields(log.Fields{
"at": "(DecryptedTunnelMessage) DeliveryInstructionsWithFragments",
"err": err.Error(),
}).Error("error getting delivery instructions fragment size")
break
}
fragment_data := remainder[:fragment_size]
pair := DeliveryInstructionsWithFragment{
DeliveryInstructions: instructions,
MessageFragment: fragment_data,
}
data = remainder[fragment_size:]
set = append(set, pair)
}
return set
}