kdf

package
v0.1.59999 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 6 Imported by: 3

README

Key Derivation Functions (KDF)

Consistent key derivation functions for the I2P cryptographic ecosystem.

Overview

This package provides a unified API for deriving cryptographic keys from root secrets using HKDF (HMAC-based Key Derivation Function) as defined in RFC 5869. It ensures that keys are derived consistently across all I2P components with purpose-specific context strings.

Features

  • ✅ Standardized key derivation from root secrets
  • ✅ Type-safe key purpose enumeration
  • ✅ Multiple key derivation support
  • ✅ Secure memory cleanup
  • ✅ Compatible with ECIES, DH, and session keys

Usage

Basic Key Derivation
package main

import (
    "github.com/go-i2p/crypto/kdf"
)

func main() {
    // Assume we have a root key from ECIES or DH key exchange
    var rootKey [32]byte
    // ... obtain root key ...
    
    // Create key derivation context
    kd := kdf.NewKeyDerivation(rootKey)
    defer kd.Zero() // Securely clear when done
    
    // Derive keys for different purposes
    tunnelKey, _ := kd.DeriveForPurpose(kdf.PurposeTunnelEncryption)
    garlicKey, _ := kd.DeriveForPurpose(kdf.PurposeGarlicEncryption)
    tagKey, _ := kd.DeriveForPurpose(kdf.PurposeSessionTag)
}
Session Key Derivation
// Derive standard set of keys for I2P session initialization
kd := kdf.NewKeyDerivation(eciesSharedSecret)

rootKey, symKey, tagKey, err := kd.DeriveSessionKeys()
if err != nil {
    return err
}

// Use keys to initialize ratchets
dhRatchet := ratchet.NewDHRatchet(rootKey, ourPriv, theirPub)
symRatchet := ratchet.NewSymmetricRatchet(symKey)
tagRatchet := ratchet.NewTagRatchet(tagKey)
// Derive multiple keys for complex encryption schemes
kd := kdf.NewKeyDerivation(masterSecret)

keys, _ := kd.DeriveKeys([]byte("Tunnel-Layer-42"), 3)
encryptKey := keys[0]  // For encryption
macKey := keys[1]      // For MAC
ivKey := keys[2]       // For IV generation
Custom Key Derivation
// Derive keys with custom context strings
kd := kdf.NewKeyDerivation(rootKey)

// Application-specific key
appKey, _ := kd.DeriveWithInfo("MyApp-Extension-v1")

// Session-specific key
sessionKey, _ := kd.DeriveWithInfo(fmt.Sprintf("Session-%s", sessionID))

Standard Key Purposes

The package defines standard purposes for I2P protocol components:

Purpose Use Case Info String
PurposeTunnelEncryption Tunnel layer encryption I2P-Tunnel-Encryption-v1
PurposeGarlicEncryption Garlic message encryption I2P-Garlic-Encryption-v1
PurposeSessionTag Session tag generation I2P-Session-Tag-v1
PurposeRatchetChain Ratchet chain keys I2P-Ratchet-Chain-v1
PurposeIVGeneration IV/nonce generation I2P-IV-Generation-v1
PurposeMessageKey Per-message encryption I2P-Message-Key-v1
PurposeHandshake Handshake keys I2P-Handshake-v1

API Reference

Types
KeyPurpose

Enumeration of standard key purposes for I2P components.

KeyDerivation

Main type for deriving keys from a root secret.

Functions
NewKeyDerivation(rootKey [32]byte) *KeyDerivation

Creates a new key derivation context from a 32-byte root key.

Methods
DeriveForPurpose(purpose KeyPurpose) ([32]byte, error)

Derives a single 32-byte key using a standard I2P purpose.

DeriveWithInfo(info string) ([32]byte, error)

Derives a single 32-byte key using a custom info string.

DeriveKeys(info []byte, count int) ([][32]byte, error)

Derives multiple 32-byte keys from the same context.

DeriveSessionKeys() (rootKey, symKey, tagKey [32]byte, err error)

Convenience method to derive the standard set of session keys.

Zero()

Securely clears the root key from memory.

Security Considerations

Root Key Requirements
  • Root keys should be high-entropy secrets (32 bytes minimum)
  • Use cryptographically secure random sources
  • Derive from established key exchange protocols (ECIES, DH)
  • Never reuse root keys across different contexts
