Skip to content

Instantly share code, notes, and snippets.

@lrvick
Created October 31, 2022 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lrvick/ef7670e1e94853d283ac9e35926d201a to your computer and use it in GitHub Desktop.
Save lrvick/ef7670e1e94853d283ac9e35926d201a to your computer and use it in GitHub Desktop.
Seeding the Linux Kernel Entropy pool using the ioctl RNDADDENTROPY interface.
use libc::{
c_int,
};
use std::{
mem::{size_of, align_of},
fs::{read_to_string},
fmt,
io::Read,
fs::File,
};
struct SystemError {
message: String,
}
impl fmt::Display for SystemError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", boot_time(), self.message)
}
}
// Log dmesg formatted log to console
fn dmesg(message: String){
println!("{} {}", boot_time(), message);
}
// Dmesg formatted seconds since boot
fn boot_time() -> String {
use libc::{clock_gettime, timespec, CLOCK_BOOTTIME};
let mut t = timespec { tv_sec: 0, tv_nsec: 0 };
unsafe { clock_gettime(CLOCK_BOOTTIME, &mut t as *mut timespec); }
format!("[ {: >4}.{}]", t.tv_sec, t.tv_nsec / 1000).to_string()
}
// Get the maximum size of the entropy pool
fn get_random_max() -> Result<usize, SystemError> {
let ps_path = "/proc/sys/kernel/random/poolsize";
let size_s = read_to_string(ps_path).unwrap_or_else(|_| String::new());
let size_s = size_s.strip_suffix("\n").unwrap_or(&size_s);
if size_s.is_empty(){
return Err(SystemError {
message: String::from("Failed to read kernel random poolsize"),
})
};
match size_s.parse::<usize>() {
Ok(size) => Ok(size),
Err(_) => Err(SystemError {
message: String::from("Failed to parse kernel random poolsize"),
}),
}
}
// Get the maximum size of the entropy pool
fn get_random_avail() -> Result<usize, SystemError> {
let ps_path = "/proc/sys/kernel/random/entropy_avail";
let size_s = read_to_string(ps_path).unwrap_or_else(|_| String::new());
let size_s = size_s.strip_suffix("\n").unwrap_or(&size_s);
if size_s.is_empty(){
return Err(SystemError {
message: String::from("Failed to read kernel random entropy_avail"),
})
};
match size_s.parse::<usize>() {
Ok(size) => Ok(size),
Err(_) => Err(SystemError {
message: String::from("Failed to parse kernel random poolsize"),
}),
}
}
const ENTROPY_IOCTL_BASE: u8 = b'R';
const ENTROPY_SETOPTIONS: u8 = 0x03;
pub unsafe fn rndaddentropy(
fd: c_int,
data: *const RandPoolInfo,
) -> c_int {
use libc::ioctl;
#[cfg(target_env = "musl")]
type IoType = ::libc::c_int;
#[cfg(not(target_env = "musl"))]
type IoType = ::libc::c_ulong;
const WRITE: u8 = 1;
const SIZEBITS: u8 = 14;
const DIRBITS: u8 = 2;
const NRBITS: IoType = 8;
const TYPEBITS: IoType = 8;
const NRSHIFT: IoType = 0;
const TYPESHIFT: IoType = NRSHIFT + NRBITS as IoType;
const SIZESHIFT: IoType = TYPESHIFT + TYPEBITS as IoType;
const DIRSHIFT: IoType = SIZESHIFT + SIZEBITS as IoType;
const NRMASK: IoType = (1 << NRBITS) - 1;
const TYPEMASK: IoType = (1 << TYPEBITS) - 1;
const SIZEMASK: IoType = (1 << SIZEBITS) - 1;
const DIRMASK: IoType = (1 << DIRBITS) - 1;
let result = ioctl(
fd,
( ((WRITE as IoType & DIRMASK) << DIRSHIFT)
| ((ENTROPY_IOCTL_BASE as IoType & TYPEMASK) << TYPESHIFT )
| ((ENTROPY_SETOPTIONS as IoType & NRMASK) << NRSHIFT)
| ((size_of::<RandPoolInfo>() as IoType & SIZEMASK) << SIZESHIFT)
) as IoType,
data,
);
result as c_int
}
pub struct RandPoolInfo {
entropy_count: i32,
buf_size: i32,
buf: [u8; 0],
}
fn write_entropy(random_fd: &mut File, data: &mut Vec<u8>) -> bool {
use std::alloc::{alloc, dealloc, Layout};
use std::os::fd::AsRawFd;
assert!(!data.is_empty());
if data.is_empty() {
return false;
}
unsafe {
let info_size = size_of::<RandPoolInfo>();
let layout =
Layout::from_size_align(data.len() + info_size, align_of::<u8>()).unwrap();
let ptr = alloc(layout);
let r_p_info = ptr as *mut RandPoolInfo;
(*r_p_info).entropy_count = (data.len() * 8) as i32;
(*r_p_info).buf_size = data.len() as i32;
let ptr_data = ptr.add(info_size);
for (index, value) in data.iter().enumerate() {
*ptr_data.add(index) = *value;
}
let result = rndaddentropy(
random_fd.as_raw_fd(),
ptr as *const RandPoolInfo
);
dealloc(ptr, layout);
if result !=0 {
false
} else {
true
}
}
}
fn seed_entropy(
max: usize,
source: fn(usize) -> Result<Vec<u8>, SystemError>,
) -> Result<usize, SystemError> {
let pool_size = match get_random_avail() {
Ok(file) => file,
Err(e) => { return Err(e) },
};
let sample_size = max - pool_size;
let mut entropy_sample = match source(sample_size) {
Ok(sample)=> sample,
Err(e)=> { return Err(e) },
};
use std::fs::OpenOptions;
let mut random_fd = match OpenOptions::new()
.read(true)
.write(true)
.open("/dev/urandom")
{
Ok(file) => file,
Err(_) => {
return Err(SystemError {
message: String::from("Failed to open /dev/urandom"),
});
},
};
if !write_entropy(&mut random_fd, &mut entropy_sample) {
return Err(SystemError {
message: String::from("Failed to write to /dev/urandom"),
});
}
Ok(entropy_sample.len())
}
fn getrandom_kernel(size: usize) -> Result<Vec<u8>, SystemError> {
use std::fs::OpenOptions;
let mut dest = Vec::with_capacity(size);
let mut random_fd = match OpenOptions::new()
.read(true)
.open("/dev/urandom")
{
Ok(file) => file,
Err(_) => {
return Err(SystemError {
message: String::from("Failed to open /dev/urandom"),
});
},
};
while dest.len() < size {
let mut buf = [0u8; 256];
match random_fd.read_exact(&mut buf) {
Ok(_) => (),
Err(_) => {
return Err(SystemError {
message: String::from("Failed to read from /dev/urandom"),
});
},
};
dest.extend_from_slice(&buf);
}
Ok(dest)
}
fn main() {
match seed_entropy(4096, getrandom_kernel) {
Ok(size)=> dmesg(format!("Seeded kernel with entropy: {}", size)),
Err(e)=> eprintln!("{}", e)
};
match getrandom_kernel(256) {
Ok(_)=> dmesg(format!("Got entropy sample from Kernel: {}", 256)),
Err(e)=> { eprintln!("{}",e) },
};
match getrandom_kernel(256) {
Ok(_)=> dmesg(format!("Got entropy sample from Kernel: {}", 256)),
Err(e)=> { eprintln!("{}",e) },
};
match getrandom_kernel(256) {
Ok(_)=> dmesg(format!("Got entropy sample from Kernel: {}", 256)),
Err(e)=> { eprintln!("{}",e) },
};
match get_random_max() {
Ok(size)=> dmesg(format!("Kernel entropy pool size: {}", size)),
Err(e)=> eprintln!("{}", e)
};
match get_random_avail() {
Ok(size)=> dmesg(format!("Kernel entropy available: {}", size)),
Err(e)=> eprintln!("{}", e)
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment