Skip to main content
TAPLE Core supports multiple cryptographic schemes for key generation, digital signatures, and content addressing.

Key Derivation Algorithms

TAPLE supports multiple key derivation algorithms for node identity and subject ownership.

KeyDerivator Enum

From the codebase, TAPLE supports these key derivation schemes:
pub enum KeyDerivator {
    Ed25519,
    Secp256k1,
}

Ed25519 (Default)

Edwards-curve Digital Signature Algorithm using Curve25519. Characteristics:
  • Fast signature generation and verification
  • Small signatures (64 bytes)
  • Deterministic (same message always produces same signature)
  • Widely supported and audited
Use cases:
  • Node identity keys
  • Subject ownership keys
  • Protocol message signing

Secp256k1 (Optional)

Elliptic Curve Digital Signature Algorithm used by Bitcoin and Ethereum. Characteristics:
  • Compatible with blockchain ecosystems
  • Recoverable signatures
  • Slightly slower than Ed25519
Use cases:
  • Blockchain integration
  • Cross-chain identity
  • Ethereum-compatible addresses
Secp256k1 support requires the secp256k1 feature flag. Enable it in your Cargo.toml:
taple-core = { version = "*", features = ["secp256k1"] }

KeyPair Implementation

The KeyPair enum wraps different cryptographic key types.

KeyPair Structure

From core/src/commons/crypto/mod.rs:24-49:
pub enum KeyPair {
    Ed25519(Ed25519KeyPair),
    #[cfg(feature = "secp256k1")]
    Secp256k1(Secp256k1KeyPair),
}

impl KeyPair {
    pub fn get_key_derivator(&self) -> KeyDerivator {
        match self {
            KeyPair::Ed25519(_) => KeyDerivator::Ed25519,
            KeyPair::Secp256k1(_) => KeyDerivator::Secp256k1,
        }
    }

    pub fn from_hex(derivator: &KeyDerivator, hex_key: &str) -> Result<KeyPair, Error> {
        match derivator {
            KeyDerivator::Ed25519 => Ok(KeyPair::Ed25519(Ed25519KeyPair::from_secret_key(
                &hex::decode(hex_key).unwrap(),
            ))),
            KeyDerivator::Secp256k1 => Ok(KeyPair::Secp256k1(Secp256k1KeyPair::from_secret_key(
                &hex::decode(hex_key).unwrap(),
            ))),
        }
    }
}

Key Generation

KeyGenerator Trait

From core/src/commons/crypto/mod.rs:88-113:
pub trait KeyGenerator: KeyMaterial {
    /// Generates random keys
    fn new() -> Self
    where
        Self: Sized,
    {
        Self::from_seed(vec![].as_slice())
    }

    /// Generates keys deterministically using a given seed
    fn from_seed(seed: &[u8]) -> Self
    where
        Self: Sized;

    /// Generates keys from existing public key
    fn from_public_key(public_key: &[u8]) -> Self
    where
        Self: Sized;

    /// Generate keys from existing secret key
    fn from_secret_key(private_key: &[u8]) -> Self
    where
        Self: Sized;
}

Generate Random Keys

use taple_core::crypto::{Ed25519KeyPair, KeyGenerator};

// Random key generation
let keypair = Ed25519KeyPair::new();

Generate from Seed

// Deterministic generation
let seed = b"my secure seed";
let keypair = Ed25519KeyPair::from_seed(seed);

Seed Requirements

From core/src/commons/crypto/mod.rs:266-277:
pub fn create_seed(initial_seed: &[u8]) -> Result<[u8; 32], Error> {
    let mut seed = [0u8; 32];
    if initial_seed.is_empty() {
        // Generate random seed
        getrandom::getrandom(&mut seed)
            .map_err(|_| Error::SeedError("couldn't generate random seed".to_owned()))?;
    } else if initial_seed.len() <= 32 {
        seed[..initial_seed.len()].copy_from_slice(initial_seed);
    } else {
        return Err(Error::SeedError("seed is greater than 32".to_owned()));
    }
    Ok(seed)
}
Rules:
  • Empty seed generates random keys
  • Seeds must be ≤ 32 bytes
  • Shorter seeds are zero-padded
  • Use cryptographically secure random seeds in production

Digital Signatures

DSA Trait