Info String Best Practices
  • Use standard purposes when available for consistency
  • Custom info strings should be unique per use case
  • Include version numbers for protocol evolution
  • Prefix with application/component identifier
Key Uniqueness

Keys derived with different purposes or info strings are cryptographically independent:

kd := kdf.NewKeyDerivation(rootKey)

key1, _ := kd.DeriveForPurpose(kdf.PurposeTunnelEncryption)
key2, _ := kd.DeriveForPurpose(kdf.PurposeGarlicEncryption)

// key1 and key2 are completely independent

I2P Protocol Integration

This package is designed for:

  • ECIES-X25519-AEAD-Ratchet: Deriving session keys from shared secrets
  • Tunnel Building: Deriving layer-specific encryption keys
  • Garlic Messages: Deriving message-specific keys
  • Session Management: Consistent key derivation across session types

Testing

Run tests:

go test -v

License

MIT License - See LICENSE file for details

Documentation

Overview

Package kdf provides consistent key derivation functions for the I2P cryptographic ecosystem.

This package standardizes key derivation across all I2P components, ensuring that keys are derived consistently and securely using HKDF (HMAC-based Key Derivation Function) as defined in RFC 5869.

Key Features

  • Unified API for deriving keys from root secrets
  • Standard info strings for different I2P purposes
  • Type-safe key purpose enumeration
  • Multiple key derivation support

Usage Example

// Derive keys from an ECIES shared secret
kd := kdf.NewKeyDerivation(eciesSharedSecret)

// Derive specific purpose keys
tunnelKey, _ := kd.DeriveForPurpose(kdf.PurposeTunnelEncryption)
garlicKey, _ := kd.DeriveForPurpose(kdf.PurposeGarlicEncryption)

// Derive multiple related keys
keys, _ := kd.DeriveKeys([]byte("custom-context"), 3)

Standard Key Purposes

The package defines standard key purposes for I2P protocol components:

  • PurposeTunnelEncryption - Keys for tunnel layer encryption
  • PurposeGarlicEncryption - Keys for garlic message encryption
  • PurposeSessionTag - Keys for session tag generation
  • PurposeRatchetChain - Keys for ratchet chain initialization
  • PurposeIVGeneration - Keys for IV/nonce generation

Security Considerations

  • Root keys should be generated using cryptographically secure random sources
  • Use standard key purposes when possible for consistency
  • Custom info strings should include context-specific prefixes
  • Derived keys should be unique per purpose and context

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidSecret indicates the secret is too short for secure blinding factor derivation
	ErrInvalidSecret = oops.Errorf("secret must be at least 32 bytes")

	// ErrInvalidDateFormat indicates the date string does not match YYYY-MM-DD format
	ErrInvalidDateFormat = oops.Errorf("date must be in YYYY-MM-DD format")

	// ErrInvalidDate indicates the date values are invalid (e.g., Feb 30)
	ErrInvalidDate = oops.Errorf("invalid date values")
)

Functions

func DeriveBlindingFactor added in v0.1.0

func DeriveBlindingFactor(secret []byte, date string) ([32]byte, error)

DeriveBlindingFactor derives a blinding factor (alpha) from a secret and date. This creates a unique per-day blinding factor for EncryptedLeaseSet rotation.

Derivation uses HKDF-SHA256:

  • IKM (input key material): secret (32+ bytes, typically from destination keypair)
  • Salt: date in YYYY-MM-DD format as bytes
  • Info: "i2p-blinding-factor"
  • Output: 64 bytes (reduced to canonical Ed25519 scalar)

The output is a canonical Ed25519 scalar (reduced modulo L), ensuring compatibility with edwards25519.Scalar.SetCanonicalBytes. The derivation process:

  1. HKDF derives 64 bytes from secret + date
  2. SetUniformBytes reduces to canonical scalar (< L)
  3. Returns 32-byte canonical scalar encoding

The same secret + date always produces the same alpha, enabling:

  • Service to create blinded destination for publication
  • Clients to derive the same alpha and verify blinded signatures

Parameters:

  • secret: Secret key material (must be at least 32 bytes, typically from private key)
  • date: Date in "YYYY-MM-DD" format (e.g., "2025-11-24")

Returns:

  • alpha: 32-byte canonical Ed25519 scalar suitable for point blinding
  • error: ErrInvalidSecret if secret is too short, ErrInvalidDateFormat or ErrInvalidDate if date is invalid

Example:

