rsign/
lib.rs

1extern crate sodiumoxide;
2extern crate libc;
3
4extern crate rpassword;
5extern crate base64;
6
7use sodiumoxide::crypto::pwhash::*;
8use sodiumoxide::crypto::pwhash;
9use sodiumoxide::crypto::sign::*;
10use sodiumoxide::crypto::sign;
11use sodiumoxide::randombytes::randombytes;
12
13
14use std::fs::File;
15use std::io::{self, BufWriter, Write};
16
17
18pub mod parse_args;
19pub mod generichash;
20pub mod perror;
21pub mod types;
22
23pub use generichash::*;
24pub use parse_args::*;
25pub use perror::*;
26pub use types::*;
27
28
29pub fn gen_keystruct() -> (PubkeyStruct, SeckeyStruct) {
30    let (pk, sk) = gen_keypair();
31    let SecretKey(sk) = sk;
32    let PublicKey(pk) = pk;
33
34    let keynum_vec = randombytes(KEYNUMBYTES);
35    let mut keynum = [0u8; KEYNUMBYTES];
36    keynum.copy_from_slice(keynum_vec.as_slice());
37
38    let kdf_salt_vec = randombytes(SALTBYTES);
39    let mut kdf_salt = [0u8; SALTBYTES];
40    kdf_salt.copy_from_slice(kdf_salt_vec.as_slice());
41
42    let OpsLimit(ops_limit) = OPSLIMIT_SENSITIVE;
43    let MemLimit(mem_limit) = MEMLIMIT_SENSITIVE;
44
45    let p_struct = PubkeyStruct {
46        sig_alg: SIGALG,
47        keynum_pk: KeynumPK {
48            keynum: keynum,
49            pk: pk,
50        },
51    };
52    let s_struct = SeckeyStruct {
53        sig_alg: SIGALG,
54        kdf_alg: KDFALG,
55        chk_alg: CHKALG,
56        kdf_salt: kdf_salt,
57        kdf_opslimit_le: store_usize_le(ops_limit),
58        kdf_memlimit_le: store_usize_le(mem_limit),
59        keynum_sk: KeynumSK {
60            keynum: keynum.clone(),
61            sk: sk,
62            chk: [0; BYTES],
63        },
64    };
65    (p_struct, s_struct)
66}
67
68pub fn get_password(prompt: &str) -> Result<String> {
69    let pwd = rpassword::prompt_password_stdout(prompt)?;
70    if pwd.len() == 0 {
71        println!("<empty>");
72        Ok(pwd)
73    } else if pwd.len() > PASSWORDMAXBYTES {
74        Err(PError::new(ErrorKind::Misc, "passphrase can't exceed 1024 bytes lenght"))
75    } else {
76        Ok(pwd)
77    }
78}
79
80pub fn store_usize_le(x: usize) -> [u8; 8] {
81    let b1: u8 = (x & 0xff) as u8;
82    let b2: u8 = ((x >> 8) & 0xff) as u8;
83    let b3: u8 = ((x >> 16) & 0xff) as u8;
84    let b4: u8 = ((x >> 24) & 0xff) as u8;
85    let b5: u8 = ((x >> 32) & 0xff) as u8;
86    let b6: u8 = ((x >> 40) & 0xff) as u8;
87    let b7: u8 = ((x >> 48) & 0xff) as u8;
88    let b8: u8 = ((x >> 56) & 0xff) as u8;
89    return [b1, b2, b3, b4, b5, b6, b7, b8];
90}
91
92pub fn load_usize_le(x: &[u8]) -> usize {
93    (x[0] as usize) | (x[1] as usize) << 8 | (x[2] as usize) << 16 | (x[3] as usize) << 24 |
94    (x[4] as usize) << 32 | (x[5] as usize) << 40 |
95    (x[6] as usize) << 48 | (x[7] as usize) << 56
96}
97pub fn verify(pk_key: PubkeyStruct,
98              sig: SigStruct,
99              global_sig: &[u8],
100              trusted_comment: &[u8],
101              message: &[u8],
102              quiet: bool,
103              output: bool)
104              -> Result<()> {
105
106    if sig.keynum != pk_key.keynum_pk.keynum {
107        return Err(PError::new(ErrorKind::Verify,
108                               format!("Signature key id: {:X} is different from public key: {:X}",
109                                       load_usize_le(&sig.keynum[..]),
110                                       load_usize_le(&pk_key.keynum_pk.keynum[..]))));
111    }
112    Signature::from_slice(&sig.sig)
113        .ok_or(PError::new(ErrorKind::Verify,
114                           "Couldn't compose message file signature from bytes"))
115        .and_then(|signature| {
116            PublicKey::from_slice(&pk_key.keynum_pk.pk)
117                .ok_or(PError::new(ErrorKind::Verify,
118                                   "Couldn't compose a public key from bytes"))
119                .and_then(|pk| if sign::verify_detached(&signature, &message, &pk) {
120                              Ok(pk)
121                          } else {
122                              Err(PError::new(ErrorKind::Verify, "Signature verification failed"))
123                          })
124                .and_then(|pk| {
125                    Signature::from_slice(&global_sig[..])
126                        .ok_or(PError::new(ErrorKind::Verify,
127                                           "Couldn't compose trusted comment signature from bytes"))
128                        .and_then(|global_sig| if sign::verify_detached(&global_sig,
129                                                                        &trusted_comment,
130                                                                        &pk) {
131                                      let just_comment =
132                                          String::from_utf8(trusted_comment[SIGNATUREBYTES..]
133                                                                .to_vec())?;
134                                      if !quiet {
135                                          println!("Signature and comment signature verified");
136                                          println!("Trusted comment: {}", just_comment);
137                                      }
138                                      if output {
139                                          print!("{}", String::from_utf8_lossy(&message[..]));
140                                      }
141                                      Ok(())
142                                  } else {
143                                      return Err(PError::new(ErrorKind::Verify,
144                                                             "Comment signature verification \
145                                                              failed"));
146                                  })
147                })
148        })
149}
150
151pub fn sign<W>(sk_key: SeckeyStruct,
152            pk_key: Option<PubkeyStruct>,
153            mut sig_buf: W,
154            message: &[u8],
155            hashed: bool,
156            trusted_comment: &str,
157            untrusted_comment: &str)
158            -> Result<()>
159    where W: Write            
160{
161
162    let mut sig_str = SigStruct::default();
163    if !hashed {
164        sig_str.sig_alg = sk_key.sig_alg.clone();
165    } else {
166        sig_str.sig_alg = SIGALG_HASHED;
167    }
168    sig_str
169        .keynum
170        .copy_from_slice(&sk_key.keynum_sk.keynum[..]);
171
172    let sk = SecretKey::from_slice(&sk_key.keynum_sk.sk)
173        .ok_or(PError::new(ErrorKind::Sign, "Couldn't generate secret key from bytes"))?;
174
175    let signature = sodiumoxide::crypto::sign::sign_detached(message, &sk);
176
177    sig_str.sig.copy_from_slice(&signature[..]);
178
179    let mut sig_and_trust_comment: Vec<u8> = vec![];
180    sig_and_trust_comment.extend(sig_str.sig.iter());
181    sig_and_trust_comment.extend(trusted_comment.as_bytes().iter());
182
183    let global_sig = sodiumoxide::crypto::sign::sign_detached(&sig_and_trust_comment, &sk);
184
185    if let Some(pk_str) = pk_key {
186        PublicKey::from_slice(&pk_str.keynum_pk.pk[..])
187            .ok_or(PError::new(ErrorKind::Sign, "failed to obtain public key from bytes"))
188            .and_then(|pk|{
189                   if !sodiumoxide::crypto::sign::verify_detached(&global_sig, &sig_and_trust_comment, &pk) {
190                       Err(PError::new(ErrorKind::Verify,format!("Could not verify signature with the \
191                        provided public key ID: {:X}", load_usize_le(&pk_str.keynum_pk.keynum[..]))))
192                    } else {
193                        println!("\nSignature checked with the public key ID: {:X}",
194                                load_usize_le(&pk_str.keynum_pk.keynum[..]));
195                                Ok(())
196                    } 
197            })?;
198    }
199
200    writeln!(sig_buf, "{}", untrusted_comment)?;
201    writeln!(sig_buf, "{}", base64::encode(&sig_str.bytes()))?;
202    writeln!(sig_buf, "{}{}", TRUSTED_COMMENT_PREFIX, trusted_comment)?;
203    writeln!(sig_buf, "{}", base64::encode(&global_sig[..]))?;
204    sig_buf.flush()?;
205    Ok(())
206}
207
208pub fn generate(mut pk_file: BufWriter<File>,
209                mut sk_file: BufWriter<File>,
210                comment: Option<&str>)
211                -> Result<(PubkeyStruct, SeckeyStruct)> {
212    let (pk_str, mut sk_str) = gen_keystruct();
213    sk_str
214        .write_checksum()
215        .map_err(|_| PError::new(ErrorKind::Generate, "failed to hash and write checksum!"))?;
216    write!(io::stdout(),
217           "Please enter a password to protect the secret key.\n")?;
218    let pwd = get_password("Password: ")?;
219    let pwd2 = get_password("Password (one more time): ")?;
220    if pwd != pwd2 {
221        return Err(PError::new(ErrorKind::Generate, "passwords don't match!"));
222    }
223
224    write!(io::stdout(),
225           "Deriving a key from the password in order to encrypt the secret key... ")
226            .map_err(|e| PError::new(ErrorKind::Io, e))
227            .and_then(|_| {
228                          io::stdout().flush()?;
229                          derive_and_crypt(&mut sk_str, &pwd.as_bytes())
230                      })
231            .and(writeln!(io::stdout(), "done").map_err(|e| PError::new(ErrorKind::Io, e)))?;
232
233
234    write!(pk_file, "{}rsign public key: ", COMMENT_PREFIX)?;
235    writeln!(pk_file, "{:X}", load_usize_le(&pk_str.keynum_pk.keynum[..]))?;
236    writeln!(pk_file, "{}", base64::encode(&pk_str.bytes()))?;
237    pk_file.flush()?;
238
239
240    write!(sk_file, "{}", COMMENT_PREFIX)?;
241    if let Some(comment) = comment {
242        writeln!(sk_file, "{}", comment)?;
243    } else {
244        writeln!(sk_file, "{}", SECRETKEY_DEFAULT_COMMENT)?;
245    }
246    writeln!(sk_file, "{}", base64::encode(&sk_str.bytes()))?;
247    sk_file.flush()?;
248
249    Ok((pk_str, sk_str))
250}
251
252pub fn derive_and_crypt(sk_str: &mut SeckeyStruct, pwd: &[u8]) -> Result<()> {
253    let mut stream = [0u8; BYTES + SECRETKEYBYTES + KEYNUMBYTES];
254    pwhash::Salt::from_slice(&sk_str.kdf_salt)
255        .ok_or(PError::new(ErrorKind::Misc, "failed to generate Salt from random bytes"))
256        .and_then(|salt| {
257
258            pwhash::derive_key(&mut stream,
259                               &pwd,
260                               &salt,
261                               OpsLimit(load_usize_le(&sk_str.kdf_opslimit_le)),
262                               MemLimit(load_usize_le(&sk_str.kdf_memlimit_le)))
263                    .map_err(|_| PError::new(ErrorKind::Misc, "failed to derive key from password"))
264
265        })?;
266    sk_str.xor_keynum(&stream);
267    Ok(())
268}
269
270#[cfg(test)]
271mod tests {
272
273    #[test]
274    fn byte_array_store() {
275        use store_usize_le;
276        assert_eq!([0xFF, 0, 0, 0, 0, 0, 0, 0], store_usize_le(0xFF));
277    }
278    #[test]
279    fn byte_array_load() {
280        use load_usize_le;
281        assert_eq!(255, load_usize_le(&[0xFF, 0, 0, 0, 0, 0, 0, 0]));
282    }
283
284    #[test]
285    fn pk_key_struct_conversion() {
286        use gen_keystruct;
287        use PubkeyStruct;
288        let (pk, _) = gen_keystruct();
289        assert_eq!(pk, PubkeyStruct::from(&pk.bytes()).unwrap());
290    }
291    #[test]
292    fn sk_key_struct_conversion() {
293        use gen_keystruct;
294        use SeckeyStruct;
295        let (_, sk) = gen_keystruct();
296        assert_eq!(sk, SeckeyStruct::from(&sk.bytes()).unwrap());
297    }
298
299    #[test]
300    fn xor_keynum() {
301        use randombytes;
302        use gen_keystruct;
303        let (_, mut sk) = gen_keystruct();
304        let key = randombytes(sk.keynum_sk.len());
305        let original_keynum = sk.keynum_sk.clone();
306        sk.xor_keynum(&key);
307        assert_ne!(original_keynum, sk.keynum_sk);
308        sk.xor_keynum(&key);
309        assert_eq!(original_keynum, sk.keynum_sk);
310
311    }
312    #[test]
313    fn sk_checksum() {
314        use gen_keystruct;
315        let (_, mut sk) = gen_keystruct();
316        assert!(sk.write_checksum().is_ok());
317        assert_eq!(sk.keynum_sk.chk.to_vec(), sk.read_checksum().unwrap());
318
319    }
320}