extern crate sodiumoxide;
extern crate libc;
extern crate rpassword;
extern crate base64;
use sodiumoxide::crypto::pwhash::*;
use sodiumoxide::crypto::pwhash;
use sodiumoxide::crypto::sign::*;
use sodiumoxide::crypto::sign;
use sodiumoxide::randombytes::randombytes;
use std::fs::File;
use std::io::{self, BufWriter, Write};
pub mod parse_args;
pub mod generichash;
pub mod perror;
pub mod types;
pub use generichash::*;
pub use parse_args::*;
pub use perror::*;
pub use types::*;
pub fn gen_keystruct() -> (PubkeyStruct, SeckeyStruct) {
let (pk, sk) = gen_keypair();
let SecretKey(sk) = sk;
let PublicKey(pk) = pk;
let keynum_vec = randombytes(KEYNUMBYTES);
let mut keynum = [0u8; KEYNUMBYTES];
keynum.copy_from_slice(keynum_vec.as_slice());
let kdf_salt_vec = randombytes(SALTBYTES);
let mut kdf_salt = [0u8; SALTBYTES];
kdf_salt.copy_from_slice(kdf_salt_vec.as_slice());
let OpsLimit(ops_limit) = OPSLIMIT_SENSITIVE;
let MemLimit(mem_limit) = MEMLIMIT_SENSITIVE;
let p_struct = PubkeyStruct {
sig_alg: SIGALG,
keynum_pk: KeynumPK {
keynum: keynum,
pk: pk,
},
};
let s_struct = SeckeyStruct {
sig_alg: SIGALG,
kdf_alg: KDFALG,
chk_alg: CHKALG,
kdf_salt: kdf_salt,
kdf_opslimit_le: store_usize_le(ops_limit),
kdf_memlimit_le: store_usize_le(mem_limit),
keynum_sk: KeynumSK {
keynum: keynum.clone(),
sk: sk,
chk: [0; BYTES],
},
};
(p_struct, s_struct)
}
pub fn get_password(prompt: &str) -> Result<String> {
let pwd = rpassword::prompt_password_stdout(prompt)?;
if pwd.len() == 0 {
println!("<empty>");
Ok(pwd)
} else if pwd.len() > PASSWORDMAXBYTES {
Err(PError::new(ErrorKind::Misc, "passphrase can't exceed 1024 bytes lenght"))
} else {
Ok(pwd)
}
}
pub fn store_usize_le(x: usize) -> [u8; 8] {
let b1: u8 = (x & 0xff) as u8;
let b2: u8 = ((x >> 8) & 0xff) as u8;
let b3: u8 = ((x >> 16) & 0xff) as u8;
let b4: u8 = ((x >> 24) & 0xff) as u8;
let b5: u8 = ((x >> 32) & 0xff) as u8;
let b6: u8 = ((x >> 40) & 0xff) as u8;
let b7: u8 = ((x >> 48) & 0xff) as u8;
let b8: u8 = ((x >> 56) & 0xff) as u8;
return [b1, b2, b3, b4, b5, b6, b7, b8];
}
pub fn load_usize_le(x: &[u8]) -> usize {
(x[0] as usize) | (x[1] as usize) << 8 | (x[2] as usize) << 16 | (x[3] as usize) << 24 |
(x[4] as usize) << 32 | (x[5] as usize) << 40 |
(x[6] as usize) << 48 | (x[7] as usize) << 56
}
pub fn verify(pk_key: PubkeyStruct,
sig: SigStruct,
global_sig: &[u8],
trusted_comment: &[u8],
message: &[u8],
quiet: bool,
output: bool)
-> Result<()> {
if sig.keynum != pk_key.keynum_pk.keynum {
return Err(PError::new(ErrorKind::Verify,
format!("Signature key id: {:X} is different from public key: {:X}",
load_usize_le(&sig.keynum[..]),
load_usize_le(&pk_key.keynum_pk.keynum[..]))));
}
Signature::from_slice(&sig.sig)
.ok_or(PError::new(ErrorKind::Verify,
"Couldn't compose message file signature from bytes"))
.and_then(|signature| {
PublicKey::from_slice(&pk_key.keynum_pk.pk)
.ok_or(PError::new(ErrorKind::Verify,
"Couldn't compose a public key from bytes"))
.and_then(|pk| if sign::verify_detached(&signature, &message, &pk) {
Ok(pk)
} else {
Err(PError::new(ErrorKind::Verify, "Signature verification failed"))
})
.and_then(|pk| {
Signature::from_slice(&global_sig[..])
.ok_or(PError::new(ErrorKind::Verify,
"Couldn't compose trusted comment signature from bytes"))
.and_then(|global_sig| if sign::verify_detached(&global_sig,
&trusted_comment,
&pk) {
let just_comment =
String::from_utf8(trusted_comment[SIGNATUREBYTES..]
.to_vec())?;
if !quiet {
println!("Signature and comment signature verified");
println!("Trusted comment: {}", just_comment);
}
if output {
print!("{}", String::from_utf8_lossy(&message[..]));
}
Ok(())
} else {
return Err(PError::new(ErrorKind::Verify,
"Comment signature verification \
failed"));
})
})
})
}
pub fn sign<W>(sk_key: SeckeyStruct,
pk_key: Option<PubkeyStruct>,
mut sig_buf: W,
message: &[u8],
hashed: bool,
trusted_comment: &str,
untrusted_comment: &str)
-> Result<()>
where W: Write
{
let mut sig_str = SigStruct::default();
if !hashed {
sig_str.sig_alg = sk_key.sig_alg.clone();
} else {
sig_str.sig_alg = SIGALG_HASHED;
}
sig_str
.keynum
.copy_from_slice(&sk_key.keynum_sk.keynum[..]);
let sk = SecretKey::from_slice(&sk_key.keynum_sk.sk)
.ok_or(PError::new(ErrorKind::Sign, "Couldn't generate secret key from bytes"))?;
let signature = sodiumoxide::crypto::sign::sign_detached(message, &sk);
sig_str.sig.copy_from_slice(&signature[..]);
let mut sig_and_trust_comment: Vec<u8> = vec![];
sig_and_trust_comment.extend(sig_str.sig.iter());
sig_and_trust_comment.extend(trusted_comment.as_bytes().iter());
let global_sig = sodiumoxide::crypto::sign::sign_detached(&sig_and_trust_comment, &sk);
if let Some(pk_str) = pk_key {
PublicKey::from_slice(&pk_str.keynum_pk.pk[..])
.ok_or(PError::new(ErrorKind::Sign, "failed to obtain public key from bytes"))
.and_then(|pk|{
if !sodiumoxide::crypto::sign::verify_detached(&global_sig, &sig_and_trust_comment, &pk) {
Err(PError::new(ErrorKind::Verify,format!("Could not verify signature with the \
provided public key ID: {:X}", load_usize_le(&pk_str.keynum_pk.keynum[..]))))
} else {
println!("\nSignature checked with the public key ID: {:X}",
load_usize_le(&pk_str.keynum_pk.keynum[..]));
Ok(())
}
})?;
}
writeln!(sig_buf, "{}", untrusted_comment)?;
writeln!(sig_buf, "{}", base64::encode(&sig_str.bytes()))?;
writeln!(sig_buf, "{}{}", TRUSTED_COMMENT_PREFIX, trusted_comment)?;
writeln!(sig_buf, "{}", base64::encode(&global_sig[..]))?;
sig_buf.flush()?;
Ok(())
}
pub fn generate(mut pk_file: BufWriter<File>,
mut sk_file: BufWriter<File>,
comment: Option<&str>)
-> Result<(PubkeyStruct, SeckeyStruct)> {
let (pk_str, mut sk_str) = gen_keystruct();
sk_str
.write_checksum()
.map_err(|_| PError::new(ErrorKind::Generate, "failed to hash and write checksum!"))?;
write!(io::stdout(),
"Please enter a password to protect the secret key.\n")?;
let pwd = get_password("Password: ")?;
let pwd2 = get_password("Password (one more time): ")?;
if pwd != pwd2 {
return Err(PError::new(ErrorKind::Generate, "passwords don't match!"));
}
write!(io::stdout(),
"Deriving a key from the password in order to encrypt the secret key... ")
.map_err(|e| PError::new(ErrorKind::Io, e))
.and_then(|_| {
io::stdout().flush()?;
derive_and_crypt(&mut sk_str, &pwd.as_bytes())
})
.and(writeln!(io::stdout(), "done").map_err(|e| PError::new(ErrorKind::Io, e)))?;
write!(pk_file, "{}rsign public key: ", COMMENT_PREFIX)?;
writeln!(pk_file, "{:X}", load_usize_le(&pk_str.keynum_pk.keynum[..]))?;
writeln!(pk_file, "{}", base64::encode(&pk_str.bytes()))?;
pk_file.flush()?;
write!(sk_file, "{}", COMMENT_PREFIX)?;
if let Some(comment) = comment {
writeln!(sk_file, "{}", comment)?;
} else {
writeln!(sk_file, "{}", SECRETKEY_DEFAULT_COMMENT)?;
}
writeln!(sk_file, "{}", base64::encode(&sk_str.bytes()))?;
sk_file.flush()?;
Ok((pk_str, sk_str))
}
pub fn derive_and_crypt(sk_str: &mut SeckeyStruct, pwd: &[u8]) -> Result<()> {
let mut stream = [0u8; BYTES + SECRETKEYBYTES + KEYNUMBYTES];
pwhash::Salt::from_slice(&sk_str.kdf_salt)
.ok_or(PError::new(ErrorKind::Misc, "failed to generate Salt from random bytes"))
.and_then(|salt| {
pwhash::derive_key(&mut stream,
&pwd,
&salt,
OpsLimit(load_usize_le(&sk_str.kdf_opslimit_le)),
MemLimit(load_usize_le(&sk_str.kdf_memlimit_le)))
.map_err(|_| PError::new(ErrorKind::Misc, "failed to derive key from password"))
})?;
sk_str.xor_keynum(&stream);
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn byte_array_store() {
use store_usize_le;
assert_eq!([0xFF, 0, 0, 0, 0, 0, 0, 0], store_usize_le(0xFF));
}
#[test]
fn byte_array_load() {
use load_usize_le;
assert_eq!(255, load_usize_le(&[0xFF, 0, 0, 0, 0, 0, 0, 0]));
}
#[test]
fn pk_key_struct_conversion() {
use gen_keystruct;
use PubkeyStruct;
let (pk, _) = gen_keystruct();
assert_eq!(pk, PubkeyStruct::from(&pk.bytes()).unwrap());
}
#[test]
fn sk_key_struct_conversion() {
use gen_keystruct;
use SeckeyStruct;
let (_, sk) = gen_keystruct();
assert_eq!(sk, SeckeyStruct::from(&sk.bytes()).unwrap());
}
#[test]
fn xor_keynum() {
use randombytes;
use gen_keystruct;
let (_, mut sk) = gen_keystruct();
let key = randombytes(sk.keynum_sk.len());
let original_keynum = sk.keynum_sk.clone();
sk.xor_keynum(&key);
assert_ne!(original_keynum, sk.keynum_sk);
sk.xor_keynum(&key);
assert_eq!(original_keynum, sk.keynum_sk);
}
#[test]
fn sk_checksum() {
use gen_keystruct;
let (_, mut sk) = gen_keystruct();
assert!(sk.write_checksum().is_ok());
assert_eq!(sk.keynum_sk.chk.to_vec(), sk.read_checksum().unwrap());
}
}