diff --git a/src/api/core/two_factor/authenticator.rs b/src/api/core/two_factor/authenticator.rs
index 7373b581bfc0681131df4d7cd4139e823fd079cd..68342e950ff87cca881be533de99a31bf4bac05f 100644
--- a/src/api/core/two_factor/authenticator.rs
+++ b/src/api/core/two_factor/authenticator.rs
@@ -34,7 +34,7 @@ async fn generate_authenticator(data: JsonUpcase<PasswordData>, headers: Headers
 
     let (enabled, key) = match twofactor {
         Some(tf) => (true, tf.data),
-        _ => (false, BASE32.encode(&crypto::get_random(vec![0u8; 20]))),
+        _ => (false, crypto::encode_random_bytes::<20>(BASE32)),
     };
 
     Ok(Json(json!({
diff --git a/src/api/core/two_factor/mod.rs b/src/api/core/two_factor/mod.rs
index 78f1318eecd2ebb4f1947ec24636d8a231817397..3d5eee83736c5e4bab150c2f2a460e123f942e71 100644
--- a/src/api/core/two_factor/mod.rs
+++ b/src/api/core/two_factor/mod.rs
@@ -105,7 +105,7 @@ async fn recover(data: JsonUpcase<RecoverTwoFactor>, mut conn: DbConn) -> JsonRe
 
 async fn _generate_recover_code(user: &mut User, conn: &mut DbConn) {
     if user.totp_recover.is_none() {
-        let totp_recover = BASE32.encode(&crypto::get_random(vec![0u8; 20]));
+        let totp_recover = crypto::encode_random_bytes::<20>(BASE32);
         user.totp_recover = Some(totp_recover);
         user.save(conn).await.ok();
     }
diff --git a/src/api/notifications.rs b/src/api/notifications.rs
index 2657d312b3d6d5139e7a94b58ad5c90b7cd98ec6..cd53c96d725af3e7202ff4250a7b21c6a1bf5efa 100644
--- a/src/api/notifications.rs
+++ b/src/api/notifications.rs
@@ -56,7 +56,7 @@ fn negotiate(_headers: Headers) -> Json<JsonValue> {
     use crate::crypto;
     use data_encoding::BASE64URL;
 
-    let conn_id = BASE64URL.encode(&crypto::get_random(vec![0u8; 16]));
+    let conn_id = crypto::encode_random_bytes::<16>(BASE64URL);
     let mut available_transports: Vec<JsonValue> = Vec::new();
 
     if CONFIG.websocket_enabled() {
diff --git a/src/crypto.rs b/src/crypto.rs
index be9680cb3e7056307df456341ef4e7523b21b974..daf5124dd2e24def8ee11fac96699f086dfb8102 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -3,7 +3,7 @@
 //
 use std::num::NonZeroU32;
 
-use data_encoding::HEXLOWER;
+use data_encoding::{Encoding, HEXLOWER};
 use ring::{digest, hmac, pbkdf2};
 
 static DIGEST_ALG: pbkdf2::Algorithm = pbkdf2::PBKDF2_HMAC_SHA256;
@@ -38,17 +38,24 @@ pub fn hmac_sign(key: &str, data: &str) -> String {
 //
 
 pub fn get_random_64() -> Vec<u8> {
-    get_random(vec![0u8; 64])
+    get_random_bytes::<64>().to_vec()
 }
 
-pub fn get_random(mut array: Vec<u8>) -> Vec<u8> {
+/// Return an array holding `N` random bytes.
+pub fn get_random_bytes<const N: usize>() -> [u8; N] {
     use ring::rand::{SecureRandom, SystemRandom};
 
+    let mut array = [0; N];
     SystemRandom::new().fill(&mut array).expect("Error generating random values");
 
     array
 }
 
+/// Encode random bytes using the provided function.
+pub fn encode_random_bytes<const N: usize>(e: Encoding) -> String {
+    e.encode(&get_random_bytes::<N>())
+}
+
 /// Generates a random string over a specified alphabet.
 pub fn get_random_string(alphabet: &[u8], num_chars: usize) -> String {
     // Ref: https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html
@@ -77,18 +84,18 @@ pub fn get_random_string_alphanum(num_chars: usize) -> String {
     get_random_string(ALPHABET, num_chars)
 }
 
-pub fn generate_id(num_bytes: usize) -> String {
-    HEXLOWER.encode(&get_random(vec![0; num_bytes]))
+pub fn generate_id<const N: usize>() -> String {
+    encode_random_bytes::<N>(HEXLOWER)
 }
 
 pub fn generate_send_id() -> String {
     // Send IDs are globally scoped, so make them longer to avoid collisions.
-    generate_id(32) // 256 bits
+    generate_id::<32>() // 256 bits
 }
 
 pub fn generate_attachment_id() -> String {
     // Attachment IDs are scoped to a cipher, so they can be smaller.
-    generate_id(10) // 80 bits
+    generate_id::<10>() // 80 bits
 }
 
 /// Generates a numeric token for email-based verifications.
diff --git a/src/db/models/device.rs b/src/db/models/device.rs
index e8a933cbdab1ef2e46f41ec9b9bf3641b7a0e66b..e47ccadcb841f5783233e9cf77232fd81e6d7568 100644
--- a/src/db/models/device.rs
+++ b/src/db/models/device.rs
@@ -48,7 +48,7 @@ impl Device {
         use crate::crypto;
         use data_encoding::BASE64;
 
-        let twofactor_remember = BASE64.encode(&crypto::get_random(vec![0u8; 180]));
+        let twofactor_remember = crypto::encode_random_bytes::<180>(BASE64);
         self.twofactor_remember = Some(twofactor_remember.clone());
 
         twofactor_remember
@@ -69,7 +69,7 @@ impl Device {
             use crate::crypto;
             use data_encoding::BASE64URL;
 
-            self.refresh_token = BASE64URL.encode(&crypto::get_random_64());
+            self.refresh_token = crypto::encode_random_bytes::<64>(BASE64URL);
         }
 
         // Update the expiration of the device and the last update date