More migration to processors

This commit is contained in:
eyedeekay
2025-05-10 21:55:06 -04:00
parent ebdd1ae0aa
commit 89e53fdb9f
4 changed files with 236 additions and 21 deletions

View File

@ -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")

View 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
}

View 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)
}

View File

@ -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 {