mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-06-07 10:01:41 -04:00
143 lines
4.8 KiB
Go
143 lines
4.8 KiB
Go
package ntcp
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/go-i2p/go-i2p/lib/common/router_info"
|
|
"github.com/go-i2p/go-i2p/lib/crypto"
|
|
"github.com/go-i2p/go-i2p/lib/transport/noise"
|
|
"github.com/go-i2p/go-i2p/lib/transport/obfs"
|
|
"golang.org/x/exp/rand"
|
|
)
|
|
|
|
/*
|
|
Summary of what needs to be done:
|
|
NTCP and SSU2 are both transport protocols based on noise, with additional features designed to prevent p2p traffic from being blocked by firewalls.
|
|
These modifications affect how the Noise handshake takes place, in particular:
|
|
- Ephemeral keys are transmitted **obfuscated** by encrypting them with the peer's known static public key.
|
|
these modifications are simple enough, but for our purposes we also want to be able to re-use as much code as possible.
|
|
So, what we need to do is devise a means of adding these modifications to the existing NoiseSession implementation.
|
|
We could do this in any number of ways, we could:
|
|
1. Implement a custom struct that embeds a NoiseSession and overrides the Compose*HandshakeMessage functions
|
|
2. Modify the NoiseSession handshake functions to allow passing an obfuscation and/or padding function as a parameter
|
|
3. Modify the NoiseSession implementation to allow replacing the Compose*HandshakeMessage functions with custom ones
|
|
4. Refactor the NoiseSession implementation to break Compose*HandshakeMessage out into a separate interface, and implement that interface in a custom struct
|
|
Ideally, we're already set up to do #1, but we'll see how it goes.
|
|
Now is the right time to make changes if we need to, go-i2p is the only consumer of go-i2p right now, we can make our lives as easy as we want to.
|
|
*/
|
|
|
|
// NTCP2Session extends the base noise.NoiseSession with NTCP2-specific functionality
|
|
type NTCP2Session struct {
|
|
*noise.NoiseSession
|
|
paddingStrategy PaddingStrategy
|
|
}
|
|
|
|
type SessionRequest struct {
|
|
ObfuscatedKey []byte // 32 bytes
|
|
Timestamp uint32 // 4 bytes
|
|
Padding []byte // Random padding
|
|
}
|
|
|
|
func (s *NTCP2Session) CreateSessionRequest() (*SessionRequest, error) {
|
|
// Get our ephemeral key pair
|
|
ephemeralKey := make([]byte, 32)
|
|
if _, err := rand.Read(ephemeralKey); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Obfuscate the ephemeral key using Bob's static key
|
|
obfuscatedKey, err := s.ObfuscateEphemeral(ephemeralKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create timestamp (current time in seconds)
|
|
timestamp := uint32(time.Now().Unix())
|
|
|
|
// Add random padding (implementation specific)
|
|
padding := make([]byte, rand.Intn(16)) // Up to 16 bytes of padding
|
|
if _, err := rand.Read(padding); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &SessionRequest{
|
|
ObfuscatedKey: obfuscatedKey,
|
|
Timestamp: timestamp,
|
|
Padding: padding,
|
|
}, nil
|
|
}
|
|
|
|
// NewNTCP2Session creates a new NTCP2 session using the existing noise implementation
|
|
func NewNTCP2Session(noiseConfig router_info.RouterInfo) (*NTCP2Session, error) {
|
|
baseNoiseSession, err := noise.NewNoiseTransportSession(noiseConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &NTCP2Session{
|
|
NoiseSession: baseNoiseSession.(*noise.NoiseSession),
|
|
}, nil
|
|
}
|
|
|
|
type PaddingStrategy interface {
|
|
AddPadding(message []byte) []byte
|
|
RemovePadding(message []byte) []byte
|
|
}
|
|
|
|
// PeerStaticKey is equal to the NTCP2 peer's static public key, found in their router info
|
|
func (s *NTCP2Session) peerStaticKey() ([32]byte, error) {
|
|
for _, addr := range s.RouterInfo.RouterAddresses() {
|
|
transportStyle, err := addr.TransportStyle().Data()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if transportStyle == NTCP_PROTOCOL_NAME {
|
|
return addr.StaticKey()
|
|
}
|
|
}
|
|
return [32]byte{}, fmt.Errorf("Remote static key error")
|
|
}
|
|
|
|
func (s *NTCP2Session) peerStaticIV() ([16]byte, error) {
|
|
for _, addr := range s.RouterInfo.RouterAddresses() {
|
|
transportStyle, err := addr.TransportStyle().Data()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if transportStyle == NTCP_PROTOCOL_NAME {
|
|
return addr.InitializationVector()
|
|
}
|
|
}
|
|
return [16]byte{}, fmt.Errorf("Remote static IV error")
|
|
}
|
|
|
|
// ObfuscateEphemeral implements NTCP2's key obfuscation using AES-256-CBC
|
|
func (s *NTCP2Session) ObfuscateEphemeral(ephemeralKey []byte) ([]byte, error) {
|
|
static, err := s.peerStaticKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
staticIV, err := s.peerStaticIV()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var AESStaticKey *crypto.AESSymmetricKey
|
|
AESStaticKey.Key = static[:]
|
|
AESStaticKey.IV = staticIV[:]
|
|
return obfs.ObfuscateEphemeralKey(ephemeralKey, AESStaticKey)
|
|
}
|
|
|
|
// DeobfuscateEphemeral reverses the key obfuscation
|
|
func (s *NTCP2Session) DeobfuscateEphemeral(obfuscatedEphemeralKey []byte) ([]byte, error) {
|
|
static, err := s.peerStaticKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
staticIV, err := s.peerStaticIV()
|
|
var AESStaticKey *crypto.AESSymmetricKey
|
|
AESStaticKey.Key = static[:]
|
|
AESStaticKey.IV = staticIV[:]
|
|
return obfs.ObfuscateEphemeralKey(obfuscatedEphemeralKey, AESStaticKey)
|
|
}
|