Single-use values
Sometimes we want values that can only be used once. One critical example of this is in cryptography: A āNonce.ā
// Copyright 2025 Google LLC // SPDX-License-Identifier: Apache-2.0 pub struct Key(/* specifics omitted */); /// A single-use number suitable for cryptographic purposes. pub struct Nonce(u32); /// A cryptographically sound random generator function. pub fn new_nonce() -> Nonce { Nonce(4) // chosen by a fair dice roll, https://xkcd.com/221/ } /// Consume a nonce, but not the key or the data. pub fn encrypt(nonce: Nonce, key: &Key, data: &[u8]) {} fn main() { let nonce = new_nonce(); let data_1: [u8; 4] = [1, 2, 3, 4]; let data_2: [u8; 4] = [4, 3, 2, 1]; let key = Key(/* specifics omitted */); // The key and data can be re-used, copied, etc. but the nonce cannot. encrypt(nonce, &key, &data_1); // encrypt(nonce, &key, &data_2); // š ļøā }
-
Problem: How can we guarantee a value is used only once?
-
Motivation: A nonce is a piece of random, unique data used in cryptographic protocols to prevent replay attacks.
Background: In practice people have ended up accidentally re-using nonces. Most commonly, this causes the cryptographic protocol to completely break down and stop fulfilling its function.
Depending on the specifics of nonce reuse and cryptography at hand, private keys can also become computable by attackers.
-
Rust has an obvious tool for achieving the invariant āOnce you use this, you canāt use it againā: passing a value as an owned argument.
-
Highlight: the
encryptfunction takesnonceby value (an owned argument), butkeyanddataby reference. -
The technique for single-use values is as follows:
-
Keep constructors private, so a user canāt construct values with the same inner value twice.
-
Donāt implement
Clone/Copytraits or equivalent methods, so a user canāt duplicate data we want to keep unique. -
Make the interior type opaque (like with the newtype pattern), so the user cannot modify an existing value on their own.
-
-
Ask: What are we missing from the newtype pattern in the slideās code?
Expect: Module boundary.
Demonstrate: Without a module boundary a user can construct a nonce on their own.
Fix: Put
Key,Nonce, andnew_noncebehind a module.
More to Explore
- Cryptography Nuance: A nonce might still be used twice if it was created through pseudo-random process with no actual randomness. That canāt be prevented through this method. This API design prevents one nonce duplication, but not all logic bugs.