1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! Lightweight validation for age public keys.
//!
//! This module provides a **non‑cryptographic** sanity check for age public key
//! strings. It is used internally (`pub(crate)`) to catch obvious programmer
//! mistakes early, before the key is actually used in cryptographic operations.
use crate;
/// Verifies that a string is non‑empty and starts with the age public key prefix `"age1"`.
///
/// This is a **lightweight, internal sanity check** that ensures a candidate public
/// key adheres to the most basic requirement of the [age specification]. It is called
/// before a raw string is wrapped inside [`PublicKey`].
///
/// # Checks performed
///
/// 1. **Non‑emptiness** – an empty string is rejected immediately.
/// 2. **Prefix** – the string must begin with `"age1"`. The comparison is
/// case‑sensitive, so `"AGE1..."` is rejected.
///
/// No further validation (length, Bech32 character set, checksum, or curve point)
/// is performed here. Full parsing is the responsibility of the upstream `age` crate.
///
/// # Why only a prefix check?
///
/// Parsing a real Bech32‑encoded age key is complex and already handled by the `age`
/// library. This minimal check catches common programmer errors (e.g., passing a
/// filename instead of a key) early and with a clear error message.
///
/// # Parameters
///
/// * `key` – A string slice (`&str`) representing the raw public key to validate.
///
/// # Returns
///
/// * `Ok(())` if the key is non‑empty and starts with `"age1"`.
/// * `Err(Error::Validation(ValidationError::InvalidPublicKeyFormat { ... }))` if
/// either condition is violated.
///
/// # Errors
///
/// | Condition | Error message |
/// |-----------|---------------|
/// | `key.is_empty()` | `"Key is empty"` |
/// | `key` does not start with `"age1"` | `"Key must start with 'age1', got: <prefix>…"` |
///
/// When the prefix is wrong, the error message includes the first few characters of
/// the supplied key (up to 10). The truncation is safe for short strings and will not
/// panic.
///
/// # Security note
///
/// **This function does not guarantee that the key is a valid age recipient.**
/// Malicious or malformed keys that still begin with `"age1"` will pass this check.
/// The actual cryptographic validation happens inside the `age` crate when the key is
/// used for encryption.
///
/// # Examples
///
/// ```rust,ignore
/// use age_setup::validation::validate_age_prefix;
///
/// // ✅ Valid keys
/// assert!(validate_age_prefix("age1abcdef").is_ok());
/// assert!(validate_age_prefix("age1").is_ok()); // minimal valid input
///
/// // ❌ Invalid keys
/// let empty = validate_age_prefix("");
/// assert!(empty.is_err());
///
/// let wrong = validate_age_prefix("ssh-rsa AAAA...");
/// let err_msg = wrong.unwrap_err().to_string();
/// assert_eq!(
/// err_msg,
/// "Key must start with 'age1', got: ssh-rsa AA"
/// );
/// ```
///
/// # See also
///
/// * [`PublicKey::new`](crate::public_key::PublicKey::new) – uses this validator.
/// * [age specification](https://github.com/FiloSottile/age) – formal documentation.
pub