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 ¶
- Variables
- func DeriveBlindingFactor(secret []byte, date string) ([32]byte, error)
- func DeriveBlindingFactorWithTimestamp(secret []byte, unixTimestamp int64) ([32]byte, error)
- func FormatDateForBlinding(t time.Time) string
- func GetCurrentBlindingDate() string
- func StandardHKDF(salt, ikm, info []byte, length int) ([]byte, error)
- type KeyDerivation
- func (kd *KeyDerivation) DeriveForPurpose(purpose KeyPurpose) ([32]byte, error)
- func (kd *KeyDerivation) DeriveKeys(info []byte, count int) ([][32]byte, error)
- func (kd *KeyDerivation) DeriveSessionKeys() (rootKey, symKey, tagKey [32]byte, err error)
- func (kd *KeyDerivation) DeriveWithInfo(info string) ([32]byte, error)
- func (kd *KeyDerivation) Zero()
- type KeyPurpose
Constants ¶
This section is empty.
Variables ¶
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
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:
- HKDF derives 64 bytes from secret + date
- SetUniformBytes reduces to canonical scalar (< L)
- 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
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
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
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 )