Unified KDF Interface Documentation

Side-channel claim limit. Nothing on this page claims constant-time behaviour or side-channel resistance for any target: the side-channel harness covers nothing (finding A48) and no measurement exists. The Rust and C cores are designed for constant time and unmeasured; managed runtimes (JIT, GC, bounds checks) make no timing claim. Where this page names constant-time or side-channel techniques, in prose or in code, they are design intent or recommendations, not measured properties.

This document defines the unified interface for Key Derivation Functions (KDFs) across all MetaMUI implementations.

Common Interface Pattern

All KDF implementations follow a consistent pattern across languages:

Class/Module Structure

KDF
├── Static Methods (Primary Interface)
│   ├── derive() or hash()
│   ├── verify() (for password hashing)
│   └── Convenience methods
├── Configuration
│   └── Parameters with sensible defaults
└── Error Handling
    └── Consistent error types

PBKDF2 Interface

Rust

use metamui_pbkdf2::PBKDF2;

// Primary methods
PBKDF2::pbkdf2_hmac_sha256(password: &[u8], salt: &[u8], iterations: u32, key_length: usize) -> Result<Vec<u8>, PBKDF2Error>
PBKDF2::pbkdf2_hmac_sha512(password: &[u8], salt: &[u8], iterations: u32, key_length: usize) -> Result<Vec<u8>, PBKDF2Error>

// BIP39 convenience
PBKDF2::bip39_mnemonic_to_seed(mnemonic: &str, passphrase: &str) -> Vec<u8>

Python

from metamui_crypto.pbkdf2 import PBKDF2

# Primary methods
PBKDF2.derive_key_sha256(password: Union[str, bytes], salt: Union[str, bytes], iterations: int, key_length: int) -> bytes
PBKDF2.derive_key_sha512(password: Union[str, bytes], salt: Union[str, bytes], iterations: int, key_length: int) -> bytes

# BIP39 convenience
PBKDF2.bip39_mnemonic_to_seed(mnemonic: str, passphrase: str = "") -> bytes

TypeScript

import { PBKDF2 } from '@metamui/crypto-pbkdf2';

// Primary methods
PBKDF2.sha256(password: Uint8Array, salt: Uint8Array, iterations: number, keyLength: number): Uint8Array
PBKDF2.sha512(password: Uint8Array, salt: Uint8Array, iterations: number, keyLength: number): Uint8Array

// Generic method
PBKDF2.derive(password: Uint8Array, salt: Uint8Array, iterations: number, keyLength: number, algorithm: 'sha256' | 'sha512'): Uint8Array

Swift

import MetaMUIPBKDF2

// Primary methods
PBKDF2.deriveKey(password: Data, salt: Data, iterations: UInt32, keyLength: Int, variant: .sha256) throws -> Data
PBKDF2.deriveKey(password: Data, salt: Data, iterations: UInt32, keyLength: Int, variant: .sha512) throws -> Data

// BIP39 convenience
PBKDF2.bip39MnemonicToSeed(mnemonic: String, passphrase: String = "") -> Data

Kotlin

import id.metamui.crypto.pbkdf2.PBKDF2

// Primary methods
PBKDF2.deriveKeySha256(password: ByteArray, salt: ByteArray, iterations: Int, keyLength: Int): ByteArray
PBKDF2.deriveKeySha512(password: ByteArray, salt: ByteArray, iterations: Int, keyLength: Int): ByteArray

// BIP39 convenience
PBKDF2.bip39MnemonicToSeed(mnemonic: String, passphrase: String = ""): ByteArray

Argon2 Interface

Rust

use metamui_argon2::{MetaMUIArgon2, Argon2Config, Argon2Variant};

// Create hasher with config
let config = Argon2Config {
    variant: Argon2Variant::Argon2id,
    memory_cost: 19456,
    time_cost: 2,
    parallelism: 1,
    output_length: 32,
    ..Default::default()
};
let argon2 = MetaMUIArgon2::with_config(config)?;

// Hash password
let hash = argon2.hash_password(password)?;

// Verify password
let valid = argon2.verify_password(password, &hash);

// Derive key
let key = argon2.derive_key(password, salt, output_length)?;

Python

from metamui_crypto.argon2 import Argon2, Argon2Type

argon2 = Argon2()

# Hash with parameters
hash_output = argon2.hash(
    password="password",
    salt=b"somesalt12345678",
    time_cost=2,
    memory_cost=19456,
    parallelism=1,
    tag_length=32,
    argon2_type=Argon2Type.ARGON2ID
)

# Verify
valid = argon2.verify(password, salt, hash_to_verify, **same_params)

TypeScript

import { Argon2, Argon2Type } from '@metamui/crypto-argon2';

const argon2 = new Argon2();

// Hash with parameters
const hash = argon2.hash(password, salt, {
    type: Argon2Type.Argon2id,
    timeCost: 2,
    memoryCost: 19456,
    parallelism: 1,
    tagLength: 32
});

// Verify
const valid = argon2.verify(password, salt, hash, params);

Swift

import MetaMUIArgon2

