Iroh Key Derivation and Signing: Working with SecretKey and PublicKey

Iroh uses a single Ed25519 key pair per endpoint where SecretKey handles signing and PublicKey (aliased as EndpointId) serves as the unique identifier and verification key.

The n0-computer/iroh repository implements a robust identity system based on Ed25519 cryptography. Understanding Iroh key derivation and signing with SecretKey/PublicKey is essential for building secure peer-to-peer applications, as these keys authenticate every endpoint and sign all network messages.

Core Key Types in Iroh

SecretKey Structure and Generation

Located in iroh-base/src/key.rs, the SecretKey struct wraps a SigningKey from the ed25519_dalek crate:

#[derive(Clone, zeroize::ZeroizeOnDrop)]
pub struct SecretKey(SigningKey);

The ZeroizeOnDrop derive ensures sensitive key material is cleared from memory when the key is dropped. The SecretKey::generate method uses the OS RNG to produce 32 random bytes and construct a new signing key.

PublicKey and EndpointId

The public counterpart uses CompressedEdwardsY for efficient representation:

#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct PublicKey(CompressedEdwardsY);

Iroh defines EndpointId as a direct alias to PublicKey:

pub type EndpointId = PublicKey;

This means your public key doubles as your network address, eliminating the need for separate identity and transport identifiers.

Key Derivation and Signing Implementation

Generating Cryptographically Secure Keys

Call SecretKey::generate() to create fresh key material. This method in iroh-base/src/key.rs (lines 18-20) sources entropy from the operating system and initializes the SigningKey with 32 random bytes.

Deriving the Public Identifier

The public() method (lines 99-101 in iroh-base/src/key.rs) derives the PublicKey from the secret key by calling self.0.verifying_key(). This deterministic derivation ensures that each secret key maps to exactly one endpoint ID.

Signing Messages and Verification

Sign data using SecretKey::sign (lines 23-26), which delegates to self.0.sign(msg):

pub fn sign(&self, msg: &[u8]) -> Signature {
    self.0.sign(msg)
}

Verification uses PublicKey::verify (lines 33-35), which calls ed25519_dalek::VerifyingKey::verify_strict to enforce strict signature verification and prevent signature malleability.

Practical Code Examples

Generating a New Key Pair

use iroh_base::{SecretKey, PublicKey};

let secret_key = SecretKey::generate();          // 32-byte random secret
let endpoint_id: PublicKey = secret_key.public(); // unique endpoint identifier
println!("Endpoint ID: {}", endpoint_id.fmt_short());

Signing and Verifying Messages

use iroh_base::{SecretKey, Signature};

let secret = SecretKey::generate();
let msg = b"hello iroh";

// Signing
let signature: Signature = secret.sign(msg);

// Verification with the derived public key
let pub_key = secret.public();
pub_key.verify(msg, &signature).expect("signature must be valid");

Integrating with the Endpoint Builder

In iroh/src/endpoint.rs, the Endpoint builder consumes the secret key to establish identity:

use iroh::Endpoint;

let secret_key = SecretKey::generate();
let ep = Endpoint::builder()
    .secret_key(secret_key)
    .bind_port(0)
    .run()
    .await?;
println!("Running as {}", ep.secret_key().public().fmt_short());

Publishing PKARR Records

The PKARR publisher in iroh/src/address_lookup/pkarr.rs uses the public key as the record owner:

use iroh::address_lookup::pkarr::PkarrPublisher;

let secret = SecretKey::generate();
let publisher = PkarrPublisher::builder()
    .secret_key(secret.clone())
    .build(/* TLS config */);
publisher.publish("example.com", secret.public()).await?;

Key Files and Architecture

The implementation spans several critical files:

Summary

  • Iroh uses a single Ed25519 key pair per endpoint, with SecretKey for signing and PublicKey as the identifier.
  • The SecretKey::generate method creates cryptographically secure keys using OS entropy.
  • SecretKey::public() deterministically derives the PublicKey, which doubles as the EndpointId.
  • Signing occurs via SecretKey::sign, while PublicKey::verify uses strict verification to prevent malleability.
  • These keys underpin TLS certificates, PKARR publishing, and relay authentication throughout the Iroh stack.

Frequently Asked Questions

How does Iroh generate secure secret keys?

Iroh's SecretKey::generate method in iroh-base/src/key.rs uses the operating system's RNG to produce 32 random bytes, feeding them into the Ed25519 SigningKey constructor. The ZeroizeOnDrop trait ensures keys are securely cleared from memory when no longer needed.

What is the relationship between PublicKey and EndpointId in Iroh?

EndpointId is a type alias for PublicKey defined in iroh-base/src/key.rs. This design means your cryptographic public key serves as your network address, ensuring that endpoint identification and cryptographic verification use the same 32-byte identifier.

How does Iroh prevent signature malleability attacks?

The PublicKey::verify method delegates to ed25519_dalek::VerifyingKey::verify_strict, which enforces strict verification standards. This prevents attackers from generating alternative valid signatures for the same message and public key.

Where is the secret key used in Iroh's TLS implementation?

In iroh/src/tls/resolver.rs (line 67), the secret key's public component is converted to bytes via self.key.public().as_bytes() to create the subject field of self-signed TLS certificates, binding the TLS identity to the Ed25519 public key.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →