seed.rs (5470B)
1 use std::cmp::Ordering; 2 use std::mem::size_of; 3 use std::os::unix::fs::PermissionsExt; 4 5 use async_fs::File; 6 use async_trait::async_trait; 7 use blocking::unblock; 8 use futures_lite::{AsyncReadExt, AsyncWriteExt}; 9 use log::info; 10 use nix::errno::Errno; 11 use nix::fcntl::{OFlag, open}; 12 use nix::request_code_write; 13 use nix::sys::stat::Mode; 14 use nix::sys::time::{TimeSpec, TimeValLike}; 15 use nix::time::{ClockId, clock_gettime}; 16 use nix::unistd::close; 17 18 use kanit_common::constants; 19 use kanit_common::error::{Context, ErrorKind, Result}; 20 use kanit_unit::{Dependencies, Unit}; 21 22 use crate::oneshot::{Clock, LocalMount}; 23 use crate::unit_name; 24 25 pub struct Seed; 26 27 const RND_IOC_MAGIC: u8 = b'R'; 28 const RND_IOC_TYPE_MODE: u8 = 0x03; 29 const MAX_SEED_LEN: usize = 512; 30 31 #[repr(C)] 32 struct RandPoolInfo { 33 entropy_count: libc::c_int, 34 buf_size: libc::c_int, 35 buf: [u8; MAX_SEED_LEN], // defined as u32 but it doesn't *really* matter 36 } 37 38 impl Seed { 39 fn untrusted_seed() -> u64 { 40 let seconds = clock_gettime(ClockId::CLOCK_REALTIME) // TODO; unblock? 41 .unwrap_or_else(|_| { 42 clock_gettime(ClockId::CLOCK_BOOTTIME).unwrap_or(TimeSpec::new(0, 0)) 43 }) 44 .num_seconds(); 45 46 u64::from_be_bytes(seconds.to_be_bytes()) 47 } 48 49 async fn trusted_seed() -> Option<u64> { 50 if let Ok(mut data) = async_fs::read(constants::KAN_SEED).await { 51 data.resize(8, 0); 52 53 // since the vector was resized, this shouldn't fail 54 Some(u64::from_be_bytes(data.as_slice().try_into().unwrap())) 55 } else { 56 None 57 } 58 } 59 60 async fn write_bytes(bytes: &[u8], trusted: bool) -> Result<()> { 61 let buf: [u8; MAX_SEED_LEN] = match bytes.len().cmp(&MAX_SEED_LEN) { 62 Ordering::Greater => (&bytes[0..MAX_SEED_LEN]).try_into().unwrap(), 63 Ordering::Less => { 64 let mut buf = [0u8; MAX_SEED_LEN]; 65 66 let view = &mut buf[0..bytes.len()]; 67 68 view.copy_from_slice(bytes); 69 70 buf 71 } 72 Ordering::Equal => bytes.try_into().unwrap(), 73 }; 74 75 let entropy_count = if trusted { bytes.len() * 8 } else { 0 }; 76 77 let info = RandPoolInfo { 78 entropy_count: entropy_count as libc::c_int, 79 buf_size: bytes.len() as libc::c_int, 80 buf, // unwrap: we ensure the size of `bytes` 81 }; 82 83 unblock(move || { 84 let rand_fd = open("/dev/urandom", OFlag::O_RDONLY, Mode::empty()) 85 .context_kind("failed to open urandom", ErrorKind::Recoverable)?; 86 87 unsafe { 88 Errno::result(libc::ioctl( 89 rand_fd, 90 request_code_write!( 91 RND_IOC_MAGIC, 92 RND_IOC_TYPE_MODE, 93 size_of::<libc::c_int>() * 2 94 ), 95 &info as *const RandPoolInfo, 96 )) 97 } 98 .context_kind("failed to add entropy", ErrorKind::Recoverable)?; 99 100 close(rand_fd).context_kind("failed to close urandom", ErrorKind::Recoverable)?; 101 102 Ok(()) 103 }) 104 .await 105 } 106 } 107 108 #[async_trait] 109 impl Unit for Seed { 110 unit_name!("seed"); 111 112 fn dependencies(&self) -> Dependencies { 113 Dependencies::new() 114 .need(LocalMount.name()) 115 .after(Clock.name()) 116 .clone() 117 } 118 119 async fn start(&mut self) -> Result<()> { 120 info!("seeding random number generator"); 121 122 let t_seed = Self::trusted_seed().await; 123 124 let seed = t_seed.unwrap_or_else(Self::untrusted_seed); 125 126 let mut rand = fastrand::Rng::with_seed(seed); 127 128 let bytes = async_fs::read_to_string("/proc/sys/kernel/random/poolsize") 129 .await 130 .unwrap_or_else(|_| "2048".to_string()) 131 .parse::<usize>() 132 .unwrap_or(2048) 133 / 8; 134 135 // preload some bytes 136 let mut buf = Vec::with_capacity(bytes); 137 138 rand.fill(buf.as_mut_slice()); 139 140 Self::write_bytes(buf.as_slice(), t_seed.is_some()).await?; 141 142 // load previous seed 143 if let Ok(mut previous_seed) = async_fs::read(constants::KAN_SEED).await { 144 let len = previous_seed.len(); 145 146 previous_seed.resize(bytes, 0); 147 148 if bytes > len { 149 rand.fill(&mut previous_seed[len..]); 150 } 151 152 Self::write_bytes(previous_seed.as_slice(), true).await?; 153 } 154 155 Ok(()) 156 } 157 158 async fn stop(&mut self) -> Result<()> { 159 info!("saving random seed"); 160 161 let mut f = File::open("/dev/urandom") 162 .await 163 .context_kind("failed to open urandom", ErrorKind::Recoverable)?; 164 165 let mut buf = [0; 16]; 166 167 f.read_exact(&mut buf) 168 .await 169 .context_kind("failed to save random seed", ErrorKind::Recoverable)?; 170 171 let mut file = File::create(constants::KAN_SEED) 172 .await 173 .context_kind("failed to open seed file", ErrorKind::Recoverable)?; 174 175 let metadata = file 176 .metadata() 177 .await 178 .context_kind("failed to get metadata", ErrorKind::Recoverable)?; 179 180 metadata 181 .permissions() 182 .set_mode((Mode::S_IWUSR | Mode::S_IRUSR).bits()); 183 184 file.write(&buf) 185 .await 186 .context_kind("failed to write seed", ErrorKind::Recoverable)?; 187 188 Ok(()) 189 } 190 }