```
.  # ./src
├── apis
│   ├── build.rs
│   ├── mod.rs
├── build
│   ├── identity.rs
│   ├── mod.rs
│   ├── recipient.rs
├── errors
│   ├── buildings.rs
│   ├── mod.rs
│   ├── security.rs
│   ├── validation.rs
├── lib.rs
├── security
│   ├── mod.rs
│   ├── zeroize.rs
├── types
│   ├── keypair.rs
│   ├── mod.rs
│   ├── public_key.rs
│   ├── secret_key.rs
│   ├── validation.rs
```
## ./src/lib.rs

```rust
//! # age-setup
//!
//! A library for generating age keypairs (X25519) with validation and secure memory handling.
//!
//! This crate provides a simple way to create a keypair for age encryption,
//! returning a [`KeyPair`] containing a public key (starting with "age1") and a secret key.
//! The secret key is automatically zeroized on drop for security.
//!
//! # Example
//!
//! ```
//! use age_setup::build_keypair;
//!
//! let keypair = build_keypair().expect("failed to generate keypair");
//! println!("Public key: {}", keypair.public);
//! println!("Secret key: [REDACTED]"); // Display redacts secret
//! // Access raw secret with .expose() – use with caution!
//! # let _ = keypair.secret.expose();
//! ```
//!
//! # Features
//! - Generate X25519 keypairs compatible with age.
//! - Public key validation (must start with "age1").
//! - Secret key zeroization on drop.
//! - Redacted `Display` for secret key.
//! - Error handling with detailed error types.

pub mod apis;
pub mod build;
pub mod errors;
pub mod security;
pub mod types;

pub use apis::build::build_keypair;
pub use errors::{Error, Result};
pub use types::KeyPair;
```
## ./src/types/mod.rs

```rust
pub mod keypair;
pub mod public_key;
pub mod secret_key;
pub mod validation;
pub use keypair::KeyPair;
pub use public_key::PublicKey;
pub use secret_key::SecretKey;
```
## ./src/types/keypair.rs

```rust
//! Keypair structure combining public and secret keys.

use crate::types::{PublicKey, SecretKey};

/// A keypair consisting of an age public key and its corresponding secret key.
///
/// This is the main output of [`build_keypair`](crate::build_keypair).
/// Both fields are public for direct access.
#[derive(Debug)]
pub struct KeyPair {
    /// The public key (starts with "age1").
    pub public: PublicKey,
    /// The secret key (zeroized on drop).
    pub secret: SecretKey,
}

impl KeyPair {
    /// Creates a new keypair (internal use only).
    pub(crate) fn new(public: PublicKey, secret: SecretKey) -> Self {
        Self { public, secret }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::{PublicKey, SecretKey};

    #[test]
    fn test_keypair_new() {
        let pub_key = PublicKey::new("age1pub".to_string()).unwrap();
        let secret_key = SecretKey::new("secret".to_string());
        let kp = KeyPair::new(pub_key, secret_key);
        assert_eq!(kp.public.expose(), "age1pub");
        assert_eq!(kp.secret.expose(), "secret");
    }
}
```
## ./src/types/public_key.rs

```rust
//! Public key type with validation and display.

use crate::errors::Result;
use crate::types::validation::validate_age_prefix;
use std::fmt;

/// An age public key, guaranteed to start with "age1".
///
/// Constructed via `PublicKey::new`, which validates the format.
/// Can be displayed or converted to a string using `expose()`.
#[derive(Debug, Clone)]
pub struct PublicKey(String);

impl PublicKey {
    /// Creates a new public key after validating the prefix.
    ///
    /// # Errors
    /// Returns `ValidationError` if the string is empty or doesn't start with "age1".
    pub(crate) fn new(raw: String) -> Result<Self> {
        validate_age_prefix(&raw)?;
        Ok(Self(raw))
    }

