truthlinked_sdk/
license.rs

1use zeroize::{Zeroize, ZeroizeOnDrop};
2
3/// Secure license key with automatic memory protection
4/// 
5/// This type provides secure storage and handling of Truthlinked license keys
6/// with the following security guarantees:
7/// 
8/// # Security Features
9/// - **Memory Protection**: Key data is automatically zeroized when dropped
10/// - **No Credential Leakage**: Debug and Display implementations show only redacted versions
11/// - **Safe Serialization**: Serialization outputs redacted version, never full key
12/// - **Constant-Time Operations**: Where applicable, operations are constant-time
13/// 
14/// # Thread Safety
15/// This type is `Send + Sync` and can be safely shared across threads.
16/// 
17/// # Example
18/// ```rust
19/// use truthlinked_sdk::LicenseKey;
20/// 
21/// let key = LicenseKey::new("tl_free_secret123".to_string());
22/// 
23/// // Safe to log - shows redacted version
24/// println!("Using key: {}", key);  // "tl_...123"
25/// 
26/// // Key is automatically zeroized when dropped
27/// drop(key);
28/// ```
29#[derive(Clone, Zeroize, ZeroizeOnDrop)]
30pub struct LicenseKey {
31    /// The actual license key string (zeroized on drop)
32    #[zeroize(skip)]
33    key: String,
34}
35
36impl LicenseKey {
37    /// Creates a new license key with memory protection
38    /// 
39    /// The provided key string will be stored securely and automatically
40    /// zeroized from memory when this instance is dropped.
41    /// 
42    /// # Arguments
43    /// * `key` - The license key string (e.g., "tl_free_...")
44    /// 
45    /// # Security
46    /// The key is immediately moved into secure storage and will be
47    /// zeroized when this instance goes out of scope.
48    pub fn new(key: String) -> Self {
49        Self { key }
50    }
51    
52    /// Returns the license key as a string slice
53    /// 
54    /// # Security Warning
55    /// This method provides access to the raw license key and should be used
56    /// sparingly. Prefer using the redacted version for logging and debugging.
57    /// 
58    /// # Usage
59    /// This method is primarily intended for internal SDK use when making
60    /// authenticated API requests.
61    pub(crate) fn as_str(&self) -> &str {
62        &self.key
63    }
64    
65    /// Returns a redacted version of the license key safe for logging
66    /// 
67    /// The redacted version shows only the first 3 and last 3 characters
68    /// of the license key, with the middle portion replaced by "...".
69    /// 
70    /// # Example
71    /// ```rust
72    /// # use truthlinked_sdk::LicenseKey;
73    /// let key = LicenseKey::new("tl_free_secret123456789".to_string());
74    /// assert_eq!(key.redacted(), "tl_...789");
75    /// ```
76    /// 
77    /// # Security
78    /// This method is safe to use in logs, error messages, and debug output
79    /// as it does not reveal the full license key.
80    pub fn redacted(&self) -> String {
81        let len = self.key.len();
82        if len > 8 {
83            format!("{}...{}", &self.key[..3], &self.key[len-3..])
84        } else {
85            "***".to_string()
86        }
87    }
88}
89
90impl std::fmt::Debug for LicenseKey {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        f.debug_struct("LicenseKey")
93            .field("key", &self.redacted())
94            .finish()
95    }
96}
97
98impl std::fmt::Display for LicenseKey {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        write!(f, "{}", self.redacted())
101    }
102}
103
104// Prevent accidental serialization
105impl serde::Serialize for LicenseKey {
106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107    where
108        S: serde::Serializer,
109    {
110        serializer.serialize_str(&self.redacted())
111    }
112}