// Derive blinding factor for today
secret := privateKey.Seed() // 32-byte secret from Ed25519 private key
alpha, err := kdf.DeriveBlindingFactor(secret, "2025-11-24")
if err != nil {
    return err
}

// Use alpha to blind public key
blindedPubKey, err := ed25519.BlindPublicKey(pubKey, alpha)

Spec: I2P Proposal 123 Section 4.2

func DeriveBlindingFactorWithTimestamp added in v0.1.0

func DeriveBlindingFactorWithTimestamp(secret []byte, unixTimestamp int64) ([32]byte, error)

DeriveBlindingFactorWithTimestamp is a convenience wrapper that formats a Unix timestamp into YYYY-MM-DD and calls DeriveBlindingFactor.

This is useful when working with Unix timestamps (e.g., from time.Now().Unix()).

Parameters:

  • secret: Secret key material (must be at least 32 bytes)
  • unixTimestamp: Unix timestamp in seconds

Returns:

  • alpha: 32-byte blinding factor
  • error: Same errors as DeriveBlindingFactor

Example:

// Derive blinding factor for current time
now := time.Now().Unix()
alpha, err := kdf.DeriveBlindingFactorWithTimestamp(secret, now)

func FormatDateForBlinding added in v0.1.0

func FormatDateForBlinding(t time.Time) string

FormatDateForBlinding formats a time.Time as YYYY-MM-DD for blinding factor derivation. This is a convenience function for consistent date formatting.

Example:

date := kdf.FormatDateForBlinding(time.Now())
alpha, err := kdf.DeriveBlindingFactor(secret, date)

func GetCurrentBlindingDate added in v0.1.0

func GetCurrentBlindingDate() string

GetCurrentBlindingDate returns today's date in UTC formatted for blinding. This is equivalent to FormatDateForBlinding(time.Now().UTC()).

Example:

today := kdf.GetCurrentBlindingDate()
alpha, err := kdf.DeriveBlindingFactor(secret, today)

func StandardHKDF added in v0.1.5

func StandardHKDF(salt, ikm, info []byte, length int) ([]byte, error)

StandardHKDF performs a one-shot RFC 5869 HKDF-SHA256 key derivation. This is a convenience function for callers that need a single HKDF invocation without constructing a KeyDerivation context. It is the canonical implementation of raw HKDF extract-and-expand in the crypto layer, replacing ad-hoc inline implementations found in higher-level packages (e.g., noise handshakes, NTCP2).

Parameters:

  • salt: Optional salt value (can be nil; HKDF will use a zero-filled salt)
  • ikm: Input key material (must not be empty)
  • info: Optional context/application-specific info (can be nil)
  • length: Desired output length in bytes (must be > 0; max 255*32 per RFC 5869)

Returns:

  • []byte: Derived key material of the requested length
  • error: Any error during derivation

Example usage:

derived, err := kdf.StandardHKDF(salt, sharedSecret, []byte("NTCP2-KDF"), 32)
if err != nil {
    return err
}

Types

type KeyDerivation

type KeyDerivation struct {
	// contains filtered or unexported fields
}

KeyDerivation provides consistent key derivation from a root key. All derived keys use HKDF-SHA256 with purpose-specific info strings.

func NewKeyDerivation

func NewKeyDerivation(rootKey [32]byte) *KeyDerivation

NewKeyDerivation creates a new key derivation context from a root key. The root key should be a high-entropy secret such as:

  • ECIES shared secret
  • Master session key
  • DH shared secret

Parameters:

  • rootKey: A 32-byte root key

Returns:

  • *KeyDerivation: The key derivation context

Example:

// From ECIES shared secret
kd := kdf.NewKeyDerivation(eciesSharedSecret)

// From DH key agreement
sharedSecret, _ := dhPrivate.SharedKey(dhPublic)
kd := kdf.NewKeyDerivation([32]byte(sharedSecret))

func (*KeyDerivation) DeriveForPurpose

func (kd *KeyDerivation) DeriveForPurpose(purpose KeyPurpose) ([32]byte, error)

DeriveForPurpose derives a single 32-byte key for a specific I2P purpose. This uses standard info strings defined for each purpose.

Parameters:

  • purpose: The intended use of the derived key

Returns:

  • [32]byte: The derived key
  • error: Any error during derivation

Example:

kd := kdf.NewKeyDerivation(rootKey)

