lightning_storage_server/
util.rs

1#[cfg(feature = "crypt")]
2use crate::chacha20::ChaCha20;
3use crate::Value;
4use bitcoin_hashes::sha256::Hash as Sha256Hash;
5use bitcoin_hashes::{Hash, HashEngine, Hmac, HmacEngine};
6use log::error;
7
8pub fn prepare_value_for_put(secret: &[u8], key: &str, value: &mut Value) {
9    append_hmac_to_value(secret, key, value.version, &mut value.value);
10    #[cfg(feature = "crypt")]
11    crypt_value(secret, key, value.version, &mut value.value);
12}
13
14pub fn append_hmac_to_value(secret: &[u8], key: &str, version: i64, value: &mut Vec<u8>) {
15    let hmac = compute_hmac(secret, key, &version, &value);
16    value.append(&mut hmac.to_vec());
17}
18
19#[cfg(feature = "crypt")]
20pub fn crypt_value(secret: &[u8], key: &str, version: i64, value: &mut [u8]) {
21    let mut engine = Sha256Hash::engine();
22    engine.input(key.as_bytes());
23    engine.input(&version.to_be_bytes());
24    let hash = Sha256Hash::from_engine(engine);
25    let mut nonce = [0u8; 12];
26    nonce[0..12].copy_from_slice(&hash[0..12]);
27
28    let mut chacha = ChaCha20::new(secret, &nonce);
29    chacha.process_in_place(value);
30}
31
32pub fn process_value_from_get(secret: &[u8], key: &str, value: &mut Value) -> Result<(), ()> {
33    #[cfg(feature = "crypt")]
34    crypt_value(secret, key, value.version, &mut value.value);
35    remove_and_check_hmac(secret, key, value.version, &mut value.value)?;
36    Ok(())
37}
38
39pub fn remove_and_check_hmac(
40    secret: &[u8],
41    key: &str,
42    version: i64,
43    value: &mut Vec<u8>,
44) -> Result<(), ()> {
45    if value.len() < 32 {
46        error!("value too short to have an HMAC");
47        return Err(());
48    }
49    let expected_hmac = value.split_off(value.len() - 32);
50    let hmac = compute_hmac(secret, key, &version, &value);
51    if hmac == expected_hmac.as_slice() {
52        Ok(())
53    } else {
54        Err(())
55    }
56}
57
58fn compute_hmac(secret: &[u8], key: &str, version: &i64, value: &[u8]) -> [u8; 32] {
59    let mut hmac = HmacEngine::<Sha256Hash>::new(secret);
60    add_to_hmac(key, version, value, &mut hmac);
61    Hmac::from_engine(hmac).into_inner()
62}
63
64/// Add key, version (8 bytes big endian) and value to HMAC
65pub fn add_to_hmac(key: &str, version: &i64, value: &[u8], hmac: &mut HmacEngine<Sha256Hash>) {
66    hmac.input(key.as_bytes());
67    hmac.input(&version.to_be_bytes());
68    hmac.input(&value);
69}
70
71/// Compute a client/server HMAC - which proves the client or server initiated this
72/// call and no replay occurred.
73pub fn compute_shared_hmac(secret: &[u8], nonce: &[u8], kvs: &[(String, Value)]) -> Vec<u8> {
74    let mut hmac_engine = HmacEngine::<Sha256Hash>::new(&secret);
75    hmac_engine.input(secret);
76    hmac_engine.input(nonce);
77    for (key, value) in kvs {
78        add_to_hmac(&key, &value.version, &value.value, &mut hmac_engine);
79    }
80    Hmac::from_engine(hmac_engine).into_inner().to_vec()
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn put_get_processing_test() {
89        let secret = [3u8; 32];
90        let key = "test/key";
91        let value = Value { version: 12, value: "test value".as_bytes().to_vec() };
92        let mut v = Value { version: value.version, value: value.value.clone() };
93        prepare_value_for_put(&secret, key, &mut v);
94        assert_ne!(v.value, value.value);
95        process_value_from_get(&secret, key, &mut v).unwrap();
96        assert_eq!(v.value, value.value);
97    }
98
99    #[test]
100    fn test_hmac() {
101        let key = [11u8; 32];
102        let orig_value = vec![1u8, 2, 3];
103        let mut value = orig_value.clone();
104        append_hmac_to_value(&key, "x", 123, &mut value);
105        remove_and_check_hmac(&key, "x", 123, &mut value).expect("hmac check failed");
106        assert_eq!(value, orig_value);
107
108        let mut value = orig_value.clone();
109        append_hmac_to_value(&key, "x", 123, &mut value);
110        value[0] = 0;
111        remove_and_check_hmac(&key, "x", 123, &mut value).expect_err("hmac check should fail");
112
113        let mut value = orig_value.clone();
114        append_hmac_to_value(&key, "x", 123, &mut value);
115        remove_and_check_hmac(&key, "x", 122, &mut value).expect_err("hmac check should fail");
116
117        let mut value = orig_value.clone();
118        append_hmac_to_value(&key, "x", 123, &mut value);
119        remove_and_check_hmac(&key, "x1", 123, &mut value).expect_err("hmac check should fail");
120    }
121}