// Hash with parameters
let hash = try Argon2.hash(
    password: password,
    salt: salt,
    type: .argon2id,
    iterations: 2,
    memory: 19456,
    parallelism: 1,
    tagLength: 32
)

// Verify
let valid = Argon2.verify(password: password, hash: hash)

Kotlin

import id.metamui.crypto.argon2.Argon2

// Hash with parameters
val hash = Argon2.hash(
    password = password,
    salt = salt,
    variant = Argon2.Variant.ARGON2ID,
    memoryCost = 19456,
    timeCost = 2,
    parallelism = 1,
    outputLength = 32
)

// Verify
val valid = Argon2.verify(password, hash)

HKDF Interface

Rust

use metamui_hkdf::{MetaMUIHkdfSha256, MetaMUIHkdfSha512};

// Extract-and-Expand
let result = MetaMUIHkdfSha256::derive(ikm, salt, info, output_length)?;

// Separate Extract/Expand
let prk = MetaMUIHkdfSha256::extract(ikm, salt);
let okm = MetaMUIHkdfSha256::expand(&prk, info, output_length)?;

Python

from metamui_crypto.hkdf import MetaMUIHkdf

hkdf = MetaMUIHkdf()

# SHA-256
output = hkdf.hkdf_sha256(ikm, salt, info, output_length)

# SHA-512
output = hkdf.hkdf_sha512(ikm, salt, info, output_length)

# Extract only
prk = hkdf.extract_sha256(salt, ikm)

TypeScript

import { HKDF } from '@metamui/crypto-utilities';

const hkdf = new HKDF();

// SHA-256
const output = hkdf.hkdfSha256(ikm, salt, info, outputLength);

// SHA-512
const output = hkdf.hkdfSha512(ikm, salt, info, outputLength);

Swift

import MetaMUIHkdf

// SHA-256
let output = try Hkdf.deriveKey(
    ikm: ikm,
    salt: salt,
    info: info,
    outputLength: outputLength,
    variant: .sha256
)

Kotlin

import id.metamui.crypto.hkdf.HKDF

// SHA-256
val output = HKDF.deriveKeySha256(ikm, salt, info, outputLength)

// SHA-512
val output = HKDF.deriveKeySha512(ikm, salt, info, outputLength)

Error Handling

Common Error Types

All implementations provide these error types:

  1. InvalidParameters - Invalid input parameters (iterations, length, etc.)
  2. InvalidKey - Key-related errors
  3. DerivationFailed - KDF operation failed
  4. VerificationFailed - Password verification failed (Argon2)

Error Handling Examples

Rust

match PBKDF2::pbkdf2_hmac_sha256(password, salt, 0, 32) {
    Ok(key) => // use key,
    Err(PBKDF2Error::InvalidIterations) => // handle error,
    Err(e) => // handle other errors
}

Python

try:
    key = PBKDF2.derive_key_sha256(password, salt, 0, 32)
except InvalidIterationsError as e:
    # handle invalid iterations
except PBKDF2Error as e:
    # handle other errors

TypeScript

try {
    const key = PBKDF2.sha256(password, salt, 0, 32);
} catch (e) {
    if (e instanceof InvalidIterationsError) {
        // handle invalid iterations
    }
}

Best Practices

  1. Input Validation
    • Always validate parameters before processing
    • Enforce minimum security requirements
    • Provide clear error messages
  2. Memory Security
    • Clear sensitive data after use
    • Use secure memory allocation where available
    • Implement constant-time operations for password verification
  3. Default Parameters
    • Provide secure defaults
    • Allow customization for specific use cases
    • Document recommended values
  4. Type Safety
    • Use strong typing where available
    • Accept both string and byte inputs where appropriate
    • Return consistent types across methods
  5. Performance
    • Implement HMAC key caching for PBKDF2
    • Use optimized hash implementations
    • Consider parallelism for Argon2

Migration Guide

From External Libraries

Rust (from pbkdf2 crate)

// Before
use pbkdf2::pbkdf2_hmac;
pbkdf2_hmac::<Sha256>(password, salt, iterations, &mut output);

// After
use metamui_pbkdf2::PBKDF2;
let output = PBKDF2::pbkdf2_hmac_sha256(password, salt, iterations, length)?;

Python (from hashlib.pbkdf2_hmac)

# Before
import hashlib
key = hashlib.pbkdf2_hmac('sha256', password, salt, iterations)

# After
from metamui_crypto.pbkdf2 import PBKDF2
key = PBKDF2.derive_key_sha256(password, salt, iterations, 32)

TypeScript (from crypto.pbkdf2)

// Before
import { pbkdf2Sync } from 'crypto';
const key = pbkdf2Sync(password, salt, iterations, keylen, 'sha256');

// After
import { PBKDF2 } from '@metamui/crypto-pbkdf2';
const key = PBKDF2.sha256(password, salt, iterations, keylen);

Testing

All implementations should include:

  1. Unit Tests
    • Test vectors from RFCs
    • Edge cases (empty inputs, maximum lengths)
    • Error conditions
  2. Cross-Language Tests
    • Shared test vectors
    • Compatibility verification
    • Performance benchmarks
  3. Security Tests
    • Timing attack resistance
    • Memory clearing verification
    • Parameter validation

References