Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

frodo-kem: `Algorithm::encryption_key_from_decryption_key` skips both of the validations its siblings perform

オープン
#381 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@cipherprofessor がすでに取り組んでいます。

2026年9月21日 から。

  • #384 @cipherprofessor による — オープン

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
58/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
rust

調査の方向性

frodo-kem/src/lib.rs の inner_encryption_key_from_decryption_key から始め、inner_encapsulate_with_rng および inner_decapsulate と比較します。frodo-kem/src/hazmat/models.rs の DecryptionKeyRef::from_slice と public_key を調べます。アルゴリズムが一致しない場合や無効な鍵長の場合に、鍵を生成したり panic を発生させたりせずエラーを返し、影響を受けない From 実装との一貫性が保たれていれば完了です。

索引モデルが issue の本文から書いたものです。

説明

Present in published 0.1.0 (src/lib.rs:995) and unchanged on master at 1d535a11 (src/lib.rs:1031, crate version 0.2.0, unreleased). Line numbers below are from master.

Raising it now partly because the fix wants a breaking signature change: 0.2.0 has not shipped, so if that signature is going to move, this is the cheapest moment for it to move.

CONFORMANCE.md lines 52-53 state, under Algorithm review:

Dynamic API boundaries reject keys and ciphertexts tagged for another parameter set and reject invalid message, salt, key, and ciphertext lengths.

This method does neither. It has two visible faces, and one fix closes both.

The code

frodo-kem/src/lib.rs:1027-1036:

fn inner_encryption_key_from_decryption_key<B: Params>(
    &self,
    secret_key: &DecryptionKey,
) -> EncryptionKey {
    let sk = DecryptionKeyRef::<B>(secret_key.value.as_slice(), PhantomData);
    EncryptionKey {
        algorithm: *self,
        value: sk.public_key().to_vec(),
    }
}

Compared with inner_encapsulate_with_rng, eleven lines further down at frodo-kem/src/lib.rs:1558-1565:

fn inner_encapsulate_with_rng<K: Kem, R: CryptoRng + ?Sized>(
    &self,
    encryption_key: &EncryptionKey,
    rng: &mut R,
) -> FrodoResult<(Ciphertext, SharedSecret)> {
    if encryption_key.algorithm != *self {
        return Err(Error::AlgorithmMismatch);
    }
    let pk = EncryptionKeyRef::from_slice(encryption_key.value.as_slice())?;

The sibling checks the tag and goes through the length-checked from_slice. The method above constructs DecryptionKeyRef::<B> by direct tuple construction, which bypasses from_slice entirely, and never compares secret_key.algorithm to *self. inner_decapsulate (frodo-kem/src/lib.rs:1643) checks both tags too.

Face 1 — panic

DecryptionKeyRef::public_key (frodo-kem/src/hazmat/models.rs:454-455) slices using the receiving parameter set's lengths over the supplied key's bytes:

pub fn public_key(&self) -> &[u8] {
    &self.0[P::SHARED_SECRET_LENGTH..P::SHARED_SECRET_LENGTH + P::PUBLIC_KEY_LENGTH]
}

When the receiving set's PUBLIC_KEY_LENGTH exceeds the supplied key's length, that range is out of bounds. A correctly-tagged 19888-byte FrodoKEM-640-AES decryption key handed to any of the four 1344 algorithms panics with range end index 21552 out of range for slice of length 19888 — measured on 96 of 96 right-length buffers at all four 1344 parameter sets.

let dk = Algorithm::FrodoKem640Aes.decryption_key_from_bytes(&bytes_19888)?;
let _ = Algorithm::FrodoKem1344Shake.encryption_key_from_decryption_key(&dk); // panics

Face 2 — silent wrong answer

When the supplied key is long enough for the receiving set's public-key window, there is no panic. The method returns a well-formed, correct-length EncryptionKey tagged with the receiving algorithm, built from bytes that are not that algorithm's public key. Ciphertexts produced under it decapsulate to nothing anyone can use.

The signature is -> EncryptionKey, not -> FrodoResult<EncryptionKey>, so the method has no way to report the mismatch even if it detected one.

Suggested fix

Matching the siblings:

if secret_key.algorithm != *self {
    return Err(Error::AlgorithmMismatch);
}
let sk = DecryptionKeyRef::<B>::from_slice(secret_key.value.as_slice())?;

That needs the public method's return type to become FrodoResult<EncryptionKey>, which is a breaking change — hence raising it as an issue rather than sending a patch, since the shape of the fix is your call.

The infallible impl From<&DecryptionKey> for EncryptionKey (frodo-kem/src/lib.rs:311-317) is unaffected: it reads secret_key.algorithm, so it is always self-consistent and cannot reach either face.

On reachability, stated carefully

Neither face is reachable through the idiomatic From path. Both require an application that pairs a DecryptionKey obtained from one source with an Algorithm value obtained from another.

That shape is plausible rather than observed: the serde impl (frodo-kem/src/lib.rs:130-242, applied at frodo-kem/src/lib.rs:386) puts the algorithm tag on the wire as the first byte of the binary form, and the deserialiser routes on it, so an application that deserialises a DecryptionKey and then calls the method with its own configured Algorithm would be crashable by whoever supplied those bytes. I did not find such an application; I am describing an API shape that permits one.

Worth noting separately: 19888 all-zero bytes are accepted as a FrodoKEM-640-AES decryption key, so no genuine key material is needed to reach the panic.

This is an API-contract and availability issue, not a vulnerability — no key recovery, no plaintext recovery, and the panic is a Rust bounds check doing its job rather than an out-of-bounds read. I am not filing it as a security advisory.

Method

Found with our own PQC test harness, and this issue was drafted with AI assistance — both stated up front as a matter of policy. Every claim above was reproduced by execution, then re-checked independently by three reviewers who did not share harness code. Two of the three tempered the original reachability wording, and this text reflects the tempered version.

主要言語
Rust
スター
118
フォーク
58
平均マージ
5日 2時間
マージ済み PR(30日)
4

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

RustCrypto/KEMs のほかの issue

RustCrypto/KEMs の issue をすべて見る

似ている issue

Rust の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。