1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
//! This module contains the [ReadcapDict] type.

use crate::{
    crypto::{
        self, rand_vec, Ciphertext, Creds, CredsPub, DecrypterExt, EncrypterExt, HashKind, SymKey,
    },
    Result,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

pub use private::ReadcapDict;

mod private {
    use super::*;

    /// A container for the readcaps stored in a block's metadata. This type is responsible for
    /// obscuring the owner of the readcaps its stores.
    #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
    pub struct ReadcapDict {
        /// The message digest to use for computing principals from public keys, and to digest
        /// these principals with the salt.
        kind: HashKind,
        /// The salt used to obscure the owner of each readcap.
        salt: Vec<u8>,
        readcaps: BTreeMap<Vec<u8>, Ciphertext<SymKey>>,
    }

    impl ReadcapDict {
        pub fn new() -> Result<Self> {
            let kind = HashKind::default();
            Ok(ReadcapDict {
                kind,
                salt: rand_vec(kind.len())?,
                readcaps: BTreeMap::new(),
            })
        }

        /// Computes the key to use for the given credentials. This function should be considered
        /// security critical, as the readcap map is stored in plaintext, so an attacker can attempt
        /// to correlate the the readcaps from different blocks.
        fn key<C: CredsPub>(&self, creds: C) -> Result<Vec<u8>> {
            let principal = creds.principal_of_kind(self.kind);
            let mut key = vec![0u8; self.kind.len()];
            self.kind
                .digest(&mut key, [principal.as_slice(), &self.salt].into_iter())?;
            Ok(key)
        }

        pub fn get<C: Creds>(&self, creds: C) -> Result<SymKey> {
            let key = self.key(&creds)?;
            let value = self.readcaps.get(&key).ok_or(crypto::Error::NoReadCap)?;
            creds.ser_decrypt(value)
        }

        pub fn set<C: CredsPub>(&mut self, creds: C, block_key: &SymKey) -> Result<()> {
            let key = self.key(&creds)?;
            let value = creds.ser_encrypt(block_key)?;
            self.readcaps.insert(key, value);
            Ok(())
        }

        /// Attempts to remove the readcap for the given creds from this dictionary. When the `Ok`
        /// variant is returned, it contains `true` if the creds were present prior to removal,
        /// and `false` otherwise.
        #[allow(dead_code)]
        pub fn remove<C: CredsPub>(&mut self, creds: C) -> Result<bool> {
            let key = self.key(creds)?;
            Ok(self.readcaps.remove(&key).is_some())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::{crypto::SymKeyKind, test_helpers::node_creds};

    #[test]
    fn set_then_get() {
        let mut dict = ReadcapDict::new().unwrap();
        let creds = node_creds();
        let expected = SymKey::generate(SymKeyKind::Aes256Ctr).unwrap();

        dict.set(creds, &expected).unwrap();
        let actual = dict.get(creds).unwrap();

        assert_eq!(expected, actual);
    }

    #[test]
    fn get_returns_no_readcap_when_key_not_found() {
        let dict = ReadcapDict::new().unwrap();

        let result = dict.get(node_creds());

        let err = result.err().unwrap().downcast::<crypto::Error>().unwrap();
        let passed = if let crypto::Error::NoReadCap = err {
            true
        } else {
            false
        };
        assert!(passed);
    }

    #[test]
    fn remove_after_set_returns_true() {
        let mut dict = ReadcapDict::new().unwrap();
        let creds = node_creds();
        let sym_key = SymKey::generate(SymKeyKind::Aes256Cbc).unwrap();

        dict.set(creds, &sym_key).unwrap();
        let was_present = dict.remove(creds).unwrap();
        let get_result = dict.get(creds);

        assert!(was_present);
        let err = get_result
            .err()
            .unwrap()
            .downcast::<crypto::Error>()
            .unwrap();
        let passed = if let crypto::Error::NoReadCap = err {
            true
        } else {
            false
        };
        assert!(passed);
    }

    #[test]
    fn remove_when_not_set_returns_false() {
        let mut dict = ReadcapDict::new().unwrap();

        let was_present = dict.remove(node_creds()).unwrap();

        assert!(!was_present);
    }
}