# 機能設計書 26-暗号化サービス

## 概要

本ドキュメントは、Laravel Frameworkの暗号化サービス（Encrypter）の設計仕様を記載する。Encrypterは、アプリケーションデータの暗号化・復号化機能を提供するコアセキュリティコンポーネントである。

### 本機能の処理概要

Encrypterは、OpenSSLを使用した対称鍵暗号化を提供し、アプリケーション内のセンシティブなデータを安全に保護する。AES-128-CBC、AES-256-CBC、AES-128-GCM、AES-256-GCMの各暗号化方式をサポート。

**業務上の目的・背景**：Webアプリケーションでは、パスワード以外にもセンシティブなデータ（APIキー、個人情報、支払い情報等）を安全に保存・伝送する必要がある。Encrypterは、これらのデータを強力な暗号化アルゴリズムで保護し、アプリケーションキーを知らない第三者からの読み取りを防止する。また、データの改ざん検知（MAC検証）も提供する。

**機能の利用シーン**：Cookieの暗号化（CookieのEncrypt/Decryptミドルウェア）、セッションデータの暗号化（EncryptedStore）、Queueジョブのペイロード暗号化、APIトークンの暗号化、機密設定値の保護など。

**主要な処理内容**：
1. encrypt()メソッドでデータをシリアライズして暗号化
2. decrypt()メソッドで復号化してデシリアライズ
3. MAC（メッセージ認証コード）による改ざん検知
4. AEAD暗号（GCMモード）でのタグベース認証
5. 旧キーによる復号化のサポート（キーローテーション）

**関連システム・外部連携**：PHPのOpenSSL拡張、SerializableClosure（クロージャのシリアライズ）との連携。

**権限による制御**：本機能自体には権限制御はない。APP_KEYに基づく暗号化処理を提供。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面 | 主機能 | Cookie/セッション暗号化 |

## 機能種別

セキュリティ / 暗号化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string | Yes | 暗号化キー | 暗号化方式に応じた長さ |
| cipher | string | No | 暗号化方式（デフォルト: aes-128-cbc） | サポートされる方式 |
| value | mixed | Yes | 暗号化対象データ | - |
| serialize | bool | No | シリアライズするか（デフォルト: true） | true/false |

### 入力データソース

- アプリケーション設定（APP_KEY、cipher設定）
- 暗号化対象のアプリケーションデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| encrypted | string | Base64エンコードされた暗号文（JSON構造） |
| decrypted | mixed | 復号化されたオリジナルデータ |

### 出力先

- 暗号化結果: 呼び出し元に返却
- 復号化結果: 呼び出し元に返却

## 処理フロー

### 処理シーケンス