    /// Returns the raw string representation of the public key.
    ///
    /// # Example
    /// ```
    /// # use age_setup::types::PublicKey;
    /// # let pk = PublicKey::new("age1example".to_string()).unwrap();
    /// assert_eq!(pk.expose(), "age1example");
    /// ```
    #[must_use]
    pub fn expose(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for PublicKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl AsRef<str> for PublicKey {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_public_key_new_valid() {
        let pk = PublicKey::new("age1valid".to_string());
        assert!(pk.is_ok());
        assert_eq!(pk.unwrap().expose(), "age1valid");
    }

    #[test]
    fn test_public_key_new_invalid() {
        let pk = PublicKey::new("invalid".to_string());
        assert!(pk.is_err());
    }

    #[test]
    fn test_public_key_display() {
        let pk = PublicKey::new("age1test".to_string()).unwrap();
        assert_eq!(format!("{}", pk), "age1test");
    }

    #[test]
    fn test_public_key_asref() {
        let pk = PublicKey::new("age1asref".to_string()).unwrap();
        let s: &str = pk.as_ref();
        assert_eq!(s, "age1asref");
    }

    #[test]
    fn test_public_key_clone() {
        let pk1 = PublicKey::new("age1clone".to_string()).unwrap();
        let pk2 = pk1.clone();
        assert_eq!(pk1.expose(), pk2.expose());
    }
}
```
## ./src/types/secret_key.rs

```rust
//! Secret key type with automatic zeroization on drop.

use crate::security::zeroize::wipe_memory;
use std::fmt;

/// An age secret key that securely wipes its memory when dropped.
///
/// The secret key is stored as bytes. The `Display` implementation redacts the value,
/// showing `[REDACTED]`. Use `expose()` to get the raw string (use with caution).
#[derive(Debug, Clone)]
pub struct SecretKey {
    inner: Vec<u8>,
}

impl SecretKey {
    /// Creates a new secret key from a string (internal).
    pub(crate) fn new(raw: String) -> Self {
        Self {
            inner: raw.into_bytes(),
        }
    }

    /// Exposes the raw secret key as a string.
    ///
    /// # Panics
    /// Panics if the inner bytes are not valid UTF-8 (should never happen because
    /// the key is created from a valid UTF-8 string).
    ///
    /// # Security
    /// Only use this when absolutely necessary, as it exposes the secret.
    #[must_use]
    pub fn expose(&self) -> &str {
        std::str::from_utf8(&self.inner).expect("SecretKey inner buffer must be valid UTF-8")
    }
}

impl Drop for SecretKey {
    fn drop(&mut self) {
        let _ = wipe_memory(&mut self.inner);
    }
}

impl fmt::Display for SecretKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[REDACTED]")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_secret_key_expose() {
        let sk = SecretKey::new("test".to_string());
        assert_eq!(sk.expose(), "test");
    }

    #[test]
    fn test_secret_key_display() {
        let sk = SecretKey::new("test".to_string());
        assert_eq!(format!("{}", sk), "[REDACTED]");
    }

    #[test]
    fn test_secret_key_clone() {
        let sk1 = SecretKey::new("secret".to_string());
        let sk2 = sk1.clone();
        assert_eq!(sk1.expose(), sk2.expose());
    }

