Одноразовые значения
Иногда нужны значения, которые можно использовать только один раз. Один из важнейших примеров — криптография: «nonce» (одноразовое число).
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
pub struct Key(/* детали опущены */);
/// Одноразовое число, пригодное для криптографических целей.
pub struct Nonce(u32);
/// Криптографически стойкая функция генерации случайных чисел.
pub fn new_nonce() -> Nonce {
Nonce(4) // выбрано честным броском кубика, https://xkcd.com/221/
}
/// Потребляет nonce, но не ключ и не данные.
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(/* детали опущены */);
// Ключ и данные можно повторно использовать, копировать и т. д., а nonce — нельзя.
encrypt(nonce, &key, &data_1);
// encrypt(nonce, &key, &data_2); // 🛠️❌
}
-
Проблема: как гарантировать, что значение будет использовано только один раз?
-
Мотивация: nonce — это случайные уникальные данные, которые используются в криптографических протоколах для защиты от атак повторного воспроизведения.
Контекст: на практике люди случайно повторно использовали nonce. Чаще всего это приводит к полному краху криптографического протокола, и он перестаёт выполнять свою функцию.
В зависимости от деталей повторного использования nonce и конкретной криптографии злоумышленники могут также вычислить закрытые ключи.
-
У Rust есть очевидный инструмент для обеспечения инварианта «после использования повторно воспользоваться нельзя»: передача значения как аргумента, переданного по значению.
-
Обратите внимание: функция
encryptпринимаетnonceпо значению, аkeyиdata— по ссылке. -
Техника для значений одноразового использования такова:
-
Делайте конструкторы приватными, чтобы пользователь не мог создать два значения с одним и тем же внутренним значением.
-
Не реализуйте трейты
Clone/Copyили эквивалентные методы, чтобы пользователь не мог дублировать данные, которые мы хотим сохранить уникальными. -
Сделайте внутренний тип непрозрачным (как в паттерне newtype), чтобы пользователь не мог самостоятельно изменить существующее значение.
-
-
Спросите: чего не хватает в коде слайда для полноценного паттерна newtype?
Ожидаемый ответ: граница модуля.
Продемонстрируйте: без границы модуля пользователь может самостоятельно создать nonce.
Исправление: поместите
Key,Nonceиnew_nonceвнутрь модуля.
Для дальнейшего изучения
- Нюанс криптографии: nonce всё же может быть использован дважды, если он был создан псевдослучайным процессом без настоящей случайности. Этот метод такое предотвратить не может. Данный дизайн API предотвращает дублирование nonce, но не все логические ошибки.