use crate::{crypto::Cryptographer, error::*};
use byteorder::{BigEndian, ByteOrder};
pub(crate) const ECE_AES_KEY_LENGTH: usize = 16;
pub(crate) const ECE_NONCE_LENGTH: usize = 12;
pub(crate) const ECE_SALT_LENGTH: usize = 16;
pub(crate) const ECE_TAG_LENGTH: usize = 16;
pub(crate) const ECE_WEBPUSH_PUBLIC_KEY_LENGTH: usize = 65;
pub(crate) const ECE_WEBPUSH_AUTH_SECRET_LENGTH: usize = 16;
pub(crate) const ECE_WEBPUSH_DEFAULT_RS: u32 = 4096;
pub(crate) const ECE_WEBPUSH_DEFAULT_PADDING_BLOCK_SIZE: usize = 128;
pub(crate) struct WebPushParams {
pub rs: u32,
pub pad_length: usize,
pub salt: Option<Vec<u8>>,
}
impl WebPushParams {
pub fn take_or_generate_salt(&mut self, cryptographer: &dyn Cryptographer) -> Result<Vec<u8>> {
Ok(match self.salt.take() {
Some(salt) => salt,
None => {
let mut salt = [0u8; ECE_SALT_LENGTH];
cryptographer.random_bytes(&mut salt)?;
salt.to_vec()
}
})
}
}
impl Default for WebPushParams {
fn default() -> Self {
Self {
rs: ECE_WEBPUSH_DEFAULT_RS,
pad_length: 0,
salt: None,
}
}
}
impl WebPushParams {
pub(crate) fn new_for_plaintext(plaintext: &[u8], min_pad_length: usize) -> Self {
let mut pad_length = ECE_WEBPUSH_DEFAULT_PADDING_BLOCK_SIZE
- (plaintext.len() % ECE_WEBPUSH_DEFAULT_PADDING_BLOCK_SIZE);
if pad_length < min_pad_length {
pad_length += ECE_WEBPUSH_DEFAULT_PADDING_BLOCK_SIZE;
}
WebPushParams {
pad_length,
..Default::default()
}
}
}
pub(crate) enum EceMode {
Encrypt,
Decrypt,
}
pub(crate) type KeyAndNonce = (Vec<u8>, Vec<u8>);
pub(crate) fn generate_iv_for_record(nonce: &[u8], counter: usize) -> [u8; ECE_NONCE_LENGTH] {
let mut iv = [0u8; ECE_NONCE_LENGTH];
let offset = ECE_NONCE_LENGTH - 8;
iv[0..offset].copy_from_slice(&nonce[0..offset]);
let mask = BigEndian::read_u64(&nonce[offset..]);
BigEndian::write_u64(&mut iv[offset..], mask ^ (counter as u64));
iv
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pad_to_block_size() {
const BLOCK_SIZE: usize = ECE_WEBPUSH_DEFAULT_PADDING_BLOCK_SIZE;
assert_eq!(
WebPushParams::new_for_plaintext(&[0; 0], 1).pad_length,
BLOCK_SIZE
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; 1], 1).pad_length,
BLOCK_SIZE - 1
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE - 2], 1).pad_length,
2
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE - 1], 1).pad_length,
1
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE], 1).pad_length,
BLOCK_SIZE
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE + 1], 1).pad_length,
BLOCK_SIZE - 1
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; 0], 2).pad_length,
BLOCK_SIZE
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; 1], 2).pad_length,
BLOCK_SIZE - 1
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE - 2], 2).pad_length,
2
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE - 1], 2).pad_length,
BLOCK_SIZE + 1
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE], 2).pad_length,
BLOCK_SIZE
);
assert_eq!(
WebPushParams::new_for_plaintext(&[0; BLOCK_SIZE + 1], 2).pad_length,
BLOCK_SIZE - 1
);
}
}