コンテンツにスキップ

AES 暗号のひみつ — AES-256 の内部構造(混乱と拡散)と AES-NI によるハードウェア高速化(読書ノート)

作成日: 2026-06-29 出典: Software Design 2026年5月号 連載「暗号のひみつ」第5回「AES 暗号のひみつ」(荒木誠/@tex2e) 一次資料での裏取り: NIST FIPS 197 Advanced Encryption Standard (AES)(2001年標準化)/Intel "Advanced Encryption Standard (AES) Instructions Set" Rev 3.01 関連: tls13_aead暗号化_鍵スケジュールとノンス管理_整理(同連載 第7回・2026年7月号)、[[aead_aes-gcm_認証付き暗号_整理]](同連載 第6回・2026年6月号「暗号利用モード」=本記事の次回予告。未作成)


0. 要点(3行)

  • AES は 128 ビット(16 バイト)の「ブロック暗号」。データを 4×4 の状態行列 State に並べ、混乱(Confusion)= SubBytes拡散(Diffusion)= ShiftRows / MixColumns を、鍵長に応じた回数(AES-256 は 14 ラウンド)繰り返して攪拌する。
  • 共通鍵暗号なので量子時代でも鍵長を伸ばすだけで安全。AES-256 は Grover のアルゴリズムでも実質 2^128 探索が必要で、1 兆回/秒でも約 10^19 年かかる(対して RSA など公開鍵は将来解読リスクあり)。
  • MixColumns の行列演算は重いので、Intel/AMD は専用命令 AES-NI を実装。CPUID の 25 ビット目で対応を判定し、_mm_aesenc_si128 等の組み込み関数で CPU 内部の専用回路を叩く。TLS・BitLocker・DB の TDE など、暗号化と速度を両立させたい場面で効く。

1. AES の位置づけ — 共通鍵暗号とハイブリッド方式

  • AES(Advanced Encryption Standard): 現在世界で最も広く使われる共通鍵暗号アルゴリズム。2001 年に NIST が標準化(FIPS 197)。暗号化通信・スマホのデータ保護などで日常的に使われる。
  • 公開鍵暗号(RSA 等)との違い: RSA は暗号化と復号に異なる鍵を使う公開鍵暗号。鍵の安全な受け渡しには向くが、複雑な数学計算を伴い処理が遅い。AES は暗号化と復号に同じ鍵を使い、大量データを非常に高速に処理できる。
  • ハイブリッド暗号方式(現代のネットワーク暗号化の主流): 重い公開鍵暗号で AES の共通鍵を安全に共有し、実データの大量通信は AES で高速に暗号化する、という役割分担。

AES は ブロック暗号の一種。データを 128 ビット(16 バイト)の固定サイズ「ブロック」に区切り、置換・行の入れ替え・列の混合といったラウンド処理を何度も繰り返す。記事の比喩 →「ルービックキューブを何度も複雑に回して元の色を完全にわからなくする」攪拌(注1: 複雑にかき混ぜること)。この攪拌+鍵長(鍵パターンの多さ)が暗号強度を生む。

2. AES の暗号強度 — 量子コンピュータ耐性

  • 量子コンピュータの発展で RSA などの公開鍵暗号は将来的に解読されるリスクがある。一方、共通鍵暗号(AES)は鍵長を増やすだけで十分な安全性を維持できるとされる。
  • AES-256 の鍵パターンは 2^256 個。量子の グローバーのアルゴリズムを使うと探索の手間が実質 2^128 まで減る(=鍵長の平方根オーダー、量子耐性の文脈で AES-256 が好まれる理由)。
  • それでも 1 秒間に 1 兆(10^12)回試行できても、2^128 パターンを調べるには約 10^19 年かかる。実質的に総当たりは不可能。

3. 内部構造 — State と 1 ラウンドの 4 ステップ(混乱と拡散)

入力 128 ビットを先頭から 4 バイトずつ 4 列に並べ、4×4 の状態行列 State として扱う。AES の全操作はこの State に対して行われる。

ブロック暗号は各ラウンドで混乱(Confusion)拡散(Diffusion)を繰り返す(注2: 平文が 1 ビット変わると暗号文の約半分のビットが変わるのが理想 = 厳密なアバランシェ基準 SAC)。

図1 AES-256 の暗号化の流れ(ASCII 再構成)

暗号化鍵(256bit)                 平文(128bit)
     │                              │
 KeyExpansion(鍵拡張)          State 初期化
     │                              │
     ├── 鍵 k0(128bit) ──→  AddRoundKey(ラウンド鍵加算)   … 初期ラウンド鍵加算
     │                              │
     │                     ┌─ ラウンド 1〜13 ─────────────┐
     ├── 鍵 k1〜k13 ──→   │ SubBytes → ShiftRows         │
     │                     │   → MixColumns → AddRoundKey │ × 13
     │                     └──────────────────────────────┘
     │                              │
     │                     ┌─ ラウンド 14(最終)─────────┐
     ├── 鍵 k14 ────→     │ SubBytes → ShiftRows         │ ← MixColumns は省略
     │                     │   → AddRoundKey              │
     │                     └──────────────────────────────┘
                               暗号文(128bit)

1 ラウンドの 4 ステップ

# ステップ 処理 役割
SubBytes(バイト置換) 各バイトを非線形変換 S-box で別バイトに置換(注3: GF(2^8) 上の乗法逆元 → アフィン変換した表) 混乱 Confusion。入出力の関係を攪拌
ShiftRows(行シフト) 各行を 0/1/2/3 バイト分だけ左巡回シフト。列の要素を別の列へ移す 拡散 Diffusion(MixColumns と組で全体へ波及)
MixColumns(列混合) 各列を行列の掛け算で別の値へ(注4: GF(2^8) 上の MDS 行列との乗算)。1 バイトの変化を列の 4 バイト全体に波及 拡散 Diffusion。最終ラウンドのみ省略
AddRoundKey(ラウンド鍵加算) State とラウンド鍵の XOR(排他的論理和) 鍵を混ぜ込む
  • 復号は手順を逆順に。最終ラウンドから開始し、各変換の逆変換 InvShiftRows / InvSubBytes / InvMixColumns を適用。ただし AddRoundKey は XOR の性質上、同じ鍵を再度 XOR すれば元に戻るので逆変換も同じ AddRoundKey でよい。

4. 鍵拡張(Key Expansion)

  • AES-256 の入力鍵は 256 ビットだが、内部の各ラウンドで使う鍵は合計 1920 ビット必要(= 15 個 × 128 ビット)。
  • そこで元の暗号化鍵から各ラウンド鍵を生成する処理が鍵拡張(Key Expansion)。AES-256 では 256 ビット鍵から 15 個の 128 ビットラウンド鍵(初期化用+14 ラウンド分)を作る。
  • 15 × 128 = 1920 ビットで本文の数値と整合。
  • 使う処理: RotWord(ローテーション)、SubWord(S-box 変換)、Rcon(ラウンド定数)との XOR。鍵に規則性があってもラウンド鍵間の相関がなくなるよう攪拌される。

図2 AES-256 のラウンド鍵生成(鍵拡張)の流れ(ASCII 再構成)

AES-256 暗号化鍵(256bit = 32バイト = 8ワード)
┌─ 初期化で使う鍵 k0 ─┐ ┌─ 1ラウンド目で使う鍵 k1 ─┐
│ w_l w_l+1 w_l+2 w_l+3│ │ w_l+4 w_l+5 w_l+6 w_l+7  │   (各ワード = 4バイト)
└──────────────────────┘ └──────────────────────────┘
        │  ┌──────── RotWord ← SubWord ← ⊕Rcon ────────┐
        └─→⊕ ─────────────────────────────────────────→ 次の 4 ワードを生成
┌─ 2ラウンド目 鍵 k2 ─┐ ┌─ 3ラウンド目 鍵 k3 ──────┐
│ w_l+8 … w_l+11      │ │ w_l+12 … w_l+15          │
└──────────────────────┘ └──────────────────────────┘
        … 14 ラウンド目まで(w_l+59 まで)同様に繰り返し鍵拡張する

5. ハードウェア高速化 — AES-NI

  • 課題: AES は強度が高い反面、MixColumns の行列演算を何度も繰り返すため計算コストが高い。
  • 解決: Intel/AMD の多くのプロセッサが AES 専用命令セット AES-NI(AES New Instructions) を搭載。ソフトウェアのみ実装に比べ暗号化・復号を大幅に高速化する。こうした専用回路・命令で高速化する技術をハードウェアアクセラレーションと呼ぶ。

Intel が定義する AES 命令(注5: Intel 公式資料)

命令 処理 AES-256 での利用
AESENC 1 ラウンド分の暗号化処理 1〜13 ラウンドの暗号化
AESENCLAST 最終ラウンドの暗号化処理 最終 14 ラウンドの暗号化
AESDEC 1 ラウンド分の復号処理 1〜13 ラウンドの復号
AESDECLAST 最終ラウンドの復号処理 最終 14 ラウンドの復号
AESKEYGENASSIST ラウンド鍵生成の支援処理(RotWord + SubWord + Rcon を実行) 鍵拡張

補足(一次資料で確認した記事外の事実): Intel 公式資料では AES-NI は全 6 命令で、上記 5 つに加え復号用ラウンド鍵を準備する AESIMC(Inverse MixColumns) がある(記事は省略)。AES-NI は 2010 年の Intel Core 系(32nm マイクロアーキ Westmere) から利用可能。専用回路化によりサイドチャネル攻撃への耐性向上という副次効果もある。

6. ライブラリ内部での命令呼び出し(コードで確認)

OpenSSL などを使うとき、プログラマーは CPU 命令を意識しない(ライブラリが最適な命令を自動選択)。その内部では次のように呼び出す。

Step 1 — CPU が AES 命令をサポートするか確認(リスト1)

GCC では CPUID の 25 ビット目(AES 機能フラグ) が 1 かをチェックする。

#include <stdio.h>
#include <cpuid.h>

int Check_CPU_support_AES()
{
    unsigned int eax, ebx, ecx, edx;
    if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
        return (ecx & (1 << 25)) != 0;   // ← 25 ビット目(AES 機能フラグ)が 1 かチェック
    }
    return 0;
}

Step 2 — AES-NI で ECB モード AES-256 暗号化(リスト2)

wmmintrin.h を include して組み込み関数を使う。key には AESKEYGENASSIST で生成した鍵拡張後のデータを渡す。_mm_aesenc_si128 が 1 ラウンド(AESENC)、_mm_aesenclast_si128 が最終ラウンド(AESENCLAST)。

#include <wmmintrin.h>

void AES_ECB_encrypt(const unsigned char *in,   // ← 平文へのポインタ
                     unsigned char *out,         // ← 暗号文へのポインタ
                     unsigned long length,       // ← テキストのバイト長
                     const char *key,            // ← 鍵拡張後の鍵データへのポインタ
                     int number_of_rounds)       // ← ラウンド数
{
    __m128i tmp;
    int i, j;
    long block_count = length / 16;   // ← 平文のバイト長をブロック数(16 バイト単位)に変換

    for (i = 0; i < block_count; i++) {
        tmp = _mm_loadu_si128(&((__m128i*)in)[i]);            // ← 入力
        tmp = _mm_xor_si128(tmp, ((__m128i*)key)[0]);         // ← ラウンド鍵加算
        for (j = 1; j < number_of_rounds; j++) {
            tmp = _mm_aesenc_si128(tmp, ((__m128i*)key)[j]);      // ← 第 N ラウンド
        }
        tmp = _mm_aesenclast_si128(tmp, ((__m128i*)key)[j]);      // ← 最終ラウンド
        _mm_storeu_si128(&((__m128i*)out)[i], tmp);              // ← 出力
    }
}

⚠️ 注6: 上記は AES-NI 命令の動作理解のためのサンプル。実運用では ECB ではなく CBC や GCM などの安全なモードを使い、平文のバイト数は適切にパディングして 16 の倍数にすること。ECB はブロックごとに独立暗号化するため同一平文ブロックが同一暗号文になり、パターンが漏れる(次回号の「暗号利用モード」に直結する論点)。

7. まとめ — AES 高速化はどこで効くか

  • AES 暗号化はプロセッサの AES 専用命令セット(AES-NI)= CPU 内部の専用回路を使って高速化されている。
  • 効く場面: TLS の暗号化通信、デバイス暗号化の BitLocker、データベースの透過的暗号化(TDE) など、暗号化とパフォーマンスの両立が必要な領域。
  • 次回予告(第6回 2026年6月号): AES が 128 ビット以上の平文を扱えるようにする「暗号利用モード」(ECB/CBC/GCM 等)。本記事のリスト2 が ECB だった理由・注6 の警告がここにつながる → [[aead_aes-gcm_認証付き暗号_整理]]。

早見表 — 状況 → 押さえどころ

状況 押さえどころ
量子時代に向けた暗号選定 公開鍵(RSA)は危険、共通鍵(AES)は鍵長を伸ばせば可。AES-256 は Grover 後も 2^128
AES が遅いと感じる/性能要件が厳しい AES-NI 対応 CPU か CPUID 25bit で確認。OpenSSL 等は自動利用
ECB のサンプルを本番転用しそう ECB はパターンが漏れる。CBC/GCM へ。パディングで 16 の倍数に
1 ビットの変化が暗号文に波及するか(拡散の確認) ShiftRows + MixColumns が担う。最終ラウンドは MixColumns 省略

参考リンク

  • Intel "Intel® Advanced Encryption Standard (Intel® AES) Instructions Set – Rev 3.01"(注5 の出典): https://www.intel.com/content/www/us/en/developer/articles/tool/intel-advanced-encryption-standard-aes-instructions-set.html
  • Intel "Advanced Encryption Standard Instructions (AES-NI)"(技術記事): https://www.intel.com/content/www/us/en/developer/articles/technical/advanced-encryption-standard-instructions-aes-ni.html
  • NIST FIPS 197 Advanced Encryption Standard (AES)(2001 標準化、2023 改訂): https://csrc.nist.gov/pubs/fips/197/final
  • AES instruction set — Wikipedia(6 命令・Westmere 2010 の裏取り): https://en.wikipedia.org/wiki/AES_instruction_set
  • Software Design 2026年5月号 連載「暗号のひみつ」第5回(荒木誠/@tex2e)

作成: 2026-06-29 / 最終更新: 2026-06-29