Skip to main content

age_setup/
public_key.rs

1//! Age public key type.
2//!
3//! This module provides the [`PublicKey`] type, a validated wrapper around a
4//! string that is guaranteed to start with the age public key prefix `"age1"`.
5//! The type is the public half of an [`KeyPair`](crate::KeyPair) and can be
6//! freely shared, displayed, and cloned.
7
8use crate::errors::Result;
9use std::fmt;
10
11/// A validated age public key.
12///
13/// `PublicKey` is a thin wrapper around a [`String`] that ensures the contained
14/// key starts with `"age1"`. The validation is performed at construction time
15/// via [`new`](PublicKey::new), which delegates to
16/// [`validate_age_prefix`](crate::validation::validate_age_prefix).
17///
18/// # Security
19///
20/// While this type guarantees the `"age1"` prefix, it does **not** perform
21/// full Bech32 decoding or curve validation. The actual cryptographic checks
22/// are left to the `age` crate when the key is used for encryption.
23///
24/// # Examples
25///
26/// ```rust
27/// use age_setup::PublicKey;
28///
29/// let pk = PublicKey::new("age1mykey".into())?;
30/// println!("Public key: {}", pk);          // uses Display
31/// println!("Exposed value: {}", pk.expose());
32/// # Ok::<(), age_setup::Error>(())
33/// ```
34#[derive(Debug, Clone)]
35pub struct PublicKey(String);
36
37impl PublicKey {
38    /// Creates a new `PublicKey` after validating the raw string.
39    ///
40    /// The string must be non‑empty and start with `"age1"` (case‑sensitive).
41    /// Validation is performed by [`validate_age_prefix`].
42    ///
43    /// # Errors
44    ///
45    /// Returns [`Error::Validation`](crate::Error::Validation) if the key
46    /// does not meet the prefix requirement.
47    ///
48    /// # Examples
49    ///
50    /// ```rust
51    /// # use age_setup::PublicKey;
52    /// let valid = PublicKey::new("age1abcdef".into()).unwrap();
53    /// assert_eq!(valid.expose(), "age1abcdef");
54    ///
55    /// let invalid = PublicKey::new("not-a-key".into());
56    /// assert!(invalid.is_err());
57    /// ```
58    pub fn new(raw: String) -> Result<Self> {
59        crate::validation::validate_age_prefix(&raw)?;
60        Ok(Self(raw))
61    }
62
63    /// Returns the raw string representation of the public key.
64    ///
65    /// The returned `&str` is safe to display, share, or use as an age
66    /// recipient.
67    ///
68    /// # Examples
69    ///
70    /// ```rust
71    /// # use age_setup::PublicKey;
72    /// let pk = PublicKey::new("age1test".into()).unwrap();
73    /// assert_eq!(pk.expose(), "age1test");
74    /// ```
75    #[must_use]
76    pub fn expose(&self) -> &str {
77        &self.0
78    }
79}
80
81/// The public key is printed in the format `age1...`.
82impl fmt::Display for PublicKey {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "{}", self.0)
85    }
86}
87
88/// Allows `PublicKey` to be used where a `&str` is expected.
89///
90/// ```rust
91/// # use age_setup::PublicKey;
92/// fn print_key(key: &impl AsRef<str>) {
93///     println!("{}", key.as_ref());
94/// }
95/// let pk = PublicKey::new("age1foo".into()).unwrap();
96/// print_key(&pk);
97/// ```
98impl AsRef<str> for PublicKey {
99    fn as_ref(&self) -> &str {
100        &self.0
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107
108    #[test]
109    fn valid() {
110        let pk = PublicKey::new("age1test".into()).unwrap();
111        assert_eq!(pk.expose(), "age1test");
112    }
113
114    #[test]
115    fn invalid() {
116        assert!(PublicKey::new("bad".into()).is_err());
117    }
118}