1 // Copyright (C) 2022 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use std::{fs::File, io::Read, os::unix::io::AsRawFd}; 16 17 use anyhow::{ensure, Context, Result}; 18 use log::debug; 19 use nix::fcntl::{fcntl, FcntlArg::F_SETFL, OFlag}; 20 use tokio::io::AsyncReadExt; 21 22 use crate::drbg; 23 24 const SEED_FOR_CLIENT_LEN: usize = 496; 25 const NUM_REQUESTS_PER_RESEED: u32 = 256; 26 27 pub struct ConditionerBuilder { 28 hwrng: File, 29 rg: drbg::Drbg, 30 } 31 32 impl ConditionerBuilder { new(mut hwrng: File) -> Result<ConditionerBuilder>33 pub fn new(mut hwrng: File) -> Result<ConditionerBuilder> { 34 let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN]; 35 hwrng.read_exact(&mut et).context("hwrng.read_exact in new")?; 36 let rg = drbg::Drbg::new(&et)?; 37 fcntl(hwrng.as_raw_fd(), F_SETFL(OFlag::O_NONBLOCK)) 38 .context("setting O_NONBLOCK on hwrng")?; 39 Ok(ConditionerBuilder { hwrng, rg }) 40 } 41 build(self) -> Conditioner42 pub fn build(self) -> Conditioner { 43 Conditioner { 44 hwrng: tokio::fs::File::from_std(self.hwrng), 45 rg: self.rg, 46 requests_since_reseed: 0, 47 } 48 } 49 } 50 51 pub struct Conditioner { 52 hwrng: tokio::fs::File, 53 rg: drbg::Drbg, 54 requests_since_reseed: u32, 55 } 56 57 impl Conditioner { reseed_if_necessary(&mut self) -> Result<()>58 pub async fn reseed_if_necessary(&mut self) -> Result<()> { 59 if self.requests_since_reseed >= NUM_REQUESTS_PER_RESEED { 60 debug!("Reseeding DRBG"); 61 let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN]; 62 self.hwrng.read_exact(&mut et).await.context("hwrng.read_exact in reseed")?; 63 self.rg.reseed(&et)?; 64 self.requests_since_reseed = 0; 65 } 66 Ok(()) 67 } 68 request(&mut self) -> Result<[u8; SEED_FOR_CLIENT_LEN]>69 pub fn request(&mut self) -> Result<[u8; SEED_FOR_CLIENT_LEN]> { 70 ensure!(self.requests_since_reseed < NUM_REQUESTS_PER_RESEED, "Not enough reseeds"); 71 let mut seed_for_client = [0u8; SEED_FOR_CLIENT_LEN]; 72 self.rg.generate(&mut seed_for_client)?; 73 self.requests_since_reseed += 1; 74 Ok(seed_for_client) 75 } 76 } 77