Skip to content

Commit

Permalink
Merge pull request #172 from bilelmoussaoui/bilelmoussaoui/crypto-fail
Browse files Browse the repository at this point in the history
client: Don't panic if a cryptography operation fails
  • Loading branch information
bilelmoussaoui authored Dec 9, 2024
2 parents 1bba4ad + fbe1cb4 commit f198bcc
Show file tree
Hide file tree
Showing 22 changed files with 337 additions and 183 deletions.
55 changes: 55 additions & 0 deletions client/src/crypto/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// Cryptography specific errors.
#[derive(Debug)]
pub enum Error {
#[cfg(feature = "openssl_crypto")]
Openssl(openssl::error::ErrorStack),
#[cfg(feature = "native_crypto")]
PadError(cipher::inout::PadError),
#[cfg(feature = "native_crypto")]
UnpadError(cipher::block_padding::UnpadError),
}

#[cfg(feature = "openssl_crypto")]
impl From<openssl::error::ErrorStack> for Error {
fn from(value: openssl::error::ErrorStack) -> Self {
Self::Openssl(value)
}
}

#[cfg(feature = "native_crypto")]
impl From<cipher::block_padding::UnpadError> for Error {
fn from(value: cipher::block_padding::UnpadError) -> Self {
Self::UnpadError(value)
}
}

#[cfg(feature = "native_crypto")]
impl From<cipher::inout::PadError> for Error {
fn from(value: cipher::inout::PadError) -> Self {
Self::PadError(value)
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
#[cfg(feature = "openssl_crypto")]
Self::Openssl(e) => Some(e),
#[cfg(feature = "native_crypto")]
Self::UnpadError(_) | Self::PadError(_) => None,
}
}
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(feature = "openssl_crypto")]
Self::Openssl(e) => f.write_fmt(format_args!("Openssl error: {e}")),
#[cfg(feature = "native_crypto")]
Self::UnpadError(e) => f.write_fmt(format_args!("Wrong padding error: {e}")),
#[cfg(feature = "native_crypto")]
Self::PadError(e) => f.write_fmt(format_args!("Wrong padding error: {e}")),
}
}
}
9 changes: 6 additions & 3 deletions client/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub(crate) use native::*;
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
pub use native::*;

mod error;
pub use error::Error;

#[cfg(feature = "openssl_crypto")]
mod openssl;
#[cfg(all(feature = "openssl_crypto", not(feature = "unstable")))]
Expand All @@ -33,10 +36,10 @@ mod test {
78, 82, 67, 158, 214, 102, 48, 109, 84, 107, 94, 54, 225, 29, 186, 246,
];

let encrypted = encrypt(data, &aes_key, aes_iv);
let encrypted = encrypt(data, &aes_key, aes_iv).unwrap();
assert_eq!(encrypted, expected_encrypted);

let decrypted = decrypt(&encrypted, &aes_key, aes_iv);
let decrypted = decrypt(&encrypted, &aes_key, aes_iv).unwrap();
assert_eq!(decrypted.to_vec(), data);
}

Expand All @@ -53,7 +56,7 @@ mod test {
let salt = &[0x92, 0xf4, 0xc0, 0x34, 0x0f, 0x5f, 0x36, 0xf9];
let iteration_count = 1782;
let password = b"test";
let (key, iv) = legacy_derive_key_and_iv(password, Ok(()), salt, iteration_count);
let (key, iv) = legacy_derive_key_and_iv(password, Ok(()), salt, iteration_count).unwrap();
assert_eq!(key.as_ref(), &expected_key[..]);
assert_eq!(iv, &expected_iv[..]);
}
Expand Down
69 changes: 39 additions & 30 deletions client/src/crypto/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,69 +22,74 @@ type EncAlg = cbc::Encryptor<aes::Aes128>;
type DecAlg = cbc::Decryptor<aes::Aes128>;
type MacAlg = hmac::Hmac<sha2::Sha256>;

