97 lines
2.5 KiB
Rust
97 lines
2.5 KiB
Rust
use anyhow::{Result, anyhow};
|
|
use chacha20poly1305::aead::{Aead, KeyInit, Payload};
|
|
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
|
use hkdf::Hkdf;
|
|
use hmac::{Hmac, Mac};
|
|
use rand::RngCore;
|
|
use sha2::{Digest, Sha256};
|
|
|
|
pub type HmacSha256 = Hmac<Sha256>;
|
|
|
|
pub fn random_32() -> [u8; 32] {
|
|
let mut out = [0u8; 32];
|
|
rand::thread_rng().fill_bytes(&mut out);
|
|
out
|
|
}
|
|
|
|
pub fn random_16() -> [u8; 16] {
|
|
let mut out = [0u8; 16];
|
|
rand::thread_rng().fill_bytes(&mut out);
|
|
out
|
|
}
|
|
|
|
pub fn random_12() -> [u8; 12] {
|
|
let mut out = [0u8; 12];
|
|
rand::thread_rng().fill_bytes(&mut out);
|
|
out
|
|
}
|
|
|
|
pub fn hmac_sha256(key: &[u8], parts: &[&[u8]]) -> [u8; 32] {
|
|
let mut mac = <HmacSha256 as Mac>::new_from_slice(key).expect("HMAC accepts any key size");
|
|
for part in parts {
|
|
mac.update(part);
|
|
}
|
|
mac.finalize().into_bytes().into()
|
|
}
|
|
|
|
pub fn verify_hmac(key: &[u8], parts: &[&[u8]], expected: &[u8; 32]) -> bool {
|
|
let actual = hmac_sha256(key, parts);
|
|
constant_time_eq(&actual, expected)
|
|
}
|
|
|
|
pub fn sha256(data: &[u8]) -> [u8; 32] {
|
|
Sha256::digest(data).into()
|
|
}
|
|
|
|
pub fn hkdf32(secret: &[u8], salt: &[u8], info: &[u8]) -> Result<[u8; 32]> {
|
|
let hk = Hkdf::<Sha256>::new(Some(salt), secret);
|
|
let mut out = [0u8; 32];
|
|
hk.expand(info, &mut out)
|
|
.map_err(|_| anyhow!("HKDF expand failed"))?;
|
|
Ok(out)
|
|
}
|
|
|
|
pub fn nonce_from(direction: u32, seq: u64) -> [u8; 12] {
|
|
let mut nonce = [0u8; 12];
|
|
nonce[..4].copy_from_slice(&direction.to_be_bytes());
|
|
nonce[4..].copy_from_slice(&seq.to_be_bytes());
|
|
nonce
|
|
}
|
|
|
|
pub fn seal(key: &[u8; 32], nonce: &[u8; 12], aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
|
|
let cipher = ChaCha20Poly1305::new(Key::from_slice(key));
|
|
cipher
|
|
.encrypt(
|
|
Nonce::from_slice(nonce),
|
|
Payload {
|
|
msg: plaintext,
|
|
aad,
|
|
},
|
|
)
|
|
.map_err(|_| anyhow!("encrypt failed"))
|
|
}
|
|
|
|
pub fn open(key: &[u8; 32], nonce: &[u8; 12], aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
|
|
let cipher = ChaCha20Poly1305::new(Key::from_slice(key));
|
|
cipher
|
|
.decrypt(
|
|
Nonce::from_slice(nonce),
|
|
Payload {
|
|
msg: ciphertext,
|
|
aad,
|
|
},
|
|
)
|
|
.map_err(|_| anyhow!("decrypt failed"))
|
|
}
|
|
|
|
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
|
|
if a.len() != b.len() {
|
|
return false;
|
|
}
|
|
let mut diff = 0u8;
|
|
for (x, y) in a.iter().zip(b.iter()) {
|
|
diff |= x ^ y;
|
|
}
|
|
diff == 0
|
|
}
|