単回使用の値
ときには、_一度しか使えない_値が欲しいことがあります。その重要な例の 1 つが、暗号分野における「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 を消費しますが、key や 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(/* 詳細は省略 */); // key と data は再利用したり、コピーしたりできますが、nonce はできません。 encrypt(nonce, &key, &data_1); // encrypt(nonce, &key, &data_2); // 🛠️❌ }
-
問題: 値が一度しか使われないことを、どうすれば保証できるでしょうか?
-
動機: nonce は、リプレイ攻撃を防ぐために暗号プロトコルで使用される、ランダムで一意なデータです。
背景: 実際には、誤って nonce を再利用してしまった事例があります。 最も一般的には、その結果として暗号プロトコルが完全に破綻し、 本来の役割を果たせなくなります。
nonce の再利用のされ方や対象の暗号方式によっては、秘密鍵まで 攻撃者に計算されてしまうこともあります。
-
Rust には、「これを一度使ったら、もう二度と使えない」という不変条件を 実現するための分かりやすい手段があります。値を 所有権を受け取る引数 として渡すことです。
-
注目点:
encrypt関数はnonceを値渡し(所有権を受け取る引数)で 受け取りますが、keyとdataは参照で受け取ります。 -
単回使用の値に対するテクニックは次のとおりです。
-
コンストラクターを非公開にして、同じ内部値を持つ値をユーザーが 2 回 構築できないようにする。
-
一意に保ちたいデータをユーザーが複製できないように、
Clone/Copyトレイトや同等のメソッドを実装しない。 -
内部の型を不透明にし(newtype パターンのように)、ユーザーが既存の値を 自分で変更できないようにする。
-
-
問い: スライドのコードにある newtype パターンには、何が欠けているでしょうか?
期待する答え: モジュール境界。
実演: モジュール境界がないと、ユーザーは nonce を自分で構築できてしまいます。
修正:
Key、Nonce、new_nonceをモジュールの内側に置きます。
さらに探る
- 暗号における注意点: nonce は、実際のランダム性がない疑似乱数過程で 生成された場合、依然として 2 回使われる可能性があります。これはこの方法では 防げません。この API 設計は 1 つの nonce を複製することは防ぎますが、 すべてのロジックバグを防ぐわけではありません。