blob: fe1113474e1d46cd8065f3f46e1d0c739b0decf7 [file] [log] [blame]
Paul Crowley9da969e2022-09-16 23:42:24 +00001// 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//! FIPS compliant random number conditioner. Reads from /dev/hw_random
16//! and applies the NIST SP 800-90A CTR DRBG strategy to provide
17//! pseudorandom bytes to clients which connect to a socket provided
18//! by init.
19
20mod conditioner;
21mod cutils_socket;
22mod drbg;
23
24use std::{
25 convert::Infallible,
26 fs::{remove_file, File},
27 io::ErrorKind,
28 os::unix::{net::UnixListener, prelude::AsRawFd},
29 path::{Path, PathBuf},
30};
31
32use anyhow::Result;
33use clap::Parser;
34use log::{error, info};
35use nix::{
36 fcntl::{fcntl, FcntlArg::F_SETFL, OFlag},
37 sys::signal,
38};
39use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
40
41use crate::conditioner::Conditioner;
42
43#[derive(Debug, clap::Parser)]
44struct Cli {
45 #[clap(long, default_value = "/dev/hw_random")]
46 source: PathBuf,
47 #[clap(long)]
48 socket: Option<PathBuf>,
49}
50
51fn configure_logging() {
52 logger::init(Default::default());
53}
54
55fn get_socket(path: &Path) -> Result<UnixListener> {
56 if let Err(e) = remove_file(path) {
57 if e.kind() != ErrorKind::NotFound {
58 return Err(e.into());
59 }
60 } else {
61 info!("Deleted old {}", path.to_string_lossy());
62 }
63 Ok(UnixListener::bind(path)?)
64}
65
66async fn listen_loop(hwrng: File, listener: UnixListener) -> Result<Infallible> {
67 let mut conditioner = Conditioner::new(hwrng)?;
68 let listener = TokioUnixListener::from_std(listener)?;
69 loop {
70 match listener.accept().await {
71 Ok((mut stream, _)) => {
72 let new_bytes = conditioner.request()?;
73 tokio::spawn(async move {
74 if let Err(e) = stream.write_all(&new_bytes).await {
75 error!("Request failed: {}", e);
76 }
77 });
78 conditioner.reseed_if_necessary().await?;
79 }
80 Err(e) if e.kind() == ErrorKind::Interrupted => {}
81 Err(e) => return Err(e.into()),
82 }
83 }
84}
85
86fn run(cli: Cli) -> Result<Infallible> {
87 let hwrng = std::fs::File::open(&cli.source)?;
88 fcntl(hwrng.as_raw_fd(), F_SETFL(OFlag::O_NONBLOCK))?;
89 let listener = match cli.socket {
90 Some(path) => get_socket(path.as_path())?,
91 None => cutils_socket::android_get_control_socket("prng_seeder")?,
92 };
93 listener.set_nonblocking(true)?;
94
95 unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }?;
96
97 tokio::runtime::Builder::new_current_thread()
98 .enable_all()
99 .build()?
100 .block_on(async { listen_loop(hwrng, listener).await })
101}
102
103fn main() {
104 let cli = Cli::parse();
105 configure_logging();
106 if let Err(e) = run(cli) {
107 error!("Launch failed: {}", e);
108 } else {
109 error!("Loop terminated without an error")
110 }
111 std::process::exit(-1);
112}