diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index daa60f9c5..284d458c6 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -55,6 +55,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
 chrono = { version = "0.4", default-features = false, optional = true }
 embedded-io = { version = "0.4.0", features = ["async"], optional = true }
 embedded-storage = { version = "0.3" }
+rand_core = "0.6.4"
 
 rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
 #rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 1c446f389..85c9bbb7a 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -5,7 +5,7 @@ use crate::{pac, reset};
 const XOSC_MHZ: u32 = 12;
 
 /// safety: must be called exactly once at bootup
-pub unsafe fn init() {
+pub(crate) unsafe fn init() {
     // Reset everything except:
     // - QSPI (we're using it to run this code!)
     // - PLLs (it may be suicide if that's what's clocking us)
@@ -196,3 +196,40 @@ unsafe fn configure_pll(p: pac::pll::Pll, refdiv: u32, vco_freq: u32, post_div1:
     // Turn on post divider
     p.pwr().modify(|w| w.set_postdivpd(false));
 }
+
+/// Random number generator based on the ROSC RANDOMBIT register.
+///
+/// This will not produce random values if the ROSC is stopped or run at some
+/// harmonic of the bus frequency. With default clock settings these are not
+/// issues.
+pub struct RoscRng;
+
+impl RoscRng {
+    fn next_u8() -> u8 {
+        let random_reg = pac::ROSC.randombit();
+        let mut acc = 0;
+        for _ in 0..u8::BITS {
+            acc <<= 1;
+            acc |= unsafe { random_reg.read().randombit() as u8 };
+        }
+        acc
+    }
+}
+
+impl rand_core::RngCore for RoscRng {
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
+        Ok(self.fill_bytes(dest))
+    }
+
+    fn next_u32(&mut self) -> u32 {
+        rand_core::impls::next_u32_via_fill(self)
+    }
+
+    fn next_u64(&mut self) -> u64 {
+        rand_core::impls::next_u64_via_fill(self)
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        dest.fill_with(Self::next_u8)
+    }
+}
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index e5b07c903..d21b5f7b0 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -21,7 +21,7 @@ pub mod uart;
 #[cfg(feature = "nightly")]
 pub mod usb;
 
-mod clocks;
+pub mod clocks;
 pub mod flash;
 mod reset;