    #[test]
    fn test_secret_key_drop_calls_wipe() {
        let sk = SecretKey::new("secret".to_string());
        drop(sk); // Tidak panic, wipe_memory dipanggil
    }
}
```
## ./src/types/validation.rs

```rust
use crate::errors::{Error, Result, ValidationError};
pub(crate) fn validate_age_prefix(key: &str) -> Result<()> {
    if key.is_empty() {
        return Err(Error::from(ValidationError::invalid_public_key(
            "Key is empty",
        )));
    }
    if !key.starts_with("age1") {
        return Err(Error::from(ValidationError::invalid_public_key(format!(
            "Key must start with 'age1', got: {}",
            &key[..key.len().min(10)]
        ))));
    }
    Ok(())
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::errors::Error;

    #[test]
    fn test_validate_age_prefix_empty() {
        let result = validate_age_prefix("");
        assert!(result.is_err());
        match result.unwrap_err() {
            Error::Validation(e) => {
                let msg = format!("{}", e);
                assert!(msg.contains("Key is empty"));
            }
            _ => panic!(),
        }
    }

    #[test]
    fn test_validate_age_prefix_wrong_prefix() {
        let result = validate_age_prefix("xyz123");
        assert!(result.is_err());
        match result.unwrap_err() {
            Error::Validation(e) => {
                let msg = format!("{}", e);
                assert!(msg.contains("must start with 'age1'"));
                assert!(msg.contains("xyz123"));
            }
            _ => panic!(),
        }
    }

    #[test]
    fn test_validate_age_prefix_short_prefix() {
        let result = validate_age_prefix("age");
        assert!(result.is_err());
    }

    #[test]
    fn test_validate_age_prefix_valid() {
        let result = validate_age_prefix("age1abcdef");
        assert!(result.is_ok());
    }
}
```
## ./src/apis/mod.rs

```rust
pub mod build;
```
## ./src/apis/build.rs

```rust
//! Keypair building API.

use crate::build::identity::create_identity;
use crate::build::recipient::extract_recipient;
use crate::errors::Result;
use crate::types::{KeyPair, PublicKey, SecretKey};
use age::secrecy::ExposeSecret;

/// Generates a new age X25519 keypair.
///
/// This function creates a random identity using `age::x25519::Identity::generate()`,
/// extracts the corresponding recipient (public key), and wraps them into a [`KeyPair`].
/// The public key is validated to start with "age1".
///
/// # Returns
/// Returns a [`Result`] containing a [`KeyPair`] on success, or an [`crate::Error`] if validation fails.
///
/// # Example
/// ```
/// # use age_setup::build_keypair;
/// let kp = build_keypair().unwrap();
/// assert!(kp.public.expose().starts_with("age1"));
/// ```
pub fn build_keypair() -> Result<KeyPair> {
    let identity = create_identity()?;
    let recipient = extract_recipient(&identity);
    let public_raw = recipient.to_string();
    let secret_raw = identity.to_string().expose_secret().to_string();
    let public = PublicKey::new(public_raw)?;
    let secret = SecretKey::new(secret_raw);
    Ok(KeyPair::new(public, secret))
}
```
## ./src/errors/mod.rs

```rust
//! Error handling types.

pub mod buildings;
pub mod security;
pub mod validation;

pub use buildings::GenerationError;
pub use security::SecurityError;
pub use validation::ValidationError;

/// Main error type for the crate.
///
/// Encompasses all possible errors during key generation, validation, or security operations.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// Error during key generation (e.g., internal library failure).
    #[error("Key generation failed: {0}")]
    Generation(#[from] GenerationError),
    /// Error validating public key format.
    #[error("Validation failed: {0}")]
    Validation(#[from] ValidationError),
    /// Error during security operations (e.g., memory wipe).
    #[error("Security operation failed: {0}")]
    Security(#[from] SecurityError),
}

/// Result type alias using `Error` from this crate.
pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_from_generation() {
        let gen_err = GenerationError::IdentityCreationFailed;
        let err: Error = gen_err.into();
        assert!(matches!(err, Error::Generation(_)));
    }

    #[test]
    fn test_error_from_validation() {
        let val_err = ValidationError::invalid_public_key("test");
        let err: Error = val_err.into();
        assert!(matches!(err, Error::Validation(_)));
    }

    #[test]
    fn test_error_from_security() {
        let sec_err = SecurityError::MemoryWipeFailed;
        let err: Error = sec_err.into();
        assert!(matches!(err, Error::Security(_)));
    }

    #[test]
    fn test_error_display() {
        let err = Error::Generation(GenerationError::IdentityCreationFailed);
        assert_eq!(
            format!("{}", err),
            "Key generation failed: Age identity generation failed: internal library error"
        );
    }
}
```
## ./src/errors/validation.rs

```rust
#[derive(Debug, thiserror::Error)]
pub enum ValidationError {
    #[error("Invalid public key format: {reason}")]
    InvalidPublicKeyFormat { reason: String },
}
impl ValidationError {
    pub(crate) fn invalid_public_key(reason: impl Into<String>) -> Self {
        Self::InvalidPublicKeyFormat {
            reason: reason.into(),
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_validation_error_display() {
        let err = ValidationError::invalid_public_key("test reason");
        let msg = format!("{}", err);
        assert_eq!(msg, "Invalid public key format: test reason");
    }
}
```
## ./src/errors/security.rs

```rust
//! Security operation error types.

/// Errors that occur during security operations (e.g., memory wiping).
#[derive(Debug, thiserror::Error)]
pub enum SecurityError {
    /// Memory wipe operation failed (unlikely, but included for completeness).
    #[error("Memory wipe operation failed")]
    MemoryWipeFailed,
}
```
## ./src/errors/buildings.rs

```rust
//! Key generation error types.

/// Errors that occur during identity generation.
#[derive(Debug, thiserror::Error)]
pub enum GenerationError {
    /// Failed to generate an age identity (internal library issue).
    #[error("Age identity generation failed: internal library error")]
    IdentityCreationFailed,
}
```
## ./src/security/mod.rs

```rust
pub mod zeroize;
```
## ./src/security/zeroize.rs

```rust
//! Memory zeroization utilities.

use crate::errors::Result;
use zeroize::Zeroize;

/// Securely overwrites a byte slice with zeros.
///
/// This function uses the `zeroize` crate to prevent compiler optimizations.
/// It always returns `Ok(())`; the `Result` is kept for future extensibility.
#[must_use = "wipe_memory should be called to ensure memory is cleared"]
pub(crate) fn wipe_memory(data: &mut [u8]) -> Result<()> {
    data.zeroize();
    Ok(())
}
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_wipe_memory_zeroizes() {
        let mut data = vec![1, 2, 3, 4];
        let result = wipe_memory(&mut data);
        assert!(result.is_ok());
        assert_eq!(data, vec![0, 0, 0, 0]);
    }
}
```
## ./src/build/mod.rs

```rust
pub mod identity;
pub mod recipient;
```
## ./src/build/identity.rs

```rust
//! Internal identity generation.

use crate::errors::Result;
use age::x25519::Identity;
pub(crate) fn create_identity() -> Result<Identity> {
    let identity = Identity::generate();
    Ok(identity)
}
```
## ./src/build/recipient.rs

```rust
//! Internal recipient extraction.

use age::x25519::{Identity, Recipient};
pub(crate) fn extract_recipient(identity: &Identity) -> Recipient {
    identity.to_public()
}
```
