diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-10-05 23:35:37 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-10-05 23:35:37 +0000 |
commit | 727d5df64ddee3f93443ba1e9c57a993a60d714f (patch) | |
tree | 6c9bad788bb991c5f40d5e516f50878e0c339941 | |
parent | b7560bcad944e3a043183e27ce63ec7457d623c2 (diff) | |
parent | a71c519140e8ca3c0dbfd502947a66eccb410527 (diff) | |
download | security-android13-qpr2-s6-release.tar.gz |
Snap for 9143244 from a71c519140e8ca3c0dbfd502947a66eccb410527 to tm-qpr2-releaseandroid-13.0.0_r49android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32android13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-release
Change-Id: I79bd39487d24283d3b62228d47f169b6753f575e
-rw-r--r-- | prng_seeder/Android.bp | 49 | ||||
-rw-r--r-- | prng_seeder/cutils_wrapper.h | 15 | ||||
-rw-r--r-- | prng_seeder/prng_seeder.rc | 12 | ||||
-rw-r--r-- | prng_seeder/src/conditioner.rs | 76 | ||||
-rw-r--r-- | prng_seeder/src/cutils_socket.rs | 25 | ||||
-rw-r--r-- | prng_seeder/src/drbg.rs | 65 | ||||
-rw-r--r-- | prng_seeder/src/main.rs | 136 |
7 files changed, 378 insertions, 0 deletions
diff --git a/prng_seeder/Android.bp b/prng_seeder/Android.bp new file mode 100644 index 00000000..f99dc929 --- /dev/null +++ b/prng_seeder/Android.bp @@ -0,0 +1,49 @@ +// Copyright (C) 2022 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. + +package { + // See: http://go/android-license-faq + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["system_security_license"], +} + +rust_bindgen { + name: "libcutils_socket_bindgen", + crate_name: "cutils_socket_bindgen", + wrapper_src: "cutils_wrapper.h", + source_stem: "bindings", + bindgen_flags: [ + "--allowlist-function=android_get_control_socket", + ], + shared_libs: [ + "libcutils", + ], +} + +rust_binary { + name: "prng_seeder", + edition: "2021", + srcs: ["src/main.rs"], + rustlibs: [ + "libanyhow", + "libbssl_ffi", + "libcutils_socket_bindgen", + "liblogger", + "liblog_rust", + "libnix", + "libtokio", + ], + + init_rc: ["prng_seeder.rc"], +} diff --git a/prng_seeder/cutils_wrapper.h b/prng_seeder/cutils_wrapper.h new file mode 100644 index 00000000..9c1fe565 --- /dev/null +++ b/prng_seeder/cutils_wrapper.h @@ -0,0 +1,15 @@ +// Copyright (C) 2022 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. + +#include <cutils/sockets.h> diff --git a/prng_seeder/prng_seeder.rc b/prng_seeder/prng_seeder.rc new file mode 100644 index 00000000..9825583a --- /dev/null +++ b/prng_seeder/prng_seeder.rc @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Start PRNG seeder daemon from early-init + +on early-init + start prng_seeder + +service prng_seeder /system/bin/prng_seeder + user prng_seeder + group prng_seeder + stdio_to_kmsg + socket prng_seeder stream+listen 0666 prng_seeder prng_seeder diff --git a/prng_seeder/src/conditioner.rs b/prng_seeder/src/conditioner.rs new file mode 100644 index 00000000..eca8af88 --- /dev/null +++ b/prng_seeder/src/conditioner.rs @@ -0,0 +1,76 @@ +// Copyright (C) 2022 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. + +use std::{fs::File, io::Read, os::unix::io::AsRawFd}; + +use anyhow::{ensure, Context, Result}; +use log::debug; +use nix::fcntl::{fcntl, FcntlArg::F_SETFL, OFlag}; +use tokio::io::AsyncReadExt; + +use crate::drbg; + +const SEED_FOR_CLIENT_LEN: usize = 496; +const NUM_REQUESTS_PER_RESEED: u32 = 256; + +pub struct ConditionerBuilder { + hwrng: File, + rg: drbg::Drbg, +} + +impl ConditionerBuilder { + pub fn new(mut hwrng: File) -> Result<ConditionerBuilder> { + let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN]; + hwrng.read_exact(&mut et).context("hwrng.read_exact in new")?; + let rg = drbg::Drbg::new(&et)?; + fcntl(hwrng.as_raw_fd(), F_SETFL(OFlag::O_NONBLOCK)) + .context("setting O_NONBLOCK on hwrng")?; + Ok(ConditionerBuilder { hwrng, rg }) + } + + pub fn build(self) -> Conditioner { + Conditioner { + hwrng: tokio::fs::File::from_std(self.hwrng), + rg: self.rg, + requests_since_reseed: 0, + } + } +} + +pub struct Conditioner { + hwrng: tokio::fs::File, + rg: drbg::Drbg, + requests_since_reseed: u32, +} + +impl Conditioner { + pub async fn reseed_if_necessary(&mut self) -> Result<()> { + if self.requests_since_reseed >= NUM_REQUESTS_PER_RESEED { + debug!("Reseeding DRBG"); + let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN]; + self.hwrng.read_exact(&mut et).await.context("hwrng.read_exact in reseed")?; + self.rg.reseed(&et)?; + self.requests_since_reseed = 0; + } + Ok(()) + } + + pub fn request(&mut self) -> Result<[u8; SEED_FOR_CLIENT_LEN]> { + ensure!(self.requests_since_reseed < NUM_REQUESTS_PER_RESEED, "Not enough reseeds"); + let mut seed_for_client = [0u8; SEED_FOR_CLIENT_LEN]; + self.rg.generate(&mut seed_for_client)?; + self.requests_since_reseed += 1; + Ok(seed_for_client) + } +} diff --git a/prng_seeder/src/cutils_socket.rs b/prng_seeder/src/cutils_socket.rs new file mode 100644 index 00000000..ab2c8698 --- /dev/null +++ b/prng_seeder/src/cutils_socket.rs @@ -0,0 +1,25 @@ +// Copyright (C) 2022 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. + +use std::ffi::CString; +use std::os::unix::{net::UnixListener, prelude::FromRawFd}; + +use anyhow::{ensure, Result}; + +pub fn android_get_control_socket(name: &str) -> Result<UnixListener> { + let name = CString::new(name)?; + let fd = unsafe { cutils_socket_bindgen::android_get_control_socket(name.as_ptr()) }; + ensure!(fd >= 0, "android_get_control_socket failed"); + Ok(unsafe { UnixListener::from_raw_fd(fd) }) +} diff --git a/prng_seeder/src/drbg.rs b/prng_seeder/src/drbg.rs new file mode 100644 index 00000000..89c5a888 --- /dev/null +++ b/prng_seeder/src/drbg.rs @@ -0,0 +1,65 @@ +// Copyright (C) 2022 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. + +use anyhow::{ensure, Result}; +use bssl_ffi as bssl_sys; + +pub const ENTROPY_LEN: usize = bssl_sys::CTR_DRBG_ENTROPY_LEN as usize; + +pub type Entropy = [u8; ENTROPY_LEN]; + +pub struct Drbg(*mut bssl_sys::CTR_DRBG_STATE); + +impl Drbg { + pub fn new(entropy: &Entropy) -> Result<Drbg> { + let p = unsafe { bssl_sys::CTR_DRBG_new(entropy.as_ptr(), std::ptr::null(), 0) }; + ensure!(!p.is_null(), "CTR_DRBG_new failed"); + Ok(Drbg(p)) + } + + pub fn reseed(&mut self, entropy: &Entropy) -> Result<()> { + ensure!( + unsafe { bssl_sys::CTR_DRBG_reseed(self.0, entropy.as_ptr(), std::ptr::null(), 0) } + == 1, + "CTR_DRBG_reseed failed" + ); + Ok(()) + } + + pub fn generate(&mut self, buf: &mut [u8]) -> Result<()> { + ensure!( + unsafe { + bssl_sys::CTR_DRBG_generate( + self.0, + buf.as_mut_ptr(), + buf.len(), + std::ptr::null(), + 0, + ) + } == 1, + "CTR_DRBG_generate failed" + ); + Ok(()) + } +} + +impl Drop for Drbg { + fn drop(&mut self) { + unsafe { + bssl_sys::CTR_DRBG_free(self.0); + } + } +} + +unsafe impl Send for Drbg {} diff --git a/prng_seeder/src/main.rs b/prng_seeder/src/main.rs new file mode 100644 index 00000000..10f853dd --- /dev/null +++ b/prng_seeder/src/main.rs @@ -0,0 +1,136 @@ +// Copyright (C) 2022 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. + +//! FIPS compliant random number conditioner. Reads from /dev/hw_random +//! and applies the NIST SP 800-90A CTR DRBG strategy to provide +//! pseudorandom bytes to clients which connect to a socket provided +//! by init. + +mod conditioner; +mod cutils_socket; +mod drbg; + +use std::{ + convert::Infallible, + fs::remove_file, + io::ErrorKind, + os::unix::net::UnixListener, + path::{Path, PathBuf}, +}; + +use anyhow::{ensure, Context, Result}; +use log::{error, info, Level}; +use nix::sys::signal; +use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener}; + +use crate::conditioner::ConditionerBuilder; + +//#[derive(Debug, clap::Parser)] +struct Cli { + //#[clap(long, default_value = "/dev/hw_random")] + source: PathBuf, + //#[clap(long)] + socket: Option<PathBuf>, +} + +fn configure_logging() -> Result<()> { + ensure!( + logger::init( + logger::Config::default().with_tag_on_device("prng_seeder").with_min_level(Level::Info) + ), + "log configuration failed" + ); + Ok(()) +} + +fn get_socket(path: &Path) -> Result<UnixListener> { + if let Err(e) = remove_file(path) { + if e.kind() != ErrorKind::NotFound { + return Err(e).context(format!("Removing old socket: {}", path.display())); + } + } else { + info!("Deleted old {}", path.display()); + } + UnixListener::bind(path) + .with_context(|| format!("In get_socket: binding socket to {}", path.display())) +} + +fn setup() -> Result<(ConditionerBuilder, UnixListener)> { + configure_logging()?; + let cli = Cli { source: PathBuf::from("/dev/hw_random"), socket: None }; + unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) } + .context("In setup, setting SIGPIPE to SIG_IGN")?; + + let listener = match cli.socket { + Some(path) => get_socket(path.as_path())?, + None => cutils_socket::android_get_control_socket("prng_seeder") + .context("In setup, calling android_get_control_socket")?, + }; + let hwrng = std::fs::File::open(&cli.source) + .with_context(|| format!("Unable to open hwrng {}", cli.source.display()))?; + let cb = ConditionerBuilder::new(hwrng)?; + Ok((cb, listener)) +} + +async fn listen_loop(cb: ConditionerBuilder, listener: UnixListener) -> Result<Infallible> { + let mut conditioner = cb.build(); + listener.set_nonblocking(true).context("In listen_loop, on set_nonblocking")?; + let listener = TokioUnixListener::from_std(listener).context("In listen_loop, on from_std")?; + info!("Starting listen loop"); + loop { + match listener.accept().await { + Ok((mut stream, _)) => { + let new_bytes = conditioner.request()?; + tokio::spawn(async move { + if let Err(e) = stream.write_all(&new_bytes).await { + error!("Request failed: {}", e); + } + }); + conditioner.reseed_if_necessary().await?; + } + Err(e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => return Err(e).context("accept on socket failed"), + } + } +} + +fn run() -> Result<Infallible> { + let (cb, listener) = match setup() { + Ok(t) => t, + Err(e) => { + // If setup fails, just hang forever. That way init doesn't respawn us. + error!("Hanging forever because setup failed: {:?}", e); + // Logs are sometimes mysteriously not being logged, so print too + println!("prng_seeder: Hanging forever because setup failed: {:?}", e); + loop { + std::thread::park(); + error!("std::thread::park() finished unexpectedly, re-parking thread"); + } + } + }; + + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .context("In run, building reactor")? + .block_on(async { listen_loop(cb, listener).await }) +} + +fn main() { + let e = run(); + error!("Launch terminated: {:?}", e); + // Logs are sometimes mysteriously not being logged, so print too + println!("prng_seeder: launch terminated: {:?}", e); + std::process::exit(-1); +} |