```
暗号化処理:
1. encrypt()で値を受け取る
   └─ シリアライズオプションに応じてserialize()

2. ランダムIV（初期化ベクトル）を生成
   └─ openssl_cipher_iv_length()でIV長を取得
   └─ random_bytes()でIVを生成

3. openssl_encrypt()で暗号化
   └─ GCMモード: タグを取得
   └─ CBCモード: MAC を計算

4. JSON構造を構築
   └─ iv, value, mac, tag を含む

5. Base64エンコードして返却

復号化処理:
1. decrypt()でペイロードを受け取る
   └─ Base64デコード
   └─ JSON解析

2. ペイロード検証
   └─ iv, value, mac の存在確認
   └─ IV長の検証

3. MAC/タグ検証
   └─ CBCモード: HMAC-SHA256で検証
   └─ GCMモード: タグ長を検証

4. 復号化を試行（現在キー → 旧キー）

5. デシリアライズして返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{暗号化/復号化?}
    B -->|暗号化| C[serialize if needed]
    C --> D[ランダムIV生成]
    D --> E[openssl_encrypt]
    E --> F{GCMモード?}
    F -->|Yes| G[タグを取得]
    F -->|No| H[MACを計算]
    G --> I[JSON構造を構築]
    H --> I
    I --> J[Base64エンコード]
    J --> K[暗号文を返却]

    B -->|復号化| L[Base64デコード]
    L --> M[JSONパース]
    M --> N{ペイロード有効?}
    N -->|No| O[DecryptException]
    N -->|Yes| P{GCMモード?}
    P -->|Yes| Q[タグ検証]
    P -->|No| R[MAC検証]
    Q --> S{検証成功?}
    R --> S
    S -->|No| O
    S -->|Yes| T[openssl_decrypt]
    T --> U{復号成功?}
    U -->|No| V[旧キーで再試行]
    V --> W{成功?}
    W -->|No| O
    W -->|Yes| X[unserialize]
    U -->|Yes| X
    X --> Y[平文を返却]
    K --> Z[終了]
    Y --> Z
    O --> Z
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | キー長検証 | 暗号化キーは方式に応じた正確な長さが必要 | 常時 |
| BR-02 | MAC検証 | CBCモードではHMAC-SHA256でデータ整合性を検証 | CBCモード使用時 |
| BR-03 | タグ検証 | GCMモードでは16バイトのタグで認証 | GCMモード使用時 |
| BR-04 | キーローテーション | 復号化時に現在キーで失敗したら旧キーを試行 | previous_keys設定時 |

### 計算ロジック

- AES-128: キー長16バイト、IV長16バイト
- AES-256: キー長32バイト、IV長16バイト
- MAC計算: hash_hmac('sha256', iv.value, key)
- GCMタグ: 16バイト（openssl_encryptで自動生成）

## データベース操作仕様

本機能はデータベースを直接操作しない。暗号化/復号化処理のみを提供。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EncryptException | 暗号化エラー | 暗号化処理失敗 | データ形式の確認 |
| DecryptException | 復号化エラー | 不正なペイロード | データの再取得または再生成 |
| DecryptException | MAC不一致 | データ改ざん検出 | 元データの再暗号化 |
| RuntimeException | 設定エラー | 無効なキー長/暗号方式 | 設定の確認 |

### リトライ仕様

復号化時、現在のキーで失敗した場合は自動的に旧キー（previous_keys）で再試行を行う。

## トランザクション仕様

本機能はトランザクションを使用しない。純粋な暗号化/復号化処理のみ。

## パフォーマンス要件

- 暗号化/復号化処理はCPU集約的だが、小〜中規模データでは十分高速
- 大量データの暗号化は分割処理を検討
- GCMモードはCBCモードより若干高速（MAC計算が不要）

## セキュリティ考慮事項

- **APP_KEY**: 32文字以上の強力な乱数を使用、環境変数で管理
- **キー漏洩**: APP_KEYが漏洩した場合は全暗号化データの安全性が損なわれる
- **キーローテーション**: previous_keys機能で安全なキー更新が可能
- **タイミング攻撃対策**: hash_equals()でMAC比較
- **AEAD推奨**: GCMモード使用時は認証と暗号化が統合され、より安全
- **シリアライズ**: 信頼できないデータのunserialize禁止（RCE脆弱性）

## 備考

- EncryptionServiceProviderで自動登録
- 'encrypter'エイリアスでコンテナから解決可能
- Crypt Facadeで簡易アクセス可能
- APP_KEYは `php artisan key:generate` で生成

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、暗号化の契約インターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Encrypter.php | `src/Illuminate/Contracts/Encryption/Encrypter.php` | 暗号化の契約 |
| 1-2 | StringEncrypter.php | `src/Illuminate/Contracts/Encryption/StringEncrypter.php` | 文字列専用暗号化 |

**読解のコツ**: encrypt/decryptメソッドのシグネチャとserializeパラメータを確認。

#### Step 2: サービス登録を理解する

EncryptionServiceProviderでの登録を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EncryptionServiceProvider.php | `src/Illuminate/Encryption/EncryptionServiceProvider.php` | サービス登録とキー解析 |

**主要処理フロー**:
- **16-20行目**: register()でencrypterとSerializableClosureを登録
- **27-37行目**: registerEncrypter()でシングルトン登録、previous_keysを設定
- **62-69行目**: parseKey()でbase64プレフィックスを処理
- **79-86行目**: key()でAPP_KEY取得、未設定時は例外

#### Step 3: コア実装を理解する

Encrypterクラスの詳細実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Encrypter.php | `src/Illuminate/Encryption/Encrypter.php` | 暗号化/復号化の実装 |

**主要処理フロー**:
- **39-44行目**: supportedCiphersで対応暗号方式を定義
- **54-66行目**: コンストラクタでキーと暗号方式を検証
- **75-82行目**: supported()でキー長と暗号方式の整合性を確認
- **90-93行目**: generateKey()で新しいキーを生成
- **104-131行目**: encrypt()で暗号化処理
- **155-196行目**: decrypt()で復号化処理
- **219-222行目**: hash()でMAC計算
- **281-284行目**: validMac()でMAC検証
- **391-404行目**: previousKeys()で旧キーを設定

### プログラム呼び出し階層図

```
EncryptionServiceProvider::register()
    │
    ├─ registerEncrypter()
    │      │
    │      ├─ parseKey() ─── base64デコード
    │      │
    │      ├─ new Encrypter(key, cipher)
    │      │
    │      └─ previousKeys() ─── 旧キー設定
    │
    └─ registerSerializableClosureSecurityKey()
           └─ SerializableClosure::setSecretKey()

Encrypter::encrypt()
    │
    ├─ serialize() ─── オプション
    │
    ├─ random_bytes(iv_length)
    │
    ├─ openssl_encrypt()
    │      └─ GCM: $tag取得
    │
    ├─ hash() ─── CBC: MAC計算
    │
    ├─ json_encode(iv, value, mac, tag)
    │
    └─ base64_encode()

Encrypter::decrypt()
    │
    ├─ base64_decode()
    │
    ├─ json_decode()
    │
    ├─ validPayload()
    │
    ├─ ensureTagIsValid() ─── GCM
    │
    ├─ getAllKeys() ─── 現在 + 旧キー
    │      │
    │      └─ foreach key
    │             │
    │             ├─ validMacForKey() ─── CBC
    │             │
    │             └─ openssl_decrypt()
    │
    └─ unserialize() ─── オプション
```

### データフロー図

```
[暗号化]

平文データ
    │
    ▼
serialize()
    │
    ▼
random_bytes() ───▶ IV (16 bytes)
    │
    ├─────────────────┐
    ▼                 ▼
openssl_encrypt() + IV
    │
    ├─── GCM: $tag取得
    └─── CBC: hash() → MAC
    │
    ▼
JSON: {iv, value, mac, tag}
    │
    ▼
base64_encode()
    │
    ▼
暗号文（文字列）


[復号化]

暗号文
    │
    ▼
base64_decode()
    │
    ▼
json_decode()
    │
    ▼
validPayload() ───▶ 検証
    │
    ▼
openssl_decrypt() ◀── IV, key
    │
    ▼
unserialize()
    │
    ▼
平文データ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Encrypter.php | `src/Illuminate/Encryption/Encrypter.php` | ソース | 暗号化/復号化実装 |
| EncryptionServiceProvider.php | `src/Illuminate/Encryption/EncryptionServiceProvider.php` | ソース | サービス登録 |
| MissingAppKeyException.php | `src/Illuminate/Encryption/MissingAppKeyException.php` | ソース | キー未設定例外 |
| Encrypter.php | `src/Illuminate/Contracts/Encryption/Encrypter.php` | インターフェース | 契約定義 |
| StringEncrypter.php | `src/Illuminate/Contracts/Encryption/StringEncrypter.php` | インターフェース | 文字列契約 |
| DecryptException.php | `src/Illuminate/Contracts/Encryption/DecryptException.php` | インターフェース | 復号化例外 |
| EncryptException.php | `src/Illuminate/Contracts/Encryption/EncryptException.php` | インターフェース | 暗号化例外 |
| Crypt.php | `src/Illuminate/Support/Facades/Crypt.php` | ソース | Facade |