// Derive keys for different purposes
tunnelKey, _ := kd.DeriveForPurpose(kdf.PurposeTunnelEncryption)
garlicKey, _ := kd.DeriveForPurpose(kdf.PurposeGarlicEncryption)
tagKey, _ := kd.DeriveForPurpose(kdf.PurposeSessionTag)

func (*KeyDerivation) DeriveKeys

func (kd *KeyDerivation) DeriveKeys(info []byte, count int) ([][32]byte, error)

DeriveKeys derives multiple 32-byte keys from the same context. This is useful when you need several related keys (e.g., encrypt + MAC + IV).

Parameters:

  • info: Context-specific info string
  • count: Number of keys to derive

Returns:

  • [][32]byte: Slice of derived keys
  • error: Any error during derivation

Example:

// Derive 3 keys for encryption, MAC, and IV generation
keys, _ := kd.DeriveKeys([]byte("Tunnel-Layer-42"), 3)
encryptKey := keys[0]
macKey := keys[1]
ivKey := keys[2]

func (*KeyDerivation) DeriveSessionKeys

func (kd *KeyDerivation) DeriveSessionKeys() (rootKey, symKey, tagKey [32]byte, err error)

DeriveSessionKeys is a convenience function that derives the standard set of keys needed for an I2P session: ratchet root key, symmetric chain key, and tag chain key.

This is equivalent to DeriveKeys() but with semantic naming for session initialization.

Returns:

  • rootKey: Key for DH ratchet initialization
  • symKey: Key for symmetric ratchet chain
  • tagKey: Key for session tag ratchet
  • error: Any error during derivation

Example:

kd := kdf.NewKeyDerivation(eciesSharedSecret)
rootKey, symKey, tagKey, err := kd.DeriveSessionKeys()
if err != nil {
    return err
}

// Initialize session ratchets
dhRatchet := ratchet.NewDHRatchet(rootKey, ourPriv, theirPub)
symRatchet := ratchet.NewSymmetricRatchet(symKey)
tagRatchet := ratchet.NewTagRatchet(tagKey)

func (*KeyDerivation) DeriveWithInfo

func (kd *KeyDerivation) DeriveWithInfo(info string) ([32]byte, error)

DeriveWithInfo derives a single 32-byte key using a custom info string. Use this when you need non-standard key derivation contexts.

Parameters:

  • info: Context-specific info string (should be unique per use case)

Returns:

  • [32]byte: The derived key
  • error: Any error during derivation

Example:

// Derive key for custom protocol extension
customKey, _ := kd.DeriveWithInfo("MyApp-Extension-v1")

// Derive key with session-specific context
sessionKey, _ := kd.DeriveWithInfo(fmt.Sprintf("Session-%s", sessionID))

func (*KeyDerivation) Zero

func (kd *KeyDerivation) Zero()

Zero securely clears the root key from memory. Call this when the KeyDerivation instance is no longer needed.

type KeyPurpose

type KeyPurpose int

KeyPurpose identifies what a derived key will be used for. This ensures keys are derived with purpose-specific context strings.

const (
	// PurposeTunnelEncryption is for deriving tunnel layer encryption keys
	PurposeTunnelEncryption KeyPurpose = iota

	// PurposeGarlicEncryption is for deriving garlic message encryption keys
	PurposeGarlicEncryption

	// PurposeSessionTag is for deriving session tag generation keys
	PurposeSessionTag

	// PurposeRatchetChain is for deriving ratchet chain keys
	PurposeRatchetChain

	// PurposeIVGeneration is for deriving IV/nonce generation keys
	PurposeIVGeneration

	// PurposeMessageKey is for deriving per-message encryption keys
	PurposeMessageKey

	// PurposeHandshake is for deriving handshake-related keys
	PurposeHandshake

	// PurposeAttachPayload is for deriving keys for attached payload encryption
	// in New Session (NS) and New Session Reply (NSR) messages.
	// Java I2P compatible - uses "AttachPayloadKDF" info string.
	PurposeAttachPayload

	// PurposeEncryptedLeaseSetEncryption is used when deriving symmetric
	// encryption keys for EncryptedLeaseSet inner data.
	//
	// The encryption key is derived from:
	//   - ECDH shared secret (X25519)
	//   - EncryptedLeaseSet cookie (32 bytes)
	//   - This purpose constant
	//
	// Info string: "i2p-encrypted-leaseset-encryption"
	//
	// Spec: I2P Proposal 123 - Encrypted LeaseSet
	PurposeEncryptedLeaseSetEncryption
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL