use base64::{engine::general_purpose, Engine as _};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use tap_agent::{
did::{DIDGenerationOptions, DIDKeyGenerator, KeyType},
error::Result,
storage::{KeyStorage, StoredKey},
};
use tempfile::TempDir;
fn main() -> Result<()> {
println!("TAP Key Management Example\n");
let temp_dir = TempDir::new().map_err(|e| tap_agent::error::Error::Storage(e.to_string()))?;
let storage_path = temp_dir.path().join("keys.json");
println!("Using temporary storage at: {:?}\n", storage_path);
println!("(This avoids affecting your production ~/.tap directory)\n");
generate_new_key(&storage_path)?;
load_and_display_keys(&storage_path)?;
generate_multiple_key_types(&storage_path)?;
demonstrate_key_rotation(&storage_path)?;
println!("Example completed! All keys were stored in temporary directory.");
println!("Your production ~/.tap directory was not affected.");
Ok(())
}
fn generate_new_key(storage_path: &PathBuf) -> Result<()> {
println!("=== Generating New Ed25519 Key ===");
let generator = DIDKeyGenerator::new();
let options = DIDGenerationOptions {
key_type: KeyType::Ed25519,
};
let generated_key = generator.generate_did(options)?;
println!("Generated DID: {}", generated_key.did);
println!("Key Type: {:?}", generated_key.key_type);
let stored_key = StoredKey {
did: generated_key.did.clone(),
label: "example-ed25519".to_string(),
key_type: generated_key.key_type,
private_key: general_purpose::STANDARD.encode(&generated_key.private_key),
public_key: general_purpose::STANDARD.encode(&generated_key.public_key),
metadata: {
let mut meta = HashMap::new();
meta.insert(
"created_by".to_string(),
"key_management_example".to_string(),
);
meta.insert("purpose".to_string(), "demonstration".to_string());
meta
},
};
let mut storage = KeyStorage::load_from_path(storage_path).unwrap_or_else(|_| {
println!("No existing key storage found, creating new one");
KeyStorage::new()
});
storage.add_key(stored_key);
storage.save_to_path(storage_path)?;
println!("Key saved to {:?}\n", storage_path);
Ok(())
}
fn load_and_display_keys(storage_path: &Path) -> Result<()> {
println!("=== Loading Existing Keys ===");
match KeyStorage::load_from_path(storage_path) {
Ok(storage) => {
println!("Found {} key(s) in storage", storage.keys.len());
if let Some(default_did) = &storage.default_did {
println!("Default DID: {}", default_did);
}
println!("\nStored keys:");
for (did, key) in &storage.keys {
println!("\n DID: {}", did);
println!(" Key Type: {:?}", key.key_type);
println!(
" Public Key (first 32 chars): {}...",
&key.public_key[..32.min(key.public_key.len())]
);
if !key.metadata.is_empty() {
println!(" Metadata:");
for (k, v) in &key.metadata {
println!(" {}: {}", k, v);
}
}
}
}
Err(e) => {
println!("No key storage found or error loading: {}", e);
println!("Run 'generate_new_key' first to create keys");
}
}
println!();
Ok(())
}
#[allow(clippy::vec_init_then_push)]
fn generate_multiple_key_types(storage_path: &PathBuf) -> Result<()> {
println!("=== Generating Multiple Key Types ===");
let generator = DIDKeyGenerator::new();
let mut storage =
KeyStorage::load_from_path(storage_path).unwrap_or_else(|_| KeyStorage::new());
let mut key_types = vec![];
#[cfg(feature = "crypto-ed25519")]
key_types.push((KeyType::Ed25519, "Ed25519 - Fast and secure"));
#[cfg(feature = "crypto-p256")]
key_types.push((KeyType::P256, "P256 - NIST standard"));
#[cfg(feature = "crypto-secp256k1")]
key_types.push((
KeyType::Secp256k1,
"Secp256k1 - Bitcoin/Ethereum compatible",
));
for (key_type, description) in key_types {
println!("\nGenerating {} key...", description);
let generated_key = generator.generate_did(DIDGenerationOptions { key_type })?;
let stored_key = StoredKey {
did: generated_key.did.clone(),
label: format!("example-{}", description.to_lowercase().replace(' ', "-")),
key_type: generated_key.key_type,
private_key: general_purpose::STANDARD.encode(&generated_key.private_key),
public_key: general_purpose::STANDARD.encode(&generated_key.public_key),
metadata: {
let mut meta = HashMap::new();
meta.insert("key_type_description".to_string(), description.to_string());
meta
},
};
storage.add_key(stored_key);
println!(
" Generated: {}",
&generated_key.did[..50.min(generated_key.did.len())]
);
}
storage.save_to_path(storage_path)?;
println!("\nAll keys saved to {:?}\n", storage_path);
Ok(())
}
fn demonstrate_key_rotation(storage_path: &Path) -> Result<()> {
println!("=== Key Rotation Example ===");
let mut storage =
KeyStorage::load_from_path(storage_path).unwrap_or_else(|_| KeyStorage::new());
if storage.keys.is_empty() {
println!("No keys found. Please run other examples first.");
return Ok(());
}
let old_default = storage.default_did.clone();
println!("Current default DID: {:?}", old_default);
let generator = DIDKeyGenerator::new();
let new_key = generator.generate_ed25519_did()?;
let stored_key = StoredKey {
did: new_key.did.clone(),
label: "rotated-key".to_string(),
key_type: new_key.key_type,
private_key: general_purpose::STANDARD.encode(&new_key.private_key),
public_key: general_purpose::STANDARD.encode(&new_key.public_key),
metadata: {
let mut meta = HashMap::new();
meta.insert("purpose".to_string(), "key_rotation".to_string());
meta.insert(
"rotated_from".to_string(),
old_default.unwrap_or_else(|| "none".to_string()),
);
meta.insert("rotation_date".to_string(), chrono::Utc::now().to_rfc3339());
meta
},
};
storage.add_key(stored_key);
storage.default_did = Some(new_key.did.clone());
storage.save_to_path(storage_path)?;
println!("Key rotation complete!");
println!("New default DID: {}", new_key.did);
println!("\nOld keys are preserved for decrypting historical data.\n");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_generation() {
let generator = DIDKeyGenerator::new();
let key = generator.generate_ed25519_did().unwrap();
assert!(key.did.starts_with("did:key:"));
assert_eq!(key.private_key.len(), 32); assert_eq!(key.public_key.len(), 32); }
#[test]
fn test_key_storage_roundtrip() {
let generator = DIDKeyGenerator::new();
let key = generator.generate_ed25519_did().unwrap();
let stored_key = StoredKey {
did: key.did.clone(),
label: "updated-key".to_string(),
key_type: key.key_type,
private_key: general_purpose::STANDARD.encode(&key.private_key),
public_key: general_purpose::STANDARD.encode(&key.public_key),
metadata: HashMap::new(),
};
let mut storage = KeyStorage::new();
storage.add_key(stored_key.clone());
assert_eq!(storage.keys.len(), 1);
assert_eq!(storage.default_did, Some(key.did.clone()));
let retrieved = &storage.keys[&key.did];
assert_eq!(retrieved.did, stored_key.did);
assert_eq!(retrieved.private_key, stored_key.private_key);
}
}