From core/src/commons/crypto/mod.rs:116-122:
pub trait DSA {
    /// Performs sign operation
    fn sign(&self, payload: Payload) -> Result<Vec<u8>, Error>;

    /// Performs verify operation
    fn verify(&self, payload: Payload, signature: &[u8]) -> Result<(), Error>;
}

Payload Types

From core/src/commons/crypto/mod.rs:258-263:
pub enum Payload {
    Buffer(Vec<u8>),
    BufferArray(Vec<Vec<u8>>),
}

Signing Example

use taple_core::crypto::{KeyPair, Ed25519KeyPair, DSA, Payload};

let keypair = KeyPair::Ed25519(Ed25519KeyPair::new());
let message = b"Hello, TAPLE!";

// Sign
let signature = keypair.sign(Payload::Buffer(message.to_vec()))?;

// Verify
keypair.verify(Payload::Buffer(message.to_vec()), &signature)?;

Signature Implementation

From core/src/commons/crypto/mod.rs:179-202:
impl DSA for KeyPair {
    fn sign(&self, payload: Payload) -> Result<Vec<u8>, Error> {
        match self {
            KeyPair::Ed25519(x) => x.sign(payload),
            #[cfg(feature = "secp256k1")]
            KeyPair::Secp256k1(x) => x.sign(payload),
        }
    }

    fn verify(&self, payload: Payload, signature: &[u8]) -> Result<(), Error> {
        match self {
            KeyPair::Ed25519(x) => x.verify(payload, signature),
            #[cfg(feature = "secp256k1")]
            KeyPair::Secp256k1(x) => x.verify(payload, signature),
        }
    }
}

Key Material Access

KeyMaterial Trait

From core/src/commons/crypto/mod.rs:72-86:
pub trait KeyMaterial {
    /// Returns the public key bytes as slice
    fn public_key_bytes(&self) -> Vec<u8>;

    /// Returns the secret key bytes as slice
    fn secret_key_bytes(&self) -> Vec<u8>;

    /// Returns bytes from key pair
    fn to_bytes(&self) -> Vec<u8>;

    /// Returns String from key pair encoded in base64
    fn to_str(&self) -> String {
        encode_config(self.to_bytes(), base64::URL_SAFE_NO_PAD)
    }
}

Extract Key Bytes

let keypair = Ed25519KeyPair::new();

// Get public key (32 bytes for Ed25519)
let public_key = keypair.public_key_bytes();

// Get secret key (32 bytes for Ed25519)
let secret_key = keypair.secret_key_bytes();

// Get as base64 string
let key_string = keypair.to_str();

Digest Derivation

TAPLE uses cryptographic hash functions for content addressing and identifiers.

DigestDerivator

Supported hash algorithms:
pub enum DigestDerivator {
    Blake3_256,
    Blake3_512,
    SHA2_256,
    SHA2_512,
    SHA3_256,
    SHA3_512,
}

Blake3 (Default)

Characteristics:
  • Extremely fast (optimized for modern CPUs)
  • Cryptographically secure
  • Built-in keyed hashing and key derivation
  • Parallizable
Use cases:
  • Subject IDs
  • Event IDs
  • Content addressing

SHA-2 and SHA-3

Well-established alternatives with different security properties:
  • SHA2-256/512: NIST standard, widely supported
  • SHA3-256/512: Keccak-based, resistant to length extension attacks

Node Settings

Configure cryptographic schemes in node settings (from core/src/commons/settings/mod.rs:234-253):
pub struct NodeSettings {
    /// KeyDerivator to be used by the secret key
    pub key_derivator: KeyDerivator,
    
    /// Secret key to be used by the node
    pub secret_key: String,
    
    /// DigestDerivator for event and subject identifiers
    pub digest_derivator: DigestDerivator,
    
    // ... other settings
}

impl Default for NodeSettings {
    fn default() -> Self {
        Self {
            key_derivator: KeyDerivator::Ed25519,
            secret_key: String::from(""),
            digest_derivator: DigestDerivator::Blake3_256,
            // ...
        }
    }
}

Configuration Examples

Ed25519 with Blake3 (Default)

use taple_core::{Settings, KeyDerivator, DigestDerivator};

let mut settings = Settings::default();
// Default is Ed25519 + Blake3_256

Secp256k1 with SHA2

use taple_core::{Settings, KeyDerivator, DigestDerivator, crypto::*};

// Generate Secp256k1 key
let keypair = Secp256k1KeyPair::new();

let mut settings = Settings::default();
settings.node.key_derivator = KeyDerivator::Secp256k1;
settings.node.secret_key = hex::encode(keypair.secret_key_bytes());
settings.node.digest_derivator = DigestDerivator::SHA2_256;

Import Existing Key

let secret_key_hex = "a1b2c3d4e5f6...";

let mut settings = Settings::default();
settings.node.key_derivator = KeyDerivator::Ed25519;
settings.node.secret_key = secret_key_hex.to_string();

Serialization

KeyPair implements Borsh serialization for efficient storage. From core/src/commons/crypto/mod.rs:204-241:
impl BorshSerialize for KeyPair {
    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
        match &self {
            KeyPair::Ed25519(x) => {
                BorshSerialize::serialize(&0u8, writer)?;
                let a: [u8; 32] = x.secret_key_bytes().try_into().unwrap();
                BorshSerialize::serialize(&a, writer)
            }
            KeyPair::Secp256k1(x) => {
                BorshSerialize::serialize(&1u8, writer)?;
                let a: [u8; 32] = x.secret_key_bytes().try_into().unwrap();
                BorshSerialize::serialize(&a, writer)
            }
        }
    }
}

impl BorshDeserialize for KeyPair {
    fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
        let order: u8 = BorshDeserialize::deserialize_reader(reader)?;
        match order {
            0 => {
                let data: [u8; 32] = BorshDeserialize::deserialize_reader(reader)?;
                Ok(KeyPair::Ed25519(Ed25519KeyPair::from_secret_key(&data)))
            }
            1 => {
                let data: [u8; 32] = BorshDeserialize::deserialize_reader(reader)?;
                Ok(KeyPair::Secp256k1(Secp256k1KeyPair::from_secret_key(&data)))
            }
            _ => Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!("Invalid KeyPair variant: {}", order),
            )),
        }
    }
}

Key Identifiers

Generate identifiers from public keys:
use taple_core::{KeyIdentifier, Derivable};
use taple_core::crypto::{KeyPair, Ed25519KeyPair, KeyMaterial};

let keypair = KeyPair::Ed25519(Ed25519KeyPair::new());
let key_id = KeyIdentifier::new(
    keypair.get_key_derivator(),
    &keypair.public_key_bytes()
);

println!("Key ID: {}", key_id.to_str());

Security Best Practices

Key Generation

  • Use cryptographically secure random number generators
  • Never hardcode keys in source code
  • Use environment variables or secure key management systems
  • Rotate keys periodically

Key Storage

  • Store private keys encrypted at rest
  • Use hardware security modules (HSM) for production
  • Implement proper access controls
  • Back up keys securely

Algorithm Selection

  • Ed25519: Best for most use cases (fast, secure, compact)
  • Secp256k1: When blockchain compatibility is required
  • Blake3: Default for hashing (fastest)
  • SHA-2/SHA-3: When regulatory compliance requires NIST standards

Signature Verification

  • Always verify signatures before processing
  • Validate public keys are on the curve
  • Use constant-time comparison to prevent timing attacks
  • Implement replay attack prevention

Performance Characteristics

Signature Generation (approximate)

  • Ed25519: ~30,000 signatures/sec
  • Secp256k1: ~10,000 signatures/sec

Signature Verification (approximate)

  • Ed25519: ~15,000 verifications/sec
  • Secp256k1: ~3,000 verifications/sec

Hash Generation (Blake3)

  • Single-threaded: ~1-3 GB/s
  • Multi-threaded: ~10+ GB/s (depends on CPU cores)
Performance numbers are approximate and vary based on hardware. Benchmark your specific use case for accurate measurements.

Migration Between Schemes

Changing cryptographic schemes requires careful planning:
  1. Key derivator changes: Requires new node identity
  2. Digest derivator changes: New subjects use new scheme, existing subjects keep original
  3. Backward compatibility: Old signatures remain valid with original scheme
// Check what derivator was used for a subject
let subject = api.get_subject(subject_id).await?;
let derivator = subject.subject_id.derivator;

Build docs developers (and LLMs) love