cargo_pwhide/
lib.rs

1//! cargo-pwhide provides tooling to hide passwords  
2//!     
3//!     
4//! **This doesn't provide any security**
5//! 
6//! It consists of a cargo subcommand to encrypt passwords and a simple macro for decrypting
7//! 
8//! # Encryption
9//! 
10//! install the cargo subcommand with:  
11//! **cargo install cargo-pwhide**
12//! 
13//! Then go to the root of your project where the Cargo.toml is   
14//! and encrypt a password
15//! 
16//! **cargo pwhide encrypt MySecretPassword**
17//! 
18//! This will deliver an encrypted data for example:
19//! 
20//! QDc2rswTJRHrFEgT2Ech77xuScNGfGGKFIJq6MJcI1lKg1hfaowsg5
21//! 
22//! Use this in configuration files on in the command code
23//! 
24//! # Decryption in the program
25//! 
26//! Add to your Config.toml   
27//! ```
28//! cargo-pwhide = {version="*", feature=["lib"], default-features = false}**
29//! ``` 
30//!    
31//! This reduces the number of dependencies and is great for compile time :)
32//! 
33//! For decryption just use the provided macro
34//! 
35//! ```
36//! use cargo_pwhide::pwdecrypt;    
37//! let cleartext:Result<String,anyhow::Error>=pwdecrypt!("QDc2rswTJRHrFEgT2Ech77xuScNGfGGKFIJq6MJcI1lKg1hfaowsg5");  
38//! ```
39//! 
40//! and automatically decrpytion is done
41//! 
42//! # how does it work?
43//! 
44//! The tool is using the package name as secret
45//! The password is encrypted using ChaCha20Poly1305 and a random nounce
46//! 
47//! This doesn't provide any security but is better compared to storing plain text passwords in version control
48//! 
49
50
51use anyhow::anyhow;
52use chacha20poly1305::aead::generic_array::GenericArray;
53use chacha20poly1305::aead::Aead;
54use chacha20poly1305::aead::OsRng;
55use chacha20poly1305::AeadCore;
56use chacha20poly1305::ChaCha20Poly1305;
57use chacha20poly1305::KeyInit;
58use sha2::{Digest, Sha256};
59
60
61
62
63
64/// The macro is used for easy decrpytion of password  
65/// 
66/// 
67/// The macro expands to    
68/// pwdecrpyt(env!("CARGO_PKG_NAME"),parameter) -> String
69/// 
70/// # Example
71/// ```  
72/// use cargo_pwhide::pwdecrypt;    
73/// let cleartext:Result<String,anyhow::Error>=pwdecrypt!("QDc2rswTJRHrFEgT2Ech77xuScNGfGGKFIJq6MJcI1lKg1hfaowsg5");     
74/// ```
75///     
76/// it returns are result with the string if decryption was possible  
77/// The secret is the package name   
78/// 
79#[macro_export]
80macro_rules! pwdecrypt {
81    ($value:expr) => {
82        cargo_pwhide::pwdecrypt(env!("CARGO_PKG_NAME"), $value)
83    };
84}
85
86/// This is used to decrpyt the password encrypted with subcommand tools cargo pwhide encrypt
87pub fn pwdecrypt(
88    packagename: impl Into<String>,
89    data: impl Into<String>,
90) -> anyhow::Result<String> {
91    let secret: String = packagename.into();
92    let mut hasher = Sha256::new();
93    hasher.update(secret);
94
95    let cipher = ChaCha20Poly1305::new(&hasher.finalize());
96
97    let value: String = data.into();
98    if value.len() < 18 {
99        return Err(anyhow!(
100            "Decoding not possible, encrypted value is too short"
101        ));
102    }
103    let (nonce, text) = value.split_at(17);
104    let nonce = if let Ok(dec) = base_62::decode(nonce) {
105        GenericArray::clone_from_slice(dec.as_slice())
106    } else {
107        return Err(anyhow!("Decoding not possible, encrypted value is invalid"));
108    };
109
110    let text = if let Ok(dec) = base_62::decode(text) {
111        dec
112    } else {
113        return Err(anyhow!("Decoding not possible, encrypted value is invalid"));
114    };
115    let plain = match cipher.decrypt(&nonce, text.as_ref()) {
116        Ok(dec) => dec,
117        Err(e) => {
118            return Err(anyhow!("Decryption failed with {:?}", e));
119        }
120    };
121    if let Ok(dec) = String::from_utf8(plain) {
122        Ok(dec)
123    } else {
124        Err(anyhow!("Decryption failed decypted data is no vailid text"))
125    }
126}
127
128
129/// This is used to encrpyt a password function used by subcommand tools cargo pwhide encrypt
130pub fn pwencrypt(
131    packagename: impl Into<String>,
132    data: impl Into<String>,
133) -> anyhow::Result<String> {
134    let secret: String = packagename.into();
135    let mut hasher = Sha256::new();
136    hasher.update(secret);
137
138    let cipher = ChaCha20Poly1305::new(&hasher.finalize());
139
140    let value: String = data.into();
141
142    let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
143    let ciphertext = cipher.encrypt(&nonce, value.as_bytes().as_ref());
144    match ciphertext {
145        Err(e) => Err(anyhow!(
146            "Encryption failed with error {:?} -raise github issue",
147            e
148        )),
149        Ok(data) => Ok(format!(
150            "{}{}",
151            base_62::encode(nonce.as_slice()),
152            base_62::encode(data.as_slice())
153        )),
154    }
155}