-
Notifications
You must be signed in to change notification settings - Fork 9
Random Number Generator API #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
f9b4ec6
5c7011c
a9a32fb
6eefddb
3d1ade8
cafc14b
8fa766d
3978761
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| [package] | ||
| name = "libtock_rng" | ||
| version = "0.1.0" | ||
| authors = ["Tock Project Developers <[email protected]>"] | ||
| license = "MIT/Apache-2.0" | ||
| edition = "2021" | ||
| repository = "https://www.github.com/tock/libtock-rs" | ||
| description = "libtock rng driver" | ||
|
|
||
| [dependencies] | ||
| libtock_platform = { path = "../../platform" } | ||
|
|
||
| [dev-dependencies] | ||
| libtock_unittest = { path = "../../unittest" } | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,88 @@ | ||||||||
| #![no_std] | ||||||||
|
|
||||||||
| use core::cell::Cell; | ||||||||
|
|
||||||||
| use libtock_platform::{share, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls}; | ||||||||
| pub struct Rng<S: Syscalls>(S); | ||||||||
|
|
||||||||
| impl<S: Syscalls> Rng<S> { | ||||||||
| /// Returns Ok() if the driver was present.This does not necessarily mean | ||||||||
| /// that the driver is working. | ||||||||
| pub fn exists() -> Result<(), ErrorCode> { | ||||||||
| S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() | ||||||||
| } | ||||||||
|
|
||||||||
| /// Register a listener to be called when the random generation is finished | ||||||||
| pub fn register_listener<'share>( | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to implement these changes but I don't know how could that |
||||||||
| listener: &'share Cell<Option<(u32, u32)>>, | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do that two
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first one is always 0, and the second one represents the number of random bytes successfuly written in the buffer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to I suggest adding a pub struct RandomDataListener<share etc, F: Fn(&'share [u8])>(pub F);
impl<F: Fn(u32)> Upcall<OneId<DRIVER_NUM, 0>> for IntensityListener<F> {
fn upcall(&self, _arg0: u32, len: u32, _arg2: u32) {
// unshare buffer
f(buffer[0..len]);
// share buffer back
}
}Add an |
||||||||
| subscribe: share::Handle<Subscribe<'share, S, DRIVER_NUM, 0>>, | ||||||||
| ) -> Result<(), ErrorCode> { | ||||||||
| S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Sets the buffer in which the random numbers will be written | ||||||||
| pub fn set_buffer<'share>( | ||||||||
| buffer: &'share mut [u8], | ||||||||
| allow_rw: share::Handle<AllowRw<'share, S, DRIVER_NUM, RW_ALLOW>>, | ||||||||
| ) -> Result<(), ErrorCode> { | ||||||||
| S::allow_rw::<DefaultConfig, DRIVER_NUM, RW_ALLOW>(allow_rw, buffer) | ||||||||
| } | ||||||||
|
|
||||||||
| /// Initiates an async random generation for n bytes | ||||||||
| /// A buffer and a callback should have been set before | ||||||||
| pub fn get_random(n: u32) -> Result<(), ErrorCode> { | ||||||||
| S::command(DRIVER_NUM, ASK_FOR_RANDOM_BYTES, n, 0).to_result() | ||||||||
| } | ||||||||
|
|
||||||||
| /// Initiates a synchronous random number generation | ||||||||
| /// `n` random bytes will be written in `buf` | ||||||||
| /// n must be smaller or equal to buf.len() | ||||||||
| /// returns the number of bytes successfully written or error | ||||||||
| pub fn get_random_sync(buf: &mut [u8], n: u32) -> Result<u32, ErrorCode> { | ||||||||
| if n > (buf.len() as u32) { | ||||||||
| return Err(ErrorCode::Size); | ||||||||
| } | ||||||||
|
|
||||||||
| let listener: Cell<Option<(u32, u32)>> = Cell::new(None); | ||||||||
| share::scope::< | ||||||||
| ( | ||||||||
| AllowRw<_, DRIVER_NUM, RW_ALLOW>, | ||||||||
| Subscribe<_, DRIVER_NUM, 0>, | ||||||||
| ), | ||||||||
| _, | ||||||||
| _, | ||||||||
| >(|handle| { | ||||||||
| let (allow_rw, subscribe) = handle.split(); | ||||||||
|
|
||||||||
| Self::set_buffer(buf, allow_rw)?; | ||||||||
| Self::register_listener(&listener, subscribe)?; | ||||||||
| Self::get_random(n)?; | ||||||||
| while listener.get() == None { | ||||||||
| S::yield_wait(); | ||||||||
| } | ||||||||
|
|
||||||||
| Ok(()) | ||||||||
| })?; | ||||||||
|
|
||||||||
| match listener.get() { | ||||||||
| Some((_, bytes_received)) => Ok(bytes_received), | ||||||||
alexandruradovici marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
| None => Err(ErrorCode::Fail), | ||||||||
| } | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| #[cfg(test)] | ||||||||
| mod tests; | ||||||||
|
|
||||||||
| // ----------------------------------------------------------------------------- | ||||||||
| // Driver number and command IDs | ||||||||
| // ----------------------------------------------------------------------------- | ||||||||
|
|
||||||||
| const DRIVER_NUM: u32 = 0x40001; | ||||||||
|
|
||||||||
| // Command IDs | ||||||||
|
|
||||||||
| const EXISTS: u32 = 0; | ||||||||
| const ASK_FOR_RANDOM_BYTES: u32 = 1; | ||||||||
|
|
||||||||
| const RW_ALLOW: u32 = 0; | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| use super::*; | ||
| use core::cell::Cell; | ||
| use libtock_platform::{share, AllowRw, ErrorCode, Subscribe, YieldNoWaitReturn}; | ||
| use libtock_unittest::fake; | ||
|
|
||
| type Rng = super::Rng<fake::Syscalls>; | ||
|
|
||
| #[test] | ||
| fn no_driver() { | ||
| let _kernel = fake::Kernel::new(); | ||
| assert_eq!(Rng::exists(), Err(ErrorCode::NoDevice)); | ||
| } | ||
|
|
||
| #[test] | ||
| fn driver_check() { | ||
| let kernel = fake::Kernel::new(); | ||
| let driver = fake::Rng::new(); | ||
| kernel.add_driver(&driver); | ||
|
|
||
| assert_eq!(Rng::exists(), Ok(())); | ||
| } | ||
|
|
||
| #[test] | ||
| fn get_random() { | ||
| let kernel = fake::Kernel::new(); | ||
| let driver = fake::Rng::new(); | ||
| kernel.add_driver(&driver); | ||
|
|
||
| let mut buf: [u8; 10] = [0; 10]; | ||
| let listener: Cell<Option<(u32, u32)>> = Cell::new(None); | ||
|
|
||
| share::scope::< | ||
| ( | ||
| AllowRw<_, DRIVER_NUM, RW_ALLOW>, | ||
| Subscribe<_, DRIVER_NUM, 0>, | ||
| ), | ||
| _, | ||
| _, | ||
| >( | ||
| |handle: share::Handle<( | ||
| AllowRw<fake::Syscalls, DRIVER_NUM, RW_ALLOW>, | ||
| Subscribe<fake::Syscalls, DRIVER_NUM, 0>, | ||
| )>| { | ||
| let (allow_rw, subscribe) = handle.split(); | ||
|
|
||
| assert!(Rng::set_buffer(&mut buf, allow_rw).is_ok()); | ||
| assert!(Rng::register_listener(&listener, subscribe).is_ok()); | ||
|
|
||
| assert!(Rng::get_random(5).is_ok()); | ||
|
|
||
| driver.add_bytes(&[1, 2, 3]); | ||
|
|
||
| assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
|
|
||
| driver.add_bytes(&[4, 5, 6]); | ||
|
|
||
| assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); | ||
|
|
||
| assert_eq!(listener.get(), Some((0, 5))); | ||
| }, | ||
| ); | ||
| assert!(buf[..5].eq(&[1, 2, 3, 4, 5])); | ||
| } | ||
| #[test] | ||
| fn get_sync() { | ||
| let kernel = fake::Kernel::new(); | ||
| let driver = fake::Rng::new(); | ||
| kernel.add_driver(&driver); | ||
|
|
||
| driver.add_bytes_sync(&[10, 20, 30, 40, 50]); | ||
|
|
||
| let mut buf: [u8; 10] = [0; 10]; | ||
|
|
||
| assert_eq!(Rng::get_random_sync(&mut buf, 5), Ok(5)); | ||
| assert!(buf[..5].eq(&[10, 20, 30, 40, 50])); | ||
|
|
||
| driver.add_bytes_sync(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); | ||
|
|
||
| assert_eq!(Rng::get_random_sync(&mut buf, 11), Err(ErrorCode::Size)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| //! A simple libtock-rs example. Checks for random number generator driver | ||
| //! asks for some random numbers and prints them. | ||
|
|
||
| #![no_main] | ||
| #![no_std] | ||
|
|
||
| use core::fmt::Write; | ||
| use libtock::console::Console; | ||
| use libtock::rng::Rng; | ||
| use libtock::runtime::{set_main, stack_size}; | ||
| set_main! {main} | ||
| stack_size! {0x200} | ||
|
|
||
| fn main() { | ||
| if Rng::exists().is_err() { | ||
| writeln!( | ||
| Console::writer(), | ||
| "Random number generator driver unavailable" | ||
| ) | ||
| .unwrap(); | ||
| return; | ||
| } | ||
|
|
||
| writeln!( | ||
| Console::writer(), | ||
| "Random number generator driver available" | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let mut buf: [u8; 10] = [0; 10]; | ||
|
|
||
| match Rng::get_random_sync(&mut buf[..], 10) { | ||
| Ok(bytes_received) => { | ||
| writeln!( | ||
| Console::writer(), | ||
| "Received {} random bytes. Buf:", | ||
| bytes_received, | ||
| ) | ||
| .unwrap(); | ||
| for byte in buf.iter() { | ||
| writeln!(Console::writer(), "{} ", byte).unwrap(); | ||
| } | ||
| } | ||
| Err(_) => { | ||
| writeln!(Console::writer(), "FAIL").unwrap(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| use std::cell::{Cell, RefCell}; | ||
|
|
||
| use crate::{DriverInfo, DriverShareRef, RwAllowBuffer}; | ||
| use core::cmp; | ||
| use libtock_platform::ErrorCode; | ||
|
|
||
| pub struct Rng { | ||
| buffer: RefCell<RwAllowBuffer>, | ||
| remaining: Cell<usize>, | ||
| idx: Cell<usize>, | ||
| getting_randomness: Cell<bool>, | ||
| share_ref: DriverShareRef, | ||
| random_numbers: Cell<Option<Vec<u8>>>, | ||
| } | ||
|
|
||
| impl Rng { | ||
| pub fn new() -> std::rc::Rc<Rng> { | ||
| std::rc::Rc::new(Rng { | ||
| buffer: Default::default(), | ||
| remaining: Cell::new(0), | ||
| idx: Cell::new(0), | ||
| getting_randomness: Cell::new(false), | ||
| share_ref: Default::default(), | ||
| random_numbers: Cell::new(None), | ||
| }) | ||
| } | ||
|
|
||
| pub fn add_bytes(&self, buf: &[u8]) { | ||
| if !self.getting_randomness.get() { | ||
| return; | ||
| } | ||
|
|
||
| // this would happen only if the buffer was changed | ||
| if self.idx.get() > self.buffer.borrow().len() { | ||
| self.idx.set(0); | ||
| self.remaining.set(0); | ||
| } else { | ||
| if self.idx.get() + self.remaining.get() > self.buffer.borrow().len() { | ||
| self.remaining | ||
| .set(self.buffer.borrow().len() - self.idx.get()) | ||
| } | ||
|
|
||
| let bytes_to_add = cmp::min(self.remaining.get(), buf.len()); | ||
| self.buffer.borrow_mut()[self.idx.get()..self.idx.get() + bytes_to_add] | ||
| .copy_from_slice(&buf[..bytes_to_add]); | ||
| self.remaining.set(self.remaining.get() - bytes_to_add); | ||
| self.idx.set(self.idx.get() + bytes_to_add); | ||
| } | ||
|
|
||
| if self.remaining.get() == 0 { | ||
| self.share_ref | ||
| .schedule_upcall(0, (0, self.idx.get() as u32, 0)) | ||
| .expect("Unable to schedule upcall"); | ||
| self.getting_randomness.set(false); | ||
| self.random_numbers.set(None); | ||
| } | ||
| } | ||
|
|
||
| pub fn add_bytes_sync(&self, buf: &[u8]) { | ||
| self.random_numbers.set(Some(Vec::from(buf))) | ||
| } | ||
| } | ||
|
|
||
| impl crate::fake::SyscallDriver for Rng { | ||
| fn info(&self) -> DriverInfo { | ||
| DriverInfo::new(DRIVER_NUM).upcall_count(1) | ||
| } | ||
|
|
||
| fn register(&self, share_ref: DriverShareRef) { | ||
| self.share_ref.replace(share_ref); | ||
| } | ||
|
|
||
| fn allow_readwrite( | ||
| &self, | ||
| buffer_num: u32, | ||
| buffer: RwAllowBuffer, | ||
| ) -> Result<RwAllowBuffer, (RwAllowBuffer, libtock_platform::ErrorCode)> { | ||
| if buffer_num == RW_ALLOW { | ||
| Ok(self.buffer.replace(buffer)) | ||
| } else { | ||
| Err((buffer, ErrorCode::Invalid)) | ||
| } | ||
| } | ||
|
|
||
| fn command(&self, command_id: u32, data: u32, _: u32) -> libtock_platform::CommandReturn { | ||
| match command_id { | ||
| EXISTS => crate::command_return::success(), | ||
|
|
||
| ASK_FOR_RANDOM_BYTES => { | ||
| self.remaining.set(data as usize); | ||
| self.idx.set(0); | ||
| self.getting_randomness.set(true); | ||
|
|
||
| if let Some(numbers) = self.random_numbers.take() { | ||
| self.add_bytes(&numbers); | ||
| } | ||
|
|
||
| crate::command_return::success() | ||
| } | ||
| _ => crate::command_return::failure(ErrorCode::NoSupport), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests; | ||
|
|
||
| // ----------------------------------------------------------------------------- | ||
| // Driver number and command IDs | ||
| // ----------------------------------------------------------------------------- | ||
|
|
||
| const DRIVER_NUM: u32 = 0x40001; | ||
|
|
||
| // Command IDs | ||
|
|
||
| const EXISTS: u32 = 0; | ||
| const ASK_FOR_RANDOM_BYTES: u32 = 1; | ||
|
|
||
| const RW_ALLOW: u32 = 0; |
Uh oh!
There was an error while loading. Please reload this page.