use std::ffi::CStr; use std::hash::Hasher; use std::ptr; use libsodium_sys::*; use serde_big_array::BigArray; use siphasher::sip::SipHasher13; use crate::errors::*; #[derive(Derivative)] #[derivative(Default)] pub struct Signature( #[derivative(Default(value = "[0u8; crypto_sign_BYTES as usize]"))] [u8; crypto_sign_BYTES as usize], ); impl Signature { pub fn as_bytes(&self) -> &[u8; crypto_sign_BYTES as usize] { &self.0 } pub fn from_bytes(bytes: [u8; crypto_sign_BYTES as usize]) -> Self { Signature(bytes) } } #[derive(Serialize, Deserialize, Derivative, Clone)] #[derivative(Default)] pub struct SignSK( #[serde(with = "BigArray")] #[derivative(Default(value = "[0u8; crypto_sign_SECRETKEYBYTES as usize]"))] [u8; crypto_sign_SECRETKEYBYTES as usize], ); impl SignSK { pub fn as_bytes(&self) -> &[u8; crypto_sign_SECRETKEYBYTES as usize] { &self.0 } pub fn from_bytes(bytes: [u8; crypto_sign_SECRETKEYBYTES as usize]) -> Self { SignSK(bytes) } pub fn sign(&self, bytes: &[u8]) -> Signature { let mut signature = Signature::default(); let ret = unsafe { crypto_sign_detached( signature.0.as_mut_ptr(), ptr::null_mut(), bytes.as_ptr(), bytes.len() as _, self.as_bytes().as_ptr(), ) }; assert_eq!(ret, 0); signature } } #[derive(Debug, Serialize, Deserialize, Default, Clone)] pub struct SignPK([u8; crypto_sign_PUBLICKEYBYTES as usize]); impl SignPK { pub fn as_bytes(&self) -> &[u8; crypto_sign_PUBLICKEYBYTES as usize] { &self.0 } pub fn from_bytes(bytes: [u8; crypto_sign_PUBLICKEYBYTES as usize]) -> Self { SignPK(bytes) } pub fn as_string(&self) -> String { bin2hex(self.as_bytes()) } } #[derive(Derivative, Serialize, Deserialize, Clone)] #[derivative(Debug, Default)] pub struct SignKeyPair { #[derivative(Debug = "ignore")] pub sk: SignSK, pub pk: SignPK, } impl SignKeyPair { pub fn new() -> Self { let mut kp = SignKeyPair::default(); unsafe { crypto_sign_keypair(kp.pk.0.as_mut_ptr(), kp.sk.0.as_mut_ptr()) }; kp } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct CryptSK([u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize]); impl CryptSK { pub fn as_bytes( &self, ) -> &[u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize] { &self.0 } pub fn from_bytes( bytes: [u8; crypto_box_curve25519xchacha20poly1305_SECRETKEYBYTES as usize], ) -> Self { CryptSK(bytes) } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct CryptPK([u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize]); impl CryptPK { pub fn as_bytes( &self, ) -> &[u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize] { &self.0 } pub fn from_bytes( bytes: [u8; crypto_box_curve25519xchacha20poly1305_PUBLICKEYBYTES as usize], ) -> Self { CryptPK(bytes) } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct CryptKeyPair { pub sk: CryptSK, pub pk: CryptPK, } impl CryptKeyPair { pub fn from_seed( seed: [u8; crypto_box_curve25519xchacha20poly1305_SEEDBYTES as usize], ) -> Self { let mut kp = CryptKeyPair::default(); unsafe { crypto_box_curve25519xchacha20poly1305_seed_keypair( kp.pk.0.as_mut_ptr(), kp.sk.0.as_mut_ptr(), seed.as_ptr(), ) }; kp } pub fn compute_shared_key(&self, pk: &[u8]) -> Result { let mut shared_key = SharedKey::default(); let res = unsafe { crypto_box_curve25519xchacha20poly1305_beforenm( shared_key.0.as_mut_ptr(), pk.as_ptr(), self.sk.0.as_ptr(), ) }; ensure!(res == 0, "Weak public key"); Ok(shared_key) } } #[derive(Debug, Clone, Default)] pub struct SharedKey([u8; crypto_box_curve25519xchacha20poly1305_BEFORENMBYTES as usize]); impl SharedKey { pub fn decrypt(&self, nonce: &[u8], encrypted: &[u8]) -> Result, Error> { let encrypted_len = encrypted.len(); let mut decrypted = vec![0u8; encrypted_len - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize]; let res = unsafe { libsodium_sys::crypto_box_curve25519xchacha20poly1305_open_easy_afternm( decrypted.as_mut_ptr(), encrypted.as_ptr(), encrypted_len as _, nonce.as_ptr(), self.0.as_ptr(), ) }; ensure!(res == 0, "Unable to decrypt"); let idx = decrypted .iter() .rposition(|x| *x != 0x00) .ok_or_else(|| anyhow!("Padding error"))?; ensure!(decrypted[idx] == 0x80, "Padding error"); decrypted.truncate(idx); Ok(decrypted) } pub fn encrypt_into( &self, target: &mut Vec, nonce: &[u8], client_nonce: &[u8], plaintext: Vec, max_target_size: usize, ) -> Result<(), Error> { ensure!( max_target_size >= crypto_box_curve25519xchacha20poly1305_MACBYTES as usize, "Max target size too small" ); let plaintext_len = plaintext.len(); let max_padded_plaintext_len = max_target_size - crypto_box_curve25519xchacha20poly1305_MACBYTES as usize; let mut hasher = SipHasher13::new(); hasher.write(&self.0); hasher.write(client_nonce); let pad_size: usize = 1 + (hasher.finish() as usize & 0xff); let mut padded_plaintext_len = (plaintext_len + pad_size) & !63; if padded_plaintext_len < plaintext_len { padded_plaintext_len += 256; } if padded_plaintext_len > max_padded_plaintext_len { padded_plaintext_len = max_padded_plaintext_len; } ensure!(padded_plaintext_len > plaintext_len, "No room for padding"); let mut padded_plaintext = plaintext; padded_plaintext.push(0x80); while padded_plaintext.len() != padded_plaintext_len { padded_plaintext.push(0x00); } let padded_plaintext_len = padded_plaintext.len(); let target_header_len = target.len(); target.resize( target_header_len + padded_plaintext_len + crypto_box_curve25519xchacha20poly1305_MACBYTES as usize, 0, ); let encrypted = &mut target[target_header_len..]; let res = unsafe { libsodium_sys::crypto_box_curve25519xchacha20poly1305_easy_afternm( encrypted.as_mut_ptr(), padded_plaintext.as_ptr(), padded_plaintext_len as _, nonce.as_ptr(), self.0.as_ptr(), ) }; ensure!(res == 0, "Unable to encrypt"); Ok(()) } } pub fn bin2hex(bin: &[u8]) -> String { let bin_len = bin.len(); let hex_len = bin_len * 2 + 1; let mut hex = vec![0u8; hex_len]; unsafe { sodium_bin2hex(hex.as_mut_ptr() as *mut _, hex_len, bin.as_ptr(), bin_len); } CStr::from_bytes_with_nul(&hex) .unwrap() .to_str() .unwrap() .to_string() } pub fn init() -> Result<(), Error> { let res = unsafe { sodium_init() }; ensure!(res >= 0, "Unable to initialize libsodium"); Ok(()) }