summaryrefslogtreecommitdiff
path: root/keystore2/src/entropy.rs
blob: 1dcdc86fc0c19546d21853ead859d2dc7faaa56f (plain)
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
// Copyright 2021, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This module holds functionality for retrieving and distributing entropy.

use anyhow::{Context, Result};
use log::error;
use std::time::{Duration, Instant};

static ENTROPY_SIZE: usize = 64;
static MIN_FEED_INTERVAL_SECS: u64 = 30;

#[derive(Default)]
struct FeederInfo {
    last_feed: Option<Instant>,
}

/// Register the entropy feeder as an idle callback.
pub fn register_feeder() {
    crate::globals::ASYNC_TASK.add_idle(|shelf| {
        let info = shelf.get_mut::<FeederInfo>();
        let now = Instant::now();
        let feed_needed = match info.last_feed {
            None => true,
            Some(last) => now.duration_since(last) > Duration::from_secs(MIN_FEED_INTERVAL_SECS),
        };
        if feed_needed {
            info.last_feed = Some(now);
            feed_devices();
        }
    });
}

fn get_entropy(size: usize) -> Result<Vec<u8>> {
    keystore2_crypto::generate_random_data(size).context("Retrieving entropy for KeyMint device")
}

/// Feed entropy to all known KeyMint devices.
pub fn feed_devices() {
    let km_devs = crate::globals::get_keymint_devices();
    if km_devs.is_empty() {
        return;
    }
    let data = match get_entropy(km_devs.len() * ENTROPY_SIZE) {
        Ok(data) => data,
        Err(e) => {
            error!(
                "Failed to retrieve {}*{} bytes of entropy: {:?}",
                km_devs.len(),
                ENTROPY_SIZE,
                e
            );
            return;
        }
    };
    for (i, km_dev) in km_devs.iter().enumerate() {
        let offset = i * ENTROPY_SIZE;
        let sub_data = &data[offset..(offset + ENTROPY_SIZE)];
        if let Err(e) = km_dev.addRngEntropy(sub_data) {
            error!("Failed to feed entropy to KeyMint device: {:?}", e);
        }
    }
}

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

    #[test]
    fn test_entropy_size() {
        for size in &[0, 1, 4, 8, 256, 4096] {
            let data = get_entropy(*size).expect("failed to get entropy");
            assert_eq!(data.len(), *size);
        }
    }
    #[test]
    fn test_entropy_uniqueness() {
        let count = 10;
        let mut seen = HashSet::new();
        for _i in 0..count {
            let data = get_entropy(16).expect("failed to get entropy");
            seen.insert(data);
        }
        assert_eq!(seen.len(), count);
    }
}