#![cfg_attr(feature="cargo-clippy", allow(new_without_default))]
use std::cmp;
use std::fmt;
#[cfg(test)]
use std::io::Write;
use data_encoding::{HEXLOWER, HEXLOWER_PERMISSIVE};
use rust_sodium::crypto::{box_, secretbox};
use rust_sodium_sys::crypto_scalarmult_base;
use serde::ser::{Serialize, Serializer};
use serde::de::{Deserialize, Deserializer, Visitor, Error as SerdeError};
use crate::errors::{SaltyResult, SaltyError, SignalingResult, SignalingError};
use crate::helpers::{libsodium_init_or_panic};
use crate::protocol::Nonce;
pub type PublicKey = box_::PublicKey;
pub type PrivateKey = box_::SecretKey;
pub type SecretKey = secretbox::Key;
pub fn public_key_from_hex_str(hex_str: &str) -> SaltyResult<PublicKey> {
let bytes = HEXLOWER_PERMISSIVE.decode(hex_str.as_bytes())
.map_err(|_| SaltyError::Decode("Could not decode public key hex string".to_string()))?;
PublicKey::from_slice(&bytes)
.ok_or_else(|| SaltyError::Decode("Invalid public key hex string".to_string()))
}
#[allow(dead_code)]
pub fn private_key_from_hex_str(hex_str: &str) -> SaltyResult<PrivateKey> {
let bytes = HEXLOWER_PERMISSIVE.decode(hex_str.as_bytes())
.map_err(|_| SaltyError::Decode("Could not decode private key hex string".to_string()))?;
PrivateKey::from_slice(&bytes)
.ok_or_else(|| SaltyError::Decode("Invalid private key hex string".to_string()))
}
#[derive(Debug, PartialEq, Eq)]
pub struct KeyPair {
public_key: PublicKey,
private_key: PrivateKey,
}
impl KeyPair {
pub fn new() -> Self {
info!("Generating new key pair");
libsodium_init_or_panic();
let (pk, sk) = box_::gen_keypair();
trace!("Public key: {:?}", pk);
KeyPair {
public_key: pk,
private_key: sk,
}
}
pub fn from_private_key(private_key: PrivateKey) -> Self {
let public_key = unsafe {
let mut buf = [0u8; box_::PUBLICKEYBYTES];
crypto_scalarmult_base(buf.as_mut_ptr(), private_key.0.as_ptr());
box_::PublicKey(buf)
};
KeyPair { public_key, private_key }
}
pub fn from_keypair(public_key: PublicKey, private_key: PrivateKey) -> Self {
KeyPair { public_key, private_key }
}
pub fn public_key(&self) -> &PublicKey {
&self.public_key
}
pub fn public_key_hex(&self) -> String {
HEXLOWER.encode(&self.public_key.0)
}
pub fn private_key(&self) -> &PrivateKey {
&self.private_key
}
pub fn private_key_hex(&self) -> String {
HEXLOWER.encode(&self.private_key.0)
}
pub(crate) fn encrypt(&self, data: &[u8], nonce: Nonce, other_key: &PublicKey) -> Vec<u8> {
let rust_sodium_nonce: box_::Nonce = nonce.into();
box_::seal(data, &rust_sodium_nonce, other_key, &self.private_key)
}
pub(crate) fn decrypt(&self, data: &[u8], nonce: Nonce, other_key: &PublicKey) -> SignalingResult<Vec<u8>> {
let rust_sodium_nonce: box_::Nonce = nonce.into();
box_::open(data, &rust_sodium_nonce, other_key, &self.private_key)
.map_err(|_| SignalingError::Crypto("Could not decrypt data".to_string()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthToken(SecretKey);
impl AuthToken {
pub fn new() -> Self {
info!("Generating new auth token");
libsodium_init_or_panic();
let key = secretbox::gen_key();
AuthToken(key)
}
pub fn from_hex_str(hex_str: &str) -> SaltyResult<Self> {
let bytes = HEXLOWER_PERMISSIVE.decode(hex_str.as_bytes())
.map_err(|e| SaltyError::Decode(format!("Could not decode auth token hex string: {}", e)))?;
let key = SecretKey::from_slice(&bytes)
.ok_or_else(|| SaltyError::Decode("Invalid auth token hex string".to_string()))?;
Ok(AuthToken(key))
}
pub fn from_slice(hex_str: &[u8]) -> SaltyResult<Self> {
if hex_str.len() != 32 {
return Err(SaltyError::Decode(
"Invalid auth token bytes: Slice must be 32 bytes long".into()
));
}
let key = SecretKey::from_slice(hex_str)
.ok_or_else(|| SaltyError::Decode(
"Invalid auth token bytes: Could not create SecretKey".into()
))?;
Ok(AuthToken(key))
}
pub fn secret_key(&self) -> &SecretKey {
&self.0
}
pub fn secret_key_bytes(&self) -> &[u8] {
&(self.0).0
}
pub(crate) fn encrypt(&self, plaintext: &[u8], nonce: Nonce) -> Vec<u8> {
let rust_sodium_nonce: secretbox::Nonce = nonce.into();
secretbox::seal(plaintext, &rust_sodium_nonce, self.secret_key())
}
pub(crate) fn decrypt(&self, ciphertext: &[u8], nonce: Nonce) -> SignalingResult<Vec<u8>> {
let rust_sodium_nonce: secretbox::Nonce = nonce.into();
secretbox::open(ciphertext, &rust_sodium_nonce, self.secret_key())
.map_err(|_| SignalingError::Crypto("Could not decrypt data".to_string()))
}
}
const SIGNED_KEYS_BYTES: usize = 2 * box_::PUBLICKEYBYTES + box_::MACBYTES;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnsignedKeys {
pub server_public_session_key: PublicKey,
pub client_public_permanent_key: PublicKey,
}
impl UnsignedKeys {
pub fn new(server_public_session_key: PublicKey, client_public_permanent_key: PublicKey) -> Self {
Self { server_public_session_key, client_public_permanent_key }
}
#[cfg(test)]
pub(crate) fn sign(
self,
server_session_keypair: &KeyPair,
client_public_permanent_key: &PublicKey,
nonce: Nonce,
) -> SignedKeys {
let mut bytes = [0u8; 64];
(&mut bytes[0..32]).write_all(&self.server_public_session_key.0).unwrap();
(&mut bytes[32..64]).write_all(&self.client_public_permanent_key.0).unwrap();
let rust_sodium_nonce: box_::Nonce = nonce.into();
let vec = box_::seal(
&bytes,
&rust_sodium_nonce,
client_public_permanent_key,
server_session_keypair.private_key(),
);
assert_eq!(vec.len(), SIGNED_KEYS_BYTES);
let mut encrypted = [0u8; SIGNED_KEYS_BYTES];
(&mut encrypted[..]).write_all(&vec).unwrap();
SignedKeys(encrypted)
}
}
pub struct SignedKeys([u8; SIGNED_KEYS_BYTES]);
impl SignedKeys {
pub fn new(bytes: [u8; SIGNED_KEYS_BYTES]) -> Self {
SignedKeys(bytes)
}
pub(crate) fn decrypt(
&self,
permanent_key: &KeyPair,
server_public_permanent_key: &PublicKey,
nonce: Nonce,
) -> SignalingResult<UnsignedKeys> {
let rust_sodium_nonce: box_::Nonce = nonce.into();
let decrypted = box_::open(
&self.0,
&rust_sodium_nonce,
server_public_permanent_key,
permanent_key.private_key(),
).map_err(|_| SignalingError::Crypto("Could not decrypt signed keys".to_string()))?;
assert_eq!(decrypted.len(), 32 * 2);
Ok(UnsignedKeys::new(
PublicKey::from_slice(&decrypted[0..32]).unwrap(),
PublicKey::from_slice(&decrypted[32..64]).unwrap(),
))
}
}
impl Clone for SignedKeys {
fn clone(&self) -> Self {
SignedKeys(self.0)
}
}
impl fmt::Debug for SignedKeys {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.0[..].fmt(formatter)
}
}
impl cmp::PartialEq<SignedKeys> for SignedKeys {
fn eq(&self, other: &SignedKeys) -> bool {
self.0[..].eq(&other.0[..])
}
}
impl Serialize for SignedKeys {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
serializer.serialize_bytes(&self.0)
}
}
struct SignedKeysVisitor;
impl<'de> Visitor<'de> for SignedKeysVisitor {
type Value = SignedKeys;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("80 bytes of binary data")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> where E: SerdeError {
if v.len() != SIGNED_KEYS_BYTES {
return Err(SerdeError::invalid_length(v.len(), &self));
}
Ok(SignedKeys::new([
v[ 0], v[ 1], v[ 2], v[ 3], v[ 4], v[ 5], v[ 6], v[ 7],
v[ 8], v[ 9], v[10], v[11], v[12], v[13], v[14], v[15],
v[16], v[17], v[18], v[19], v[20], v[21], v[22], v[23],
v[24], v[25], v[26], v[27], v[28], v[29], v[30], v[31],
v[32], v[33], v[34], v[35], v[36], v[37], v[38], v[39],
v[40], v[41], v[42], v[43], v[44], v[45], v[46], v[47],
v[48], v[49], v[50], v[51], v[52], v[53], v[54], v[55],
v[56], v[57], v[58], v[59], v[60], v[61], v[62], v[63],
v[64], v[65], v[66], v[67], v[68], v[69], v[70], v[71],
v[72], v[73], v[74], v[75], v[76], v[77], v[78], v[79],
]))
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E> where E: SerdeError {
self.visit_bytes(&v)
}
}
impl<'de> Deserialize<'de> for SignedKeys {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
deserializer.deserialize_bytes(SignedKeysVisitor)
}
}
#[cfg(test)]
use crate::test_helpers::TestRandom;
#[cfg(test)]
impl TestRandom for PublicKey {
fn random() -> PublicKey {
use rust_sodium::randombytes::randombytes_into;
libsodium_init_or_panic();
let mut rand = [0; 32];
randombytes_into(&mut rand);
PublicKey::from_slice(&rand).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
for _ in 0..255 {
let ks1 = KeyPair::new();
let ks2 = KeyPair::new();
assert_ne!(ks1.public_key(), ks2.public_key());
assert_ne!(ks1.private_key(), ks2.private_key());
assert_ne!(ks1, ks2);
}
}
#[test]
fn from_private_key() {
for _ in 0..255 {
let ks1 = KeyPair::new();
let ks2 = KeyPair::from_private_key(ks1.private_key().clone());
assert_eq!(ks1.public_key(), ks2.public_key());
}
}
#[test]
fn from_keypair() {
for _ in 0..255 {
let ks1 = KeyPair::new();
let ks2 = KeyPair::new();
let ks3 = KeyPair::from_keypair(ks1.public_key().clone(), ks1.private_key().clone());
assert_ne!(ks1, ks2);
assert_ne!(ks2, ks3);
assert_eq!(ks1, ks3);
}
}
#[test]
fn from_private_key_precomputed() {
let sk_hex = b"8bb6b6ae1497bf0288e6f82923e8875f2fdeab2ab6833e770182b35936232af9";
let sk_bytes = HEXLOWER.decode(sk_hex).unwrap();
let sk = PrivateKey::from_slice(&sk_bytes).unwrap();
let ks = KeyPair::from_private_key(sk);
assert_eq!(
ks.public_key_hex(),
"133798235bc42d37ce009b4b202cfe08bfd133c8e6eea75037fabb88f01fd959"
);
}
#[test]
fn encrypt_precomputed() {
let sk_hex = b"8bb6b6ae1497bf0288e6f82923e8875f2fdeab2ab6833e770182b35936232af9";
let sk_bytes = HEXLOWER.decode(sk_hex).unwrap();
let sk = PrivateKey::from_slice(&sk_bytes).unwrap();
let other_key_hex = b"424291495954d3fa8ffbcecc99b208f49016096ef84dffe33355cbc1f0348b20";
let other_key_bytes = HEXLOWER.decode(other_key_hex).unwrap();
let other_key = PublicKey::from_slice(&other_key_bytes).unwrap();
let nonce_hex = b"fe381c4bdb8bfc2a27d2c9a6485113e7638613ffb02b3747";
let nonce_bytes = HEXLOWER.decode(nonce_hex).unwrap();
let nonce = Nonce::from_bytes(&nonce_bytes).unwrap();
let ks = KeyPair::from_private_key(sk);
let plaintext = b"hello";
let encrypted = ks.encrypt(plaintext, nonce, &other_key);
let encrypted_hex = HEXLOWER.encode(&encrypted);
assert_eq!(encrypted_hex, "687f2cb605d80a0660bacb2c6ce6e076591b58f9c9");
}
#[test]
fn decrypt_precomputed() {
let sk_hex = b"717284c21d52489ddd8afa1adda32fa332cb0410b72ef83b415314cb12521bfe";
let sk_bytes = HEXLOWER.decode(sk_hex).unwrap();
let sk = PrivateKey::from_slice(&sk_bytes).unwrap();
let other_key_hex = b"133798235bc42d37ce009b4b202cfe08bfd133c8e6eea75037fabb88f01fd959";
let other_key_bytes = HEXLOWER.decode(other_key_hex).unwrap();
let other_key = PublicKey::from_slice(&other_key_bytes).unwrap();
let nonce_hex = b"fe381c4bdb8bfc2a27d2c9a6485113e7638613ffb02b3747";
let nonce_bytes = HEXLOWER.decode(nonce_hex).unwrap();
let nonce = Nonce::from_bytes(&nonce_bytes).unwrap();
let ks = KeyPair::from_private_key(sk);
let good_ciphertext_hex = b"687f2cb605d80a0660bacb2c6ce6e076591b58f9c9";
let good_ciphertext_bytes = HEXLOWER.decode(good_ciphertext_hex).unwrap();
let decrypted_good = ks.decrypt(&good_ciphertext_bytes, nonce, &other_key);
assert!(decrypted_good.is_ok());
assert_eq!(decrypted_good.unwrap(), b"hello".to_vec());
let mut bad_ciphertext_bytes = good_ciphertext_bytes.clone();
bad_ciphertext_bytes[0] += 1;
let nonce = Nonce::from_bytes(&nonce_bytes).unwrap();
let decrypted_bad = ks.decrypt(&bad_ciphertext_bytes, nonce, &other_key);
assert!(decrypted_bad.is_err());
let error = decrypted_bad.unwrap_err();
assert_eq!(format!("{}", error), "Crypto error: Could not decrypt data");
}
#[test]
fn auth_token_from_hex_str() {
let invalid_hex = "foobar";
let res1 = AuthToken::from_hex_str(&invalid_hex);
assert_eq!(res1, Err(SaltyError::Decode("Could not decode auth token hex string: invalid symbol at 1".into())));
let invalid_key = "012345ab";
let res2 = AuthToken::from_hex_str(&invalid_key);
assert_eq!(res2, Err(SaltyError::Decode("Invalid auth token hex string".into())));
let valid_key = "53459fb52fdeeb74103a2932a5eff8095ea1efbaf657f2181722c4e61e6f7e79";
let res3 = AuthToken::from_hex_str(&valid_key);
let _ = res3.unwrap();
}
#[test]
fn auth_token_from_slice() {
let too_short = [0; 31];
let res1 = AuthToken::from_slice(&too_short);
assert_eq!(res1, Err(SaltyError::Decode("Invalid auth token bytes: Slice must be 32 bytes long".into())));
let valid_token = [1; 32];
let res2 = AuthToken::from_slice(&valid_token);
let _ = res2.unwrap();
}
#[test]
fn auth_token_zero_on_drop() {
use std::borrow::Borrow;
let token = Box::new(AuthToken::from_hex_str("2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a").unwrap());
let token_bytes = (token.0).0;
let zero_bytes = [0; 32];
let ptr = token.borrow() as *const AuthToken;
println!("Old data is {:?}", &token_bytes);
println!("Pointer address is {:?}", ptr);
let deref1: &AuthToken = unsafe { &*ptr };
println!("Deref1 data is {:?}", &(deref1.0).0);
assert_eq!((deref1.0).0, token_bytes);
assert_ne!((deref1.0).0, zero_bytes);
drop(token);
println!("Pointer address is {:?}", ptr);
let deref2: &AuthToken = unsafe { &*ptr };
println!("Deref2 data is {:?}", &(deref2.0).0);
assert_ne!((deref2.0).0, token_bytes);
}
#[test]
fn unsigned_keys_sign_decrypt() {
let kp_server = KeyPair::new();
let kp_client = KeyPair::new();
let nonce_hex = b"fe381c4bdb8bfc2a27d2c9a6485113e7638613ffb02b3747";
let nonce_bytes = HEXLOWER.decode(nonce_hex).unwrap();
let nonce = Nonce::from_bytes(&nonce_bytes).unwrap();
let unsigned = UnsignedKeys::new(
kp_server.public_key().clone(),
kp_client.public_key().clone(),
);
let signed = unsigned.clone().sign(
&kp_server,
kp_client.public_key(),
unsafe { nonce.clone() },
);
let decrypted = box_::open(
&signed.0,
&{ unsafe { nonce.clone() } }.into(),
kp_server.public_key(),
kp_client.private_key(),
).unwrap();
assert_eq!(decrypted.len(), 2 * 32);
assert_eq!(&decrypted[0..32], &kp_server.public_key().0);
assert_eq!(&decrypted[32..64], &kp_client.public_key().0);
let unsigned2 = signed.decrypt(&kp_client, kp_server.public_key(), nonce).unwrap();
assert_eq!(unsigned, unsigned2);
}
}