diff --git a/Cargo.lock b/Cargo.lock
index 3a5a2b1a0bbe907c5e9f19def03a7b33057b5097..b008c5d36d2cae7a67a66a9b817022852c2d4252 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -153,6 +153,7 @@ dependencies = [
  "once_cell",
  "openssl",
  "percent-encoding 2.1.0",
+ "rand 0.7.3",
  "regex",
  "reqwest",
  "ring",
diff --git a/Cargo.toml b/Cargo.toml
index 396a5e81e5b1ccfd0b63266f52979fd4b71d9a18..10876570230aae10831fe6bb3264689b81af21bc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -60,7 +60,8 @@ diesel_migrations = "1.4.0"
 # Bundled SQLite
 libsqlite3-sys = { version = "0.18.0", features = ["bundled"], optional = true }
 
-# Crypto library
+# Crypto-related libraries
+rand = "0.7.3"
 ring = "0.16.15"
 
 # UUID generation
diff --git a/src/crypto.rs b/src/crypto.rs
index c2da8fbdbbea8d6be21f7dc7bb339d066d263aa1..7d0440c73f66bec1ff70277e80767ae8dffc860d 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -55,17 +55,21 @@ pub fn get_random(mut array: Vec<u8>) -> Vec<u8> {
 }
 
 pub fn generate_token(token_size: u32) -> Result<String, Error> {
+    // A u64 can represent all whole numbers up to 19 digits long.
     if token_size > 19 {
-        err!("Generating token failed")
+        err!("Token size is limited to 19 digits")
     }
 
-    // 8 bytes to create an u64 for up to 19 token digits
-    let bytes = get_random(vec![0; 8]);
-    let mut bytes_array = [0u8; 8];
-    bytes_array.copy_from_slice(&bytes);
+    let low: u64 = 0;
+    let high: u64 = 10u64.pow(token_size);
 
-    let number = u64::from_be_bytes(bytes_array) % 10u64.pow(token_size);
+    // Generate a random number in the range [low, high), then format it as a
+    // token of fixed width, left-padding with 0 as needed.
+    use rand::{thread_rng, Rng};
+    let mut rng = thread_rng();
+    let number: u64 = rng.gen_range(low, high);
     let token = format!("{:0size$}", number, size = token_size as usize);
+
     Ok(token)
 }