mirror of
https://github.com/go-i2p/go-i2p.git
synced 2025-07-02 12:43:41 -04:00
More migration to processors
This commit is contained in:
@ -5,9 +5,67 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-i2p/go-i2p/lib/transport/ntcp/handshake"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/ntcp/messages"
|
||||
"github.com/samber/oops"
|
||||
)
|
||||
|
||||
// CreateHandshakeProcessors initializes all the handshake message processors
|
||||
func (s *NTCP2Session) CreateHandshakeProcessors() {
|
||||
s.Processors = map[messages.MessageType]handshake.HandshakeMessageProcessor{
|
||||
messages.MessageTypeSessionRequest: &SessionRequestProcessor{NTCP2Session: s},
|
||||
messages.MessageTypeSessionCreated: &SessionCreatedProcessor{NTCP2Session: s},
|
||||
messages.MessageTypeSessionConfirmed: &SessionConfirmedProcessor{NTCP2Session: s},
|
||||
}
|
||||
}
|
||||
|
||||
// GetProcessor returns the appropriate processor for a message type
|
||||
func (s *NTCP2Session) GetProcessor(messageType messages.MessageType) (handshake.HandshakeMessageProcessor, error) {
|
||||
processor, exists := s.Processors[messageType]
|
||||
if !exists {
|
||||
return nil, oops.Errorf("no processor for message type: %v", messageType)
|
||||
}
|
||||
return processor, nil
|
||||
}
|
||||
|
||||
// PerformClientHandshake performs the NTCP2 handshake as a client
|
||||
func (s *NTCP2Session) PerformClientHandshake(conn net.Conn) error {
|
||||
// Initialize processors
|
||||
s.CreateHandshakeProcessors()
|
||||
|
||||
// Get request processor
|
||||
processor, err := s.GetProcessor(messages.MessageTypeSessionRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create and send SessionRequest
|
||||
msg, err := processor.CreateMessage(s.HandshakeState.(*handshake.HandshakeState))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Obfuscate key
|
||||
obfuscatedKey, err := processor.ObfuscateKey(msg, s.HandshakeState.(*handshake.HandshakeState))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Encrypt payload
|
||||
//encryptedPayload
|
||||
_, err = processor.EncryptPayload(msg, obfuscatedKey, s.HandshakeState.(*handshake.HandshakeState))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write message to connection
|
||||
// ... implementation ...
|
||||
|
||||
// Continue with rest of handshake
|
||||
// ... implementation ...
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveSessionRequest processes Message 1 (SessionRequest) from remote
|
||||
func (c *NTCP2Session) receiveSessionRequest(conn net.Conn, hs *handshake.HandshakeState) error {
|
||||
log.Debugf("NTCP2: Processing incoming SessionRequest message")
|
||||
|
152
lib/transport/ntcp/kdf/kdf.go
Normal file
152
lib/transport/ntcp/kdf/kdf.go
Normal file
@ -0,0 +1,152 @@
|
||||
package kdf
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/samber/oops"
|
||||
)
|
||||
|
||||
// NTCP2KDF handles key derivation functions for the NTCP2 protocol
|
||||
// according to the Noise_XK_25519_ChaChaPoly_SHA256 specification with
|
||||
// I2P-specific customizations.
|
||||
type NTCP2KDF struct {
|
||||
// ChainingKey is used in the key derivation chain
|
||||
ChainingKey []byte
|
||||
// HandshakeHash is the cumulative hash of handshake data
|
||||
HandshakeHash []byte
|
||||
}
|
||||
|
||||
// NewNTCP2KDF creates a new KDF context for NTCP2 with initial
|
||||
// protocol name as defined in the I2P spec.
|
||||
func NewNTCP2KDF() *NTCP2KDF {
|
||||
// Initialize with protocol name as per Noise protocol
|
||||
protocolName := []byte("Noise_XK_25519_ChaChaPoly_SHA256")
|
||||
h := sha256.New()
|
||||
h.Write(protocolName)
|
||||
initialHash := h.Sum(nil)
|
||||
|
||||
return &NTCP2KDF{
|
||||
ChainingKey: initialHash,
|
||||
HandshakeHash: initialHash,
|
||||
}
|
||||
}
|
||||
|
||||
// MixKey derives a new chaining key and encryption key from DH output
|
||||
// following the Noise protocol specification.
|
||||
func (k *NTCP2KDF) MixKey(dhOutput []byte) (encryptionKey []byte, err error) {
|
||||
if len(dhOutput) != 32 {
|
||||
return nil, oops.Errorf("invalid DH output length: expected 32, got %d", len(dhOutput))
|
||||
}
|
||||
|
||||
// Generate a temp key from the chaining key and DH result
|
||||
tempKey := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := tempKey.Write(dhOutput); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
tempKeyBytes := tempKey.Sum(nil)
|
||||
|
||||
// Set new chaining key (output 1)
|
||||
ckMac := hmac.New(sha256.New, tempKeyBytes)
|
||||
if _, err := ckMac.Write([]byte{0x01}); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
k.ChainingKey = ckMac.Sum(nil)
|
||||
|
||||
// Generate encryption key (output 2)
|
||||
keyMac := hmac.New(sha256.New, tempKeyBytes)
|
||||
if _, err := keyMac.Write([]byte{0x02}); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
encryptionKey = keyMac.Sum(nil)
|
||||
|
||||
return encryptionKey, nil
|
||||
}
|
||||
|
||||
// MixHash updates the handshake hash with new data
|
||||
// according to the Noise protocol pattern.
|
||||
func (k *NTCP2KDF) MixHash(data []byte) error {
|
||||
h := sha256.New()
|
||||
if _, err := h.Write(k.HandshakeHash); err != nil {
|
||||
return oops.Errorf("hash update failed: %w", err)
|
||||
}
|
||||
if _, err := h.Write(data); err != nil {
|
||||
return oops.Errorf("hash update failed: %w", err)
|
||||
}
|
||||
k.HandshakeHash = h.Sum(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeriveKeys performs Split() operation to derive final session keys
|
||||
// for bidirectional communication.
|
||||
func (k *NTCP2KDF) DeriveKeys() (keyAB, keyBA []byte, err error) {
|
||||
// Generate key for Alice->Bob
|
||||
keyABMac := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := keyABMac.Write([]byte{0x01}); err != nil {
|
||||
return nil, nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
keyAB = keyABMac.Sum(nil)
|
||||
|
||||
// Generate key for Bob->Alice
|
||||
keyBAMac := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := keyBAMac.Write([]byte{0x02}); err != nil {
|
||||
return nil, nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
keyBA = keyBAMac.Sum(nil)
|
||||
|
||||
return keyAB, keyBA, nil
|
||||
}
|
||||
|
||||
// DeriveHandshakeMessageKey derives a key for a specific handshake message
|
||||
// used during the different phases of the NTCP2 handshake.
|
||||
func (k *NTCP2KDF) DeriveHandshakeMessageKey(messageNum uint8) ([]byte, error) {
|
||||
mac := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := mac.Write([]byte{messageNum}); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
return mac.Sum(nil), nil
|
||||
}
|
||||
|
||||
// DeriveSipHashKey derives a key for SipHash length obfuscation
|
||||
// used specifically in NTCP2 for frame length obfuscation.
|
||||
func (k *NTCP2KDF) DeriveSipHashKey() ([]byte, error) {
|
||||
// "ask" key derivation for SipHash
|
||||
askMac := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := askMac.Write([]byte("ask")); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
if _, err := askMac.Write([]byte{0x01}); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
askMaster := askMac.Sum(nil)
|
||||
|
||||
// SipHash key derivation
|
||||
tempMac := hmac.New(sha256.New, askMaster)
|
||||
if _, err := tempMac.Write(k.HandshakeHash); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
if _, err := tempMac.Write([]byte("siphash")); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
tempKey := tempMac.Sum(nil)
|
||||
|
||||
sipMac := hmac.New(sha256.New, tempKey)
|
||||
if _, err := sipMac.Write([]byte{0x01}); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
|
||||
// SipHash requires 16 bytes (128 bits)
|
||||
return sipMac.Sum(nil)[:16], nil
|
||||
}
|
||||
|
||||
// DeriveFramingKey derives the key used for frame obfuscation
|
||||
// in the data phase of NTCP2.
|
||||
func (k *NTCP2KDF) DeriveFramingKey() ([]byte, error) {
|
||||
// Frame key derivation
|
||||
frameMac := hmac.New(sha256.New, k.ChainingKey)
|
||||
if _, err := frameMac.Write([]byte("frame")); err != nil {
|
||||
return nil, oops.Errorf("HMAC write failed: %w", err)
|
||||
}
|
||||
|
||||
return frameMac.Sum(nil), nil
|
||||
}
|
23
lib/transport/ntcp/obfs.go
Normal file
23
lib/transport/ntcp/obfs.go
Normal file
@ -0,0 +1,23 @@
|
||||
package ntcp
|
||||
|
||||
import "github.com/go-i2p/go-i2p/lib/transport/obfs"
|
||||
|
||||
// ObfuscateEphemeral implements NTCP2's key obfuscation using AES-256-CBC
|
||||
func (s *NTCP2Session) ObfuscateEphemeral(ephemeralKey []byte) ([]byte, error) {
|
||||
AESStaticKey, err := s.buildAesStaticKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obfs.ObfuscateEphemeralKey(ephemeralKey, AESStaticKey)
|
||||
}
|
||||
|
||||
// DeobfuscateEphemeral reverses the key obfuscation
|
||||
func (s *NTCP2Session) DeobfuscateEphemeral(obfuscatedEphemeralKey []byte) ([]byte, error) {
|
||||
AESStaticKey, err := s.buildAesStaticKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obfs.DeobfuscateEphemeralKey(obfuscatedEphemeralKey, AESStaticKey)
|
||||
}
|
@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-i2p/go-i2p/lib/transport/noise"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/ntcp/handshake"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/ntcp/kdf"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/obfs"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/ntcp/messages"
|
||||
"github.com/go-i2p/go-i2p/lib/transport/padding"
|
||||
"github.com/go-i2p/go-i2p/lib/util/time/sntp"
|
||||
|
||||
@ -44,6 +44,8 @@ type NTCP2Session struct {
|
||||
// Key for frame obfuscation in data phase
|
||||
framingKey []byte
|
||||
paddingStrategy padding.PaddingStrategy
|
||||
// Processors for handling handshake messages
|
||||
Processors map[messages.MessageType]handshake.HandshakeMessageProcessor
|
||||
}
|
||||
|
||||
// NewNTCP2Session creates a new NTCP2 session using the existing noise implementation
|
||||
@ -102,26 +104,6 @@ func (s *NTCP2Session) peerStaticIV() ([16]byte, error) {
|
||||
return [16]byte{}, oops.Errorf("Remote static IV error")
|
||||
}
|
||||
|
||||
// ObfuscateEphemeral implements NTCP2's key obfuscation using AES-256-CBC
|
||||
func (s *NTCP2Session) ObfuscateEphemeral(ephemeralKey []byte) ([]byte, error) {
|
||||
AESStaticKey, err := s.buildAesStaticKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obfs.ObfuscateEphemeralKey(ephemeralKey, AESStaticKey)
|
||||
}
|
||||
|
||||
// DeobfuscateEphemeral reverses the key obfuscation
|
||||
func (s *NTCP2Session) DeobfuscateEphemeral(obfuscatedEphemeralKey []byte) ([]byte, error) {
|
||||
AESStaticKey, err := s.buildAesStaticKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obfs.DeobfuscateEphemeralKey(obfuscatedEphemeralKey, AESStaticKey)
|
||||
}
|
||||
|
||||
func (s *NTCP2Session) buildAesStaticKey() (*aes.AESSymmetricKey, error) {
|
||||
staticKey, err := s.peerStaticKey()
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user