pub fn encrypt(data: impl AsRef<[u8]>, key: &Key, iv: impl AsRef<[u8]>) -> Vec<u8> {
pub fn encrypt(
data: impl AsRef<[u8]>,
key: &Key,
iv: impl AsRef<[u8]>,
) -> Result<Vec<u8>, super::Error> {
let mut blob = vec![0; data.as_ref().len() + EncAlg::block_size()];

// Unwrapping since adding `CIPHER_BLOCK_SIZE` to array is enough space for
// PKCS7
let encrypted_len = EncAlg::new_from_slices(key.as_ref(), iv.as_ref())
.expect("Invalid key length")
.encrypt_padded_b2b_mut::<Pkcs7>(data.as_ref(), &mut blob)
.unwrap()
.encrypt_padded_b2b_mut::<Pkcs7>(data.as_ref(), &mut blob)?
.len();

blob.truncate(encrypted_len);

blob
Ok(blob)
}

pub fn decrypt(blob: impl AsRef<[u8]>, key: &Key, iv: impl AsRef<[u8]>) -> Zeroizing<Vec<u8>> {
pub fn decrypt(
blob: impl AsRef<[u8]>,
key: &Key,
iv: impl AsRef<[u8]>,
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
let mut data = blob.as_ref().to_vec();

DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
Ok(DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
.expect("Invalid key length")
.decrypt_padded_mut::<Pkcs7>(&mut data)
.unwrap()
.decrypt_padded_mut::<Pkcs7>(&mut data)?
.to_vec()
.into()
.into())
}

pub(crate) fn decrypt_no_padding(
blob: impl AsRef<[u8]>,
key: &Key,
iv: impl AsRef<[u8]>,
) -> Zeroizing<Vec<u8>> {
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
let mut data = blob.as_ref().to_vec();

DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
Ok(DecAlg::new_from_slices(key.as_ref(), iv.as_ref())
.expect("Invalid key length")
.decrypt_padded_mut::<NoPadding>(&mut data)
.unwrap()
.decrypt_padded_mut::<NoPadding>(&mut data)?
.to_vec()
.into()
.into())
}

pub(crate) fn iv_len() -> usize {
DecAlg::iv_size()
}

pub(crate) fn generate_private_key() -> Zeroizing<Vec<u8>> {
pub(crate) fn generate_private_key() -> Result<Zeroizing<Vec<u8>>, super::Error> {
let generic_array = EncAlg::generate_key(cipher::rand_core::OsRng);
Zeroizing::new(generic_array.to_vec())
Ok(Zeroizing::new(generic_array.to_vec()))
}

pub(crate) fn generate_public_key(private_key: impl AsRef<[u8]>) -> Vec<u8> {
pub(crate) fn generate_public_key(private_key: impl AsRef<[u8]>) -> Result<Vec<u8>, super::Error> {
let private_key_uint = BigUint::from_bytes_be(private_key.as_ref());
static DH_GENERATOR: LazyLock<BigUint> = LazyLock::new(|| BigUint::from_u64(0x2).unwrap());
let public_key_uint = powm(&DH_GENERATOR, private_key_uint);

public_key_uint.to_bytes_be()
Ok(public_key_uint.to_bytes_be())
}

pub(crate) fn generate_aes_key(
private_key: impl AsRef<[u8]>,
server_public_key: impl AsRef<[u8]>,
) -> Zeroizing<Vec<u8>> {
) -> Result<Zeroizing<Vec<u8>>, super::Error> {
let server_public_key_uint = BigUint::from_bytes_be(server_public_key.as_ref());
let private_key_uint = BigUint::from_bytes_be(private_key.as_ref());
let common_secret = powm(&server_public_key_uint, private_key_uint);
Expand All @@ -107,27 +112,31 @@ pub(crate) fn generate_aes_key(
hk.expand(&info, okm.as_mut())
.expect("hkdf expand should never fail");

okm
Ok(okm)
}

pub fn generate_iv() -> Vec<u8> {
EncAlg::generate_iv(cipher::rand_core::OsRng).to_vec()
pub fn generate_iv() -> Result<Vec<u8>, super::Error> {
Ok(EncAlg::generate_iv(cipher::rand_core::OsRng).to_vec())
}

pub(crate) fn mac_len() -> usize {
MacAlg::output_size()
}

pub(crate) fn compute_mac(data: impl AsRef<[u8]>, key: &Key) -> Vec<u8> {
pub(crate) fn compute_mac(data: impl AsRef<[u8]>, key: &Key) -> Result<Vec<u8>, super::Error> {
let mut mac = MacAlg::new_from_slice(key.as_ref()).unwrap();
mac.update(data.as_ref());
mac.finalize().into_bytes().to_vec()
Ok(mac.finalize().into_bytes().to_vec())
}

pub(crate) fn verify_mac(data: impl AsRef<[u8]>, key: &Key, expected: impl AsRef<[u8]>) -> bool {
pub(crate) fn verify_mac(
data: impl AsRef<[u8]>,
key: &Key,
expected: impl AsRef<[u8]>,
) -> Result<bool, super::Error> {
let mut mac = MacAlg::new_from_slice(key.as_ref()).unwrap();
mac.update(data.as_ref());
mac.verify_slice(expected.as_ref()).is_ok()
Ok(mac.verify_slice(expected.as_ref()).is_ok())
}

pub(crate) fn verify_checksum_md5(digest: impl AsRef<[u8]>, content: impl AsRef<[u8]>) -> bool {
Expand All @@ -141,7 +150,7 @@ pub(crate) fn derive_key(
key_strength: Result<(), file::WeakKeyError>,
salt: impl AsRef<[u8]>,
iteration_count: usize,
) -> Key {
) -> Result<Key, super::Error> {
let mut key = Key::new_with_strength(vec![0; EncAlg::block_size()], key_strength);

pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(
Expand All @@ -152,15 +161,15 @@ pub(crate) fn derive_key(
)
.expect("HMAC can be initialized with any key length");

key
Ok(key)
}

pub(crate) fn legacy_derive_key_and_iv(
secret: impl AsRef<[u8]>,
key_strength: Result<(), file::WeakKeyError>,
salt: impl AsRef<[u8]>,
iteration_count: usize,
) -> (Key, Vec<u8>) {
) -> Result<(Key, Vec<u8>), super::Error> {
let mut buffer = vec![0; EncAlg::key_size() + EncAlg::iv_size()];
let mut hasher = Sha256::new();
let mut digest_buffer = vec![0; <Sha256 as Digest>::output_size()];
Expand Down Expand Up @@ -198,7 +207,7 @@ pub(crate) fn legacy_derive_key_and_iv(
}

let iv = buffer.split_off(EncAlg::key_size());
(Key::new_with_strength(buffer, key_strength), iv)
Ok((Key::new_with_strength(buffer, key_strength), iv))
}

/// from https://github.com/plietar/librespot/blob/master/core/src/util/mod.rs#L53
Expand Down
Loading

0 comments on commit f198bcc

Please sign in to comment.