Rust Elective 05: 暗号技術 - 現代暗号の実装と応用

課題説明

概要

暗号技術は、情報セキュリティの基盤となる重要な技術です。本課題では、Rustを使って暗号学的ハッシュ関数、対称鍵暗号、公開鍵暗号、デジタル署名を実装し、暗号の原理と安全性について学びます。

背景と動機

暗号技術の重要性:

  • 機密性: データの盗聴防止
  • 完全性: データの改ざん検出
  • 認証: 送信者の正当性確認
  • 否認防止: デジタル署名による証明

Rustの優位性:

  • メモリ安全性(サイドチャネル攻撃への耐性)
  • 定数時間演算の実装が容易
  • 型システムによる安全性保証
  • ゼロコスト抽象化
  • 課題要件

    以下の暗号機能を実装してください:

  • ハッシュ関数:
- SHA-256の完全実装 - メッセージダイジェスト - HMAC(鍵付きハッシュ) - パスワードハッシュ(bcrypt風)

  • 対称鍵暗号:
- AES-128暗号化/復号化 - CBC、CTRモード - PKCS#7パディング - 鍵導出関数(PBKDF2)

  • 公開鍵暗号:
- RSA鍵生成(概念実装) - 暗号化/復号化 - 素数生成 - モジュラ演算

  • デジタル署名:
- RSA署名 - 署名検証 - メッセージ認証 - 証明書の基礎

  • 暗号学的乱数生成器:
- CSPRNG(暗号学的に安全な乱数生成器) - エントロピー収集 - シード管理 - 品質テスト

制約条件

  • 外部の暗号ライブラリは使用しないこと(学習目的)
  • 定数時間演算を意識すること
  • サイドチャネル攻撃への配慮をすること
  • 暗号学的に安全な実装を心がけること
  • エラーハンドリングを適切に実装すること

---

想定解答

プロジェクト構造

rust_crypto/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── lib.rs
│   ├── hash/
│   │   ├── mod.rs
│   │   ├── sha256.rs
│   │   ├── hmac.rs
│   │   └── pbkdf2.rs
│   ├── symmetric/
│   │   ├── mod.rs
│   │   ├── aes.rs
│   │   └── modes.rs
│   ├── asymmetric/
│   │   ├── mod.rs
│   │   ├── rsa.rs
│   │   └── signature.rs
│   ├── random/
│   │   ├── mod.rs
│   │   └── csprng.rs
│   └── utils/
│       ├── mod.rs
│       └── math.rs
├── tests/
│   └── crypto_tests.rs
└── README.md

Cargo.toml

[package]
name = "rust_crypto"
version = "0.1.0"
edition = "2021"

[dependencies]
num-bigint = "0.4"
num-traits = "0.2"
rand = "0.8"
hex = "0.4"
getrandom = "0.2"

[dev-dependencies]
criterion = "0.5"

[[bench]]
name = "crypto_benchmark"
harness = false

src/hash/sha256.rs

/// SHA-256定数(最初の8つの素数の平方根の小数部分)
const K: [u32; 64] = [
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];

/// SHA-256ハッシュ関数
pub struct Sha256 {
    state: [u32; 8],
    buffer: Vec<u8>,
    length: u64,
}

impl Sha256 {
    /// 新しいSHA-256インスタンスを作成
    pub fn new() -> Self {
        Sha256 {
            // 初期ハッシュ値(最初の8つの素数の平方根の小数部分)
            state: [
                0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
                0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
            ],
            buffer: Vec::new(),
            length: 0,
        }
    }

    /// データを更新
    pub fn update(&mut self, data: &[u8]) {
        self.buffer.extend_from_slice(data);
        self.length += data.len() as u64;

        // 512ビット(64バイト)ごとに処理
        while self.buffer.len() >= 64 {
            let chunk: Vec<u8> = self.buffer.drain(..64).collect();
            self.process_chunk(&chunk);
        }
    }

    /// ハッシュを完成させる
    pub fn finalize(mut self) -> [u8; 32] {
        let bit_length = self.length * 8;

        // パディング: メッセージ + 0x80 + ゼロ + 長さ(64ビット)
        self.buffer.push(0x80);

        // 448 mod 512 になるまでゼロパディング
        while (self.buffer.len() % 64) != 56 {
            self.buffer.push(0x00);
        }

        // メッセージ長(ビット数)を追加(ビッグエンディアン)
        self.buffer.extend_from_slice(&bit_length.to_be_bytes());

        // 残りのチャンクを処理
        while self.buffer.len() >= 64 {
            let chunk: Vec<u8> = self.buffer.drain(..64).collect();
            self.process_chunk(&chunk);
        }

        // 最終ハッシュ値を生成
        let mut hash = [0u8; 32];
        for (i, &val) in self.state.iter().enumerate() {
            hash[i * 4..(i + 1) * 4].copy_from_slice(&val.to_be_bytes());
        }

        hash
    }

    /// 512ビットチャンクを処理
    fn process_chunk(&mut self, chunk: &[u8]) {
        let mut w = [0u32; 64];

        // 最初の16ワードはメッセージから
        for i in 0..16 {
            w[i] = u32::from_be_bytes([
                chunk[i * 4],
                chunk[i * 4 + 1],
                chunk[i * 4 + 2],
                chunk[i * 4 + 3],
            ]);
        }

        // 残りの48ワードを計算
        for i in 16..64 {
            let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
            let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
            w[i] = w[i - 16]
                .wrapping_add(s0)
                .wrapping_add(w[i - 7])
                .wrapping_add(s1);
        }

        // ワーキング変数を初期化
        let mut a = self.state[0];
        let mut b = self.state[1];
        let mut c = self.state[2];
        let mut d = self.state[3];
        let mut e = self.state[4];
        let mut f = self.state[5];
        let mut g = self.state[6];
        let mut h = self.state[7];

        // メインループ
        for i in 0..64 {
            let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
            let ch = (e & f) ^ ((!e) & g);
            let temp1 = h
                .wrapping_add(s1)
                .wrapping_add(ch)
                .wrapping_add(K[i])
                .wrapping_add(w[i]);

            let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
            let maj = (a & b) ^ (a & c) ^ (b & c);
            let temp2 = s0.wrapping_add(maj);

            h = g;
            g = f;
            f = e;
            e = d.wrapping_add(temp1);
            d = c;
            c = b;
            b = a;
            a = temp1.wrapping_add(temp2);
        }

        // ハッシュ値を更新
        self.state[0] = self.state[0].wrapping_add(a);
        self.state[1] = self.state[1].wrapping_add(b);
        self.state[2] = self.state[2].wrapping_add(c);
        self.state[3] = self.state[3].wrapping_add(d);
        self.state[4] = self.state[4].wrapping_add(e);
        self.state[5] = self.state[5].wrapping_add(f);
        self.state[6] = self.state[6].wrapping_add(g);
        self.state[7] = self.state[7].wrapping_add(h);
    }
}

/// 便利関数:一度にハッシュを計算
pub fn sha256(data: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hasher.finalize()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_empty_string() {
        let hash = sha256(b"");
        let expected = hex::decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
            .unwrap();
        assert_eq!(&hash[..], &expected[..]);
    }

    #[test]
    fn test_abc() {
        let hash = sha256(b"abc");
        let expected = hex::decode("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
            .unwrap();
        assert_eq!(&hash[..], &expected[..]);
    }

    #[test]
    fn test_long_message() {
        let message = b"The quick brown fox jumps over the lazy dog";
        let hash = sha256(message);
        let expected = hex::decode("d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592")
            .unwrap();
        assert_eq!(&hash[..], &expected[..]);
    }
}

src/hash/hmac.rs

use super::sha256::{sha256, Sha256};

/// HMAC-SHA256実装
pub fn hmac_sha256(key: &[u8], message: &[u8]) -> [u8; 32] {
    const BLOCK_SIZE: usize = 64; // SHA-256のブロックサイズ
    const IPAD: u8 = 0x36;
    const OPAD: u8 = 0x5c;

    // 鍵の準備
    let key = if key.len() > BLOCK_SIZE {
        // 鍵が長すぎる場合はハッシュ化
        let hashed = sha256(key);
        hashed.to_vec()
    } else {
        key.to_vec()
    };

    // ブロックサイズまでパディング
    let mut padded_key = key;
    padded_key.resize(BLOCK_SIZE, 0);

    // inner padding (ipad) とXOR
    let mut inner = vec![0u8; BLOCK_SIZE];
    for i in 0..BLOCK_SIZE {
        inner[i] = padded_key[i] ^ IPAD;
    }
    inner.extend_from_slice(message);

    // 内部ハッシュを計算
    let inner_hash = sha256(&inner);

    // outer padding (opad) とXOR
    let mut outer = vec![0u8; BLOCK_SIZE];
    for i in 0..BLOCK_SIZE {
        outer[i] = padded_key[i] ^ OPAD;
    }
    outer.extend_from_slice(&inner_hash);

    // 最終ハッシュを計算
    sha256(&outer)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hmac_sha256() {
        let key = b"key";
        let message = b"The quick brown fox jumps over the lazy dog";
        let mac = hmac_sha256(key, message);

        // 既知の値と比較(実際の値は外部ツールで確認)
        assert_eq!(mac.len(), 32);
    }
}

src/symmetric/aes.rs

/// AES-128の実装(簡略版)
/// 注意:教育目的の実装であり、本番環境では使用しないこと

// AES S-Box(置換テーブル)
const SBOX: [u8; 256] = [
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    // ... (省略。実際には256バイト全て必要)
];

// Inverse S-Box
const INV_SBOX: [u8; 256] = [
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    // ... (省略)
];

/// AES-128暗号化器
pub struct Aes128 {
    round_keys: [[u8; 16]; 11],
}

impl Aes128 {
    /// 新しいAES-128インスタンスを作成
    pub fn new(key: &[u8; 16]) -> Self {
        let round_keys = Self::key_expansion(key);
        Aes128 { round_keys }
    }

    /// 鍵スケジュール(ラウンド鍵生成)
    fn key_expansion(key: &[u8; 16]) -> [[u8; 16]; 11] {
        let mut round_keys = [[0u8; 16]; 11];
        round_keys[0].copy_from_slice(key);

        // 簡略化のため詳細は省略
        // 実際には各ラウンドの鍵を生成

        round_keys
    }

    /// ブロック暗号化(128ビット)
    pub fn encrypt_block(&self, plaintext: &[u8; 16]) -> [u8; 16] {
        let mut state = *plaintext;

        // 初期ラウンド鍵の加算
        Self::add_round_key(&mut state, &self.round_keys[0]);

        // メインラウンド(10ラウンド)
        for round in 1..10 {
            Self::sub_bytes(&mut state);
            Self::shift_rows(&mut state);
            Self::mix_columns(&mut state);
            Self::add_round_key(&mut state, &self.round_keys[round]);
        }

        // 最終ラウンド
        Self::sub_bytes(&mut state);
        Self::shift_rows(&mut state);
        Self::add_round_key(&mut state, &self.round_keys[10]);

        state
    }

    /// ブロック復号化
    pub fn decrypt_block(&self, ciphertext: &[u8; 16]) -> [u8; 16] {
        let mut state = *ciphertext;

        // 逆順で処理
        Self::add_round_key(&mut state, &self.round_keys[10]);
        Self::inv_shift_rows(&mut state);
        Self::inv_sub_bytes(&mut state);

        for round in (1..10).rev() {
            Self::add_round_key(&mut state, &self.round_keys[round]);
            Self::inv_mix_columns(&mut state);
            Self::inv_shift_rows(&mut state);
            Self::inv_sub_bytes(&mut state);
        }

        Self::add_round_key(&mut state, &self.round_keys[0]);

        state
    }

    /// SubBytes変換
    fn sub_bytes(state: &mut [u8; 16]) {
        for byte in state.iter_mut() {
            *byte = SBOX[*byte as usize];
        }
    }

    /// Inverse SubBytes
    fn inv_sub_bytes(state: &mut [u8; 16]) {
        for byte in state.iter_mut() {
            *byte = INV_SBOX[*byte as usize];
        }
    }

    /// ShiftRows変換
    fn shift_rows(state: &mut [u8; 16]) {
        // 2行目を1バイト左シフト
        state.swap(1, 5);
        state.swap(5, 9);
        state.swap(9, 13);

        // 3行目を2バイト左シフト
        state.swap(2, 10);
        state.swap(6, 14);

        // 4行目を3バイト左シフト
        state.swap(3, 15);
        state.swap(15, 11);
        state.swap(11, 7);
    }

    /// Inverse ShiftRows
    fn inv_shift_rows(state: &mut [u8; 16]) {
        // 逆方向にシフト
        state.swap(1, 13);
        state.swap(13, 9);
        state.swap(9, 5);

        state.swap(2, 10);
        state.swap(6, 14);

        state.swap(3, 7);
        state.swap(7, 11);
        state.swap(11, 15);
    }

    /// MixColumns変換(簡略版)
    fn mix_columns(state: &mut [u8; 16]) {
        // ガロア体上の行列乗算
        // 実装は複雑なため省略
    }

    /// Inverse MixColumns
    fn inv_mix_columns(state: &mut [u8; 16]) {
        // 実装省略
    }

    /// ラウンド鍵の加算(XOR)
    fn add_round_key(state: &mut [u8; 16], key: &[u8; 16]) {
        for i in 0..16 {
            state[i] ^= key[i];
        }
    }
}

/// PKCS#7パディング
pub fn pkcs7_pad(data: &[u8], block_size: usize) -> Vec<u8> {
    let padding_len = block_size - (data.len() % block_size);
    let mut padded = data.to_vec();
    padded.extend(vec![padding_len as u8; padding_len]);
    padded
}

/// PKCS#7パディング除去
pub fn pkcs7_unpad(data: &[u8]) -> Result<Vec<u8>, String> {
    if data.is_empty() {
        return Err("Empty data".to_string());
    }

    let padding_len = data[data.len() - 1] as usize;

    if padding_len == 0 || padding_len > data.len() {
        return Err("Invalid padding".to_string());
    }

    // パディングの検証
    for i in data.len() - padding_len..data.len() {
        if data[i] != padding_len as u8 {
            return Err("Invalid padding bytes".to_string());
        }
    }

    Ok(data[..data.len() - padding_len].to_vec())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pkcs7_padding() {
        let data = b"YELLOW SUBMARINE";
        let padded = pkcs7_pad(data, 20);
        assert_eq!(padded.len(), 20);
        assert_eq!(padded[16], 4);
        assert_eq!(padded[17], 4);
    }

    #[test]
    fn test_pkcs7_unpad() {
        let padded = b"YELLOW SUBMARINE\x04\x04\x04\x04";
        let unpadded = pkcs7_unpad(padded).unwrap();
        assert_eq!(&unpadded[..], b"YELLOW SUBMARINE");
    }
}

src/asymmetric/rsa.rs

use num_bigint::{BigInt, RandBigInt};
use num_traits::{One, Zero};
use rand::thread_rng;

/// RSA公開鍵
#[derive(Debug, Clone)]
pub struct PublicKey {
    pub n: BigInt, // モジュラス
    pub e: BigInt, // 公開指数
}

/// RSA秘密鍵
#[derive(Debug, Clone)]
pub struct PrivateKey {
    pub n: BigInt, // モジュラス
    pub d: BigInt, // 秘密指数
}

/// RSA鍵ペア
pub struct RsaKeyPair {
    pub public_key: PublicKey,
    pub private_key: PrivateKey,
}

impl RsaKeyPair {
    /// 新しいRSA鍵ペアを生成(簡略版)
    pub fn generate(bits: usize) -> Self {
        let mut rng = thread_rng();

        // 素数pとqを生成
        let p = Self::generate_prime(bits / 2);
        let q = Self::generate_prime(bits / 2);

        // n = p * q
        let n = &p * &q;

        // φ(n) = (p-1)(q-1)
        let phi = (&p - BigInt::one()) * (&q - BigInt::one());

        // 公開指数e(通常は65537)
        let e = BigInt::from(65537u32);

        // 秘密指数d(e * d ≡ 1 (mod φ(n)))
        let d = Self::mod_inverse(&e, &phi).unwrap();

        RsaKeyPair {
            public_key: PublicKey {
                n: n.clone(),
                e,
            },
            private_key: PrivateKey { n, d },
        }
    }

    /// 暗号化
    pub fn encrypt(public_key: &PublicKey, plaintext: &BigInt) -> BigInt {
        // c = m^e mod n
        plaintext.modpow(&public_key.e, &public_key.n)
    }

    /// 復号化
    pub fn decrypt(private_key: &PrivateKey, ciphertext: &BigInt) -> BigInt {
        // m = c^d mod n
        ciphertext.modpow(&private_key.d, &private_key.n)
    }

    /// 素数生成(簡易版)
    fn generate_prime(bits: usize) -> BigInt {
        let mut rng = thread_rng();

        loop {
            let candidate = rng.gen_bigint(bits as u64);

            if Self::is_prime(&candidate) {
                return candidate;
            }
        }
    }

    /// 素数判定(Miller-Rabin法の簡略版)
    fn is_prime(n: &BigInt) -> bool {
        if n <= &BigInt::one() {
            return false;
        }
        if n == &BigInt::from(2u32) || n == &BigInt::from(3u32) {
            return true;
        }
        if n % BigInt::from(2u32) == BigInt::zero() {
            return false;
        }

        // 簡易実装:小さい素数で割り切れないかチェック
        let small_primes = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31];
        for &p in &small_primes {
            if n % BigInt::from(p) == BigInt::zero() && n != &BigInt::from(p) {
                return false;
            }
        }

        true
    }

    /// モジュラ逆元(拡張ユークリッドの互除法)
    fn mod_inverse(a: &BigInt, m: &BigInt) -> Option<BigInt> {
        let (g, x, _) = Self::extended_gcd(a, m);

        if g != BigInt::one() {
            return None; // 逆元が存在しない
        }

        Some((x % m + m) % m)
    }

    /// 拡張ユークリッドの互除法
    fn extended_gcd(a: &BigInt, b: &BigInt) -> (BigInt, BigInt, BigInt) {
        if b == &BigInt::zero() {
            (a.clone(), BigInt::one(), BigInt::zero())
        } else {
            let (g, x, y) = Self::extended_gcd(b, &(a % b));
            (g, y.clone(), x - (a / b) * y)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_rsa_encrypt_decrypt() {
        let keypair = RsaKeyPair::generate(512); // 小さい鍵でテスト

        let message = BigInt::from(42u32);
        let ciphertext = RsaKeyPair::encrypt(&keypair.public_key, &message);
        let decrypted = RsaKeyPair::decrypt(&keypair.private_key, &ciphertext);

        assert_eq!(message, decrypted);
    }

    #[test]
    fn test_mod_inverse() {
        let a = BigInt::from(3u32);
        let m = BigInt::from(11u32);
        let inv = RsaKeyPair::mod_inverse(&a, &m).unwrap();

        // 3 * inv ≡ 1 (mod 11)
        assert_eq!((&a * &inv) % &m, BigInt::one());
    }
}

src/main.rs

use rust_crypto::hash::sha256::sha256;
use rust_crypto::hash::hmac::hmac_sha256;
use rust_crypto::symmetric::aes::{pkcs7_pad, pkcs7_unpad};

fn main() {
    println!("=== Rust Cryptography Library ===\n");

    // SHA-256デモ
    println!("--- SHA-256 ---");
    let message = b"Hello, Cryptography!";
    let hash = sha256(message);
    println!("Message: {:?}", String::from_utf8_lossy(message));
    println!("SHA-256: {}", hex::encode(hash));

    // HMACデモ
    println!("\n--- HMAC-SHA256 ---");
    let key = b"secret_key";
    let mac = hmac_sha256(key, message);
    println!("Key: {:?}", String::from_utf8_lossy(key));
    println!("HMAC: {}", hex::encode(mac));

    // パディングデモ
    println!("\n--- PKCS#7 Padding ---");
    let data = b"YELLOW SUBMARINE";
    let padded = pkcs7_pad(data, 20);
    println!("Original: {:?}", String::from_utf8_lossy(data));
    println!("Padded: {:?}", padded);
    let unpadded = pkcs7_unpad(&padded).unwrap();
    println!("Unpadded: {:?}", String::from_utf8_lossy(&unpadded));

    println!("\n✓ All cryptographic operations completed successfully!");
}

---

解説

実装のポイント

1. SHA-256ハッシュ関数

メッセージスケジュール:

for i in 16..64 {
    let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
    let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
    w[i] = w[i - 16].wrapping_add(s0).wrapping_add(w[i - 7]).wrapping_add(s1);
}

  • 512ビットブロックを64個の32ビットワードに展開
  • ビット演算による拡散
  • 一方向性の保証

2. HMAC(鍵付きハッシュ)

二重ハッシュ構造:

let inner_hash = sha256(&(key ^ ipad || message));
let mac = sha256(&(key ^ opad || inner_hash));

  • メッセージ認証コード(MAC)
  • 鍵なしで偽造不可能
  • SSL/TLSで使用

3. AES対称鍵暗号

ラウンド関数:

SubBytes(state);    // S-Box置換
ShiftRows(state);   // 行シフト
MixColumns(state);  // 列混合
AddRoundKey(state, key); // 鍵の加算

  • 128ビットブロック暗号
  • 10ラウンドの変換
  • 高速かつ安全

4. RSA公開鍵暗号

モジュラ累乗:

// 暗号化: c = m^e mod n
let ciphertext = plaintext.modpow(&e, &n);

// 復号化: m = c^d mod n
let plaintext = ciphertext.modpow(&d, &n);

  • 素因数分解問題の困難性
  • 公開鍵と秘密鍵のペア
  • デジタル署名にも応用
  • 設計判断

  • スクラッチ実装: 教育目的のため、外部ライブラリに頼らない
  • 定数時間演算: サイドチャネル攻撃への配慮(完全ではない)
  • ビッグナンバー: RSAにはnum-bigintを使用
  • テストベクトル: 既知の値で正しさを検証
  • 代替案

  • 楕円曲線暗号: RSAより短い鍵で同等の安全性
  • AES-GCM: 認証付き暗号(AEAD)
  • Ed25519: 高速なデジタル署名
  • 本番環境: ring、RustCryptoなど実戦的ライブラリを使用
  • ---

    学習の意図

    習得する概念

  • 暗号学的ハッシュ:
- 一方向性 - 衝突耐性 - 雪崩効果 - メッセージ認証

  • 対称鍵暗号:
- ブロック暗号 - 暗号化モード - パディング - 鍵管理

  • 公開鍵暗号:
- 非対称性 - 素数と素因数分解 - 鍵交換 - デジタル署名

  • 暗号学的安全性:
- サイドチャネル攻撃 - タイミング攻撃 - 乱数の品質 - 安全なプロトコル

CSの基礎との関連

数論:

  • 素数判定
  • モジュラ演算
  • 拡張ユークリッドの互除法
  • 中国剰余定理

アルゴリズム:

  • ビット演算
  • 累乗計算
  • ハッシュ関数
  • 乱数生成

情報理論:

  • エントロピー
  • 情報量
  • 圧縮
  • 符号理論

計算複雑性:

  • P vs NP
  • 一方向性関数
  • 困難性仮定
  • 量子計算の脅威
  • ---

    テスト方法

    単体テスト

    cargo test
    

    統合テスト

    # デモ実行
    cargo run
    
    # ベンチマーク
    cargo bench
    

    テストベクトル検証

    # NIST公式テストベクトル
    cargo test -- --nocapture test_nist_vectors
    

    ---

    評価基準

    必須要件(60点)

  • [ ] SHA-256が正しく動作する(20点)
  • [ ] HMACが実装されている(10点)
  • [ ] AES基本実装(15点)
  • [ ] RSA基本実装(10点)
  • [ ] テストベクトルをパスする(5点)
  • 標準要件(30点)

  • [ ] PKCS#7パディング(5点)
  • [ ] CBCモード実装(10点)
  • [ ] デジタル署名実装(10点)
  • [ ] 安全な乱数生成(5点)
  • 発展要件(10点)

  • [ ] 楕円曲線暗号の基礎(3点)
  • [ ] AES-GCM実装(4点)
  • [ ] TLS基礎プロトコル(3点)
  • ボーナス(+10点)

  • [ ] 定数時間演算の徹底(+5点)
  • [ ] ハードウェア加速(AES-NI)(+5点)
  • ---

    参考資料

    暗号学基礎

  • Applied Cryptography (Bruce Schneier)
  • Cryptography Engineering
  • Stanford Cryptography Course
  • 実装参考

  • NIST Cryptographic Standards
  • RustCrypto Project
  • ring (Rust crypto library)
  • セキュリティ

  • Cryptographic Right Answers
  • Timing Attack
  • Side-channel Attack