Files
go-i2p/lib/transport/ntcp/aead.go

126 lines
4.1 KiB
Go

package ntcp
import (
"encoding/binary"
"github.com/samber/oops"
"golang.org/x/crypto/chacha20poly1305"
)
// AEADOperator defines the interface for AEAD operations in the NTCP2 protocol
type AEADOperator interface {
// EncryptWithAssociatedData encrypts data using the provided key and associated data
EncryptWithAssociatedData(key, data, associatedData []byte, nonceCounter uint64) ([]byte, error)
// DecryptWithAssociatedData decrypts data using the provided key and associated data
DecryptWithAssociatedData(key, data, associatedData []byte, nonceCounter uint64) ([]byte, error)
// EncryptWithDerivedKey encrypts data, deriving the key from raw key material first
EncryptWithDerivedKey(keyMaterial, data, associatedData []byte, nonceCounter uint64) ([]byte, error)
// DecryptWithDerivedKey decrypts data, deriving the key from raw key material first
DecryptWithDerivedKey(keyMaterial, data, associatedData []byte, nonceCounter uint64) ([]byte, error)
}
var _ AEADOperator = (*NTCP2Session)(nil)
// PerformAEADOperation handles both encryption and decryption using ChaCha20-Poly1305
func (c *NTCP2Session) PerformAEADOperation(
keyMaterial []byte, // Raw key material to derive key from
data []byte, // Data to encrypt/decrypt
associatedData []byte, // Associated data for AEAD
nonceCounter uint64, // Nonce counter (0 for first message)
encrypt bool, // true for encrypt, false for decrypt
) ([]byte, error) {
// 1. Derive key
key, err := c.deriveChacha20Key(keyMaterial)
if err != nil {
return nil, oops.Errorf("failed to derive ChaCha20 key: %w", err)
}
// 2. Create cipher
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, oops.Errorf("failed to create AEAD cipher: %w", err)
}
// 3. Create nonce (all zeros for first message, otherwise based on counter)
nonce := make([]byte, 12)
if nonceCounter > 0 {
binary.BigEndian.PutUint64(nonce[4:], nonceCounter)
}
// 4. Perform operation
var result []byte
var opErr error
if encrypt {
result = aead.Seal(nil, nonce, data, associatedData)
} else {
result, opErr = aead.Open(nil, nonce, data, associatedData)
if opErr != nil {
return nil, oops.Errorf("AEAD authentication failed: %w", opErr)
}
}
return result, nil
}
// EncryptWithAssociatedData encrypts data using ChaCha20-Poly1305 with the provided key and associated data
func (c *NTCP2Session) EncryptWithAssociatedData(
key []byte,
data []byte,
associatedData []byte,
nonceCounter uint64,
) ([]byte, error) {
return c.PerformAEADOperation(key, data, associatedData, nonceCounter, true)
}
// DecryptWithAssociatedData decrypts data using ChaCha20-Poly1305 with the provided key and associated data
func (c *NTCP2Session) DecryptWithAssociatedData(
key []byte,
data []byte,
associatedData []byte,
nonceCounter uint64,
) ([]byte, error) {
return c.PerformAEADOperation(key, data, associatedData, nonceCounter, false)
}
// PerformAEADWithDerivedKey performs AEAD operation, deriving the key from raw key material first
func (c *NTCP2Session) PerformAEADWithDerivedKey(
keyMaterial []byte, // Raw key material to derive key from
data []byte,
associatedData []byte,
nonceCounter uint64,
encrypt bool,
) ([]byte, error) {
// 1. Derive key using KDF
key, err := c.deriveChacha20Key(keyMaterial)
if err != nil {
return nil, oops.Errorf("failed to derive ChaCha20 key: %w", err)
}
// 2. Perform the AEAD operation
return c.PerformAEADOperation(key, data, associatedData, nonceCounter, encrypt)
}
// EncryptWithDerivedKey encrypts data, deriving the key from raw key material first
func (c *NTCP2Session) EncryptWithDerivedKey(
keyMaterial []byte,
data []byte,
associatedData []byte,
nonceCounter uint64,
) ([]byte, error) {
return c.PerformAEADWithDerivedKey(keyMaterial, data, associatedData, nonceCounter, true)
}
// DecryptWithDerivedKey decrypts data, deriving the key from raw key material first
func (c *NTCP2Session) DecryptWithDerivedKey(
keyMaterial []byte,
data []byte,
associatedData []byte,
nonceCounter uint64,
) ([]byte, error) {
return c.PerformAEADWithDerivedKey(keyMaterial, data, associatedData, nonceCounter, false)
}