memory_testing/
lib.rs

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
use std::path::Path;

use bitwarden_crypto::Kdf;
use zeroize::{Zeroize, Zeroizing};

pub const TEST_STRING: &str = "THIS IS USED TO CHECK THAT THE MEMORY IS DUMPED CORRECTLY";

pub fn load_cases(base_dir: &Path) -> Cases {
    let mut json_str = std::fs::read_to_string(base_dir.join("cases.json")).unwrap();
    let cases: Cases = serde_json::from_str(&json_str).unwrap();

    // Make sure that we don't leave extra copies of the string data in memory
    json_str.zeroize();
    cases
}

#[derive(serde::Deserialize)]
pub struct Cases {
    pub cases: Vec<Case>,
}

#[derive(serde::Deserialize)]
pub struct Case {
    pub name: String,
    #[serde(flatten)]
    pub command: CaseCommand,
    pub memory_lookups: Vec<MemoryLookup>,
}

// We don't actively zeroize this struct because we want the code in bitwarden_crypto
// to handle it for us
#[derive(serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CaseCommand {
    SymmetricKey {
        key: String,
    },
    AsymmetricKey {
        private_key: String,
    },
    MasterKey {
        password: String,
        email: String,
        kdf: Kdf,
    },
    String {
        parts: Vec<String>,
    },
}

#[derive(serde::Deserialize)]
pub struct MemoryLookup {
    pub name: String,

    #[serde(flatten)]
    pub value: MemoryLookupValue,

    #[serde(default)]
    pub allowed_count: Option<usize>,
}

// We don't actually want these values to be caught by the memory testing,
// so this enum should be always zeroized
#[derive(serde::Deserialize)]
#[serde(untagged)]
pub enum MemoryLookupValue {
    String { string: Zeroizing<String> },
    Binary { hex: Zeroizing<String> },
}