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}