SIGABA-EXとは?
SIGABA-EXは、第二次世界大戦期の暗号機SIGABAへの敬意を受け継ぎ、その先を 目指して開発した現代の暗号通信端末です。
「EX」には、3つの意味を込めています。
- EXceed — SIGABAを超えることを目指す
- EXtended — SIGABAの思想を現代のWeb暗号へ拡張する
- EXchange — 共有キーを用いて暗号文を交換する
歴史への敬意、技術の発展、そして人と人との安全な通信。その3つを一つの 名前に込めています。
この暗号ツールを使用する際の注意点
SIGABA-EXは、学習・個人利用を目的とした暗号化ツールです。ブラウザ内だけで 暗号化・復号化が完結し、平文・暗号文・鍵コードのいずれもサーバーへ送信・ 保存されません。安心してお使いいただくために、採用しているアルゴリズムと、 あらかじめ知っておいていただきたい限界をこのページにまとめます。
このツールはHTTPS(またはlocalhost)でのみ動作します。
暗号化・復号化にはブラウザ標準のWeb Crypto API(crypto.subtle)を使用しており、これはブラウザの仕様上、HTTPSで配信されたページでしか利用できません。平文HTTPで開いた場合はエラーメッセージが表示され、処理が中止されます。
2つの鍵方式
SIGABA-EXは、用途に応じて選べる2つの鍵方式を提供しています。
| 合言葉モード(既定・FORMAT_VERSION 7) | 乱数鍵モード(FORMAT_VERSION 8) | |
|---|---|---|
| 鍵の形式 | 人間が読める半角英数16〜32文字 | 256bitの完全ランダム値(Base64URL・43文字) |
| 鍵導出 | PBKDF2-HMAC-SHA256(400,000回反復)を経由してAES-256-GCM鍵を導出 | 経由しない。生成した256bitの乱数をそのままAES-256-GCM鍵としてインポートする |
| 鍵空間 | 文字列の長さ・種類に依存(総当たり耐性はPBKDF2の反復回数に頼る) | 常に256bit(原理的に最大。鍵導出のステップ自体が存在しないため、反復回数の妥当性という論点自体がない) |
| 向いている場面 | 鍵を対面・電話など口頭ベースで伝える場合 | 鍵自体をテキストとして安全な経路(パスワードマネージャー等)で受け渡しできる場合 |
復号時はモードを意識する必要がありません。 暗号文のヘッダに埋め込まれたバージョン番号(マスクされた状態で格納) から、どちらのモードで暗号化されたかを自動的に判別します。利用者は、 受け取った鍵の値(合言葉、またはBase64URL形式の乱数鍵)を そのまま「鍵」欄に貼り付けるだけで復号できます。
この機能を追加した経緯:ネット上の他の暗号化ツールには、 パスワードを経由せず256bitの完全ランダム鍵をブラウザで直接生成する 方式を採用しているものがあります。この方式は、PBKDF2でどれだけ強化 しても有限の文字列空間に留まる合言葉モードよりも、鍵空間という 観点では原理的に優れています。SIGABA-EXでも、合言葉モードの 使いやすさ(口頭・電話で伝えられる)を維持しつつ、より高い鍵強度を 求める利用者向けの選択肢として、この方式を追加のオプションとして 用意しました。
「生成ボタンでしか作れない」制限は、セキュリティ境界ではありません。
乱数鍵モードでは、画面上「乱数鍵生成」ボタンで作った値以外は暗号化に使えないようになっています。ただし、これはあくまで「弱い鍵を自分で考えて打ち込んでしまう」という誤操作を防ぐためのUI上の配慮であり、暗号学的なセキュリティ境界ではありません。実装は、直近で生成ボタンが作った値をJavaScriptの変数として保持し、暗号化時に入力欄の値と一致するかを照合しているだけです。ブラウザの開発者ツールを使えば、この照合は誰でも数秒で回避できます。守ろうとしているのは「攻撃者からの防御」ではなく「利用者自身のうっかりミス」である点を理解した上でお使いください。なお、この制限は乱数鍵モードのみに適用されており、合言葉モードでは引き続き任意の16〜32文字を自由に手入力できます。
採用アルゴリズム
| 用途 | アルゴリズム | 備考 |
|---|---|---|
| 鍵導出(合言葉→AES-256-GCM用鍵) | PBKDF2-HMAC-SHA256, 400,000回反復(crypto.subtleのPBKDF2で直接鍵を導出) |
合言葉モードのみ。ソルトは暗号化のたびにランダム生成(16byte)。事前計算(レインボーテーブル)攻撃の使い回しを防ぐ |
| 鍵のインポート(乱数鍵モード) | 256bitの乱数をcrypto.subtle.importKeyで直接AES-256-GCM鍵として使用 |
PBKDF2を経由しない。鍵そのものが既に最大エントロピーのため、鍵導出による強化が不要 |
| 暗号化+改ざん検知 | AES-256-GCM(NIST標準AEAD、crypto.subtleネイティブ実装) |
暗号化と認証を1つの標準構成で行う。両モード共通 |
| ヘッダマスク用ハッシュ | SHA-256(crypto.subtle.digest) |
秘密鍵は使わない(下記参照) |
| 乱数源 | crypto.getRandomValues(Web Crypto API)のみ |
利用できない端末・ブラウザでは、弱い乱数へフォールバックせず処理を中止する |
| ノンスの生成方式 | AES-GCMの96bitノンスを暗号化のたびにcrypto.getRandomValuesで生成 |
カウンタ管理はしていない(下記「既知の限界」を参照) |
| 暗号文表現 | ヘッダをBase36・本文をBase62 | 半角英数字のみで構成し、コピー&ペーストしやすくするため |
| ヘッダの識別性対策 | version+長さ(5byte)をソルト由来のマスクでXOR | 短文では固定パターン(例:毎回同じ先頭文字列)が出ないようにし、どのツールで暗号化したかの推測を難しくする |
| 平文の長さの秘匿(パディング) | 暗号化前に平文を256byte単位にパディング(ISO/IEC 7816-4方式) | 短いメッセージと少し長いメッセージが同じ長さの暗号文になり、内容を知らない第三者が長さから文脈を推測する難易度を上げる(下記参照) |
v7での変更点:v6までは、平文をそのままAES-256-GCMで暗号化して いました。AES-GCMは平文と同じ長さの暗号文を生成するストリーム暗号モード のため、暗号文の長さから平文のバイト数がほぼそのまま推測できてしまう という弱点がありました(後述の「既知の限界」で以前から明記していました)。 v7では、暗号化前の平文を256byte単位にパディングしてから暗号化することで、 この弱点を緩和しています。例えば「はい」のような数バイトの短文も、それ より長い数十〜100文字程度の文章も、同じ1ブロック分の暗号文の長さに なります。ただし、これは完全な秘匿ではなく「長さの粒度を粗くする」 対策である点に注意してください。256byteを超える長文では、依然として おおよそ何ブロック分(256byte区切り)かは暗号文の長さから推測できます。
v6での大きな変更点:v5まではChaCha20-Poly1305 AEADをこの
プロジェクト独自にJavaScriptで実装していました。RFC準拠であることや
Node.js組み込み実装との出力一致はテストで確認していましたが、それでも
「ブラウザベンダーが実装・監査しているネイティブコードと同等に信頼できるか」
という点では見劣りするという指摘がありました。v6では、鍵導出(PBKDF2)・
暗号化+認証(AES-256-GCM)・ヘッダマスク用のハッシュ(SHA-256)の
すべてを、ブラウザ・Node.js共通のWeb Crypto API(crypto.subtle)に
置き換えています。これにより、暗号処理の実体はブラウザベンダー
(Chromium/Firefox/WebKit)が実装・監査しているネイティブコードが担う
ことになり、このプロジェクト固有の自前実装(PBKDF2/HMAC/SHA-256/
ChaCha20/Poly1305の手書きJS実装)はコードベースから完全に削除しました。
復号時は必ずタグを検証し、検証に失敗した場合(鍵違い・改ざん・破損の
いずれか)は平文を画面に出力しません。
Webセキュリティ強化(v8)
暗号アルゴリズム自体は既に十分な強度に達していると判断し、v8では 「暗号システム全体を堅牢化する」観点から、Webアプリケーションとしての 土台部分を強化しました。暗号の中身がどれだけ強くても、XSS等で鍵や 平文が盗聴されてしまえば意味がないため、これは暗号強化と並んで 重要な対策です。
| 対策 | 内容 | 設定方法 |
|---|---|---|
| CSP(Content-Security-Policy) | script-src/style-srcを'self'のみに限定し、'unsafe-inline'は一切許可しない |
HTMLの<meta>タグ、および_headers(Cloudflare Pages)/.htaccess(Apache系ホスト) |
| HSTS(Strict-Transport-Security) | HTTPSでの接続を強制し、HTTPへのダウングレードを防ぐ | _headers/.htaccessのみ(HTTPレスポンスヘッダでしか設定できない仕様のため) |
| SRI(Subresource Integrity) | 該当なし。外部CDN・外部スクリプトを一切使用しておらず、すべてのリソースを自ドメインから配信しているため、SRIを付与する対象自体が存在しない | - |
| X-Frame-Options / frame-ancestors | 他サイトへの埋め込み(クリックジャッキング対策) | _headers/.htaccessのみ(frame-ancestorsは<meta>タグでは効果を持たない仕様のため) |
| X-Content-Type-Options | MIMEスニッフィング対策 | _headers/.htaccess |
| Referrer-Policy | no-referrer。リファラー経由の情報送出を最小化 |
_headers/.htaccess |
| Permissions-Policy | カメラ・マイク・位置情報など、このツールが使わないブラウザ機能を明示的に無効化 | _headers/.htaccess |
SIGABA-EXは2種類のヘッダー設定ファイルを同梱しています。デプロイ先に 応じて、該当する方が自動的に使われます(両方置いておいても問題 ありません。使われない方は単に無視されます)。
-
Cloudflare Pagesの場合(推奨):プロジェクトルートの
_headersファイルが使われます。これはCloudflare Pages公式の ヘッダー設定方式で、サーバー設定・環境変数・ビルド手順を一切必要とせず、 確実に反映されることが公式に保証されています。 -
Apache系のレンタルサーバーの場合(例:cloudfree.jp等):
.htaccessが使われます。ただし、このプロジェクトでは以前、.htaccessの文字コード指定がホスト環境によって反映されない 現象が確認されています。ホストがApache用ディレクティブをどこまで 正しく解釈するかは環境依存であり、_headersほどの確実性は ありません。
ヘッダー設定の確実性という観点では、Cloudflare Pagesでの運用を推奨します。
CSPの主要部分(script-src/style-src等)はHTML側の<meta>タグでも併せて設定しているため、.htaccessが効かない環境でも最低限の防御は機能しますが、反映状況はブラウザの開発者ツール(ネットワークタブ→レスポンスヘッダ)で確認することをおすすめします。
既知の限界
-
自前の暗号実装は
crypto.subtleに置き換わりましたが、鍵導出方式やヘッダ構造自体はこのプロジェクト独自の設計です。 暗号化・改ざん検知そのものはAES-256-GCMというブラウザネイティブの実装に 委ねていますが、「合言葉1つから毎回ランダムなソルトを使って鍵を 導出する」という全体の設計や、暗号文のヘッダ構造・マスク方式は、監査を 受けた既製ライブラリの設計ではなく、このプロジェクト独自のものです。 -
ノンスは構造的な重複防止ではなく、確率的な安全性に依拠しています。
AES-GCMの96bitノンスは、暗号化のたびに
crypto.getRandomValuesで 完全にランダム生成しており、カウンタ管理(「同じ鍵で何回目の暗号化か」を 記録して重複を構造的に避ける方式)は行っていません。同一鍵で約2^48回 (誕生日限界)の暗号化を行わない限り、ノンスの衝突確率は無視できるほど 小さいままです。鍵を人間が使い回す個人・小規模利用の範囲では、 この回数に到達することは現実的にありません。ただし「絶対に重複しない」 という構造的な保証ではなく、確率的な安全性に依拠している点は理解して おいてください(なお、AES-GCMは本来ノンスの再利用に対してChaCha20-Poly1305 よりもデリケートなアルゴリズムとされています。今回の設計では、ランダムな 96bitノンスを暗号化のたびに新規生成するという運用によって、この点への 対策としています)。 -
乱数が確保できない場合は処理を中止します(弱い乱数へは逃げません)。
crypto.getRandomValuesが利用できない端末・ブラウザでは、ノンス・ ソルト・鍵コードの生成いずれについても、Math.randomのような 暗号学的に弱い乱数へ静かにフォールバックすることはありません。安全性を 優先し、エラーメッセージを表示して処理を中止します。 - 鍵の受け渡し方法はこのツールの対象外です。 鍵(合言葉、または乱数鍵)をどうやって安全に相手に伝えるか(対面での 交換、既存の信頼できる別チャネルの利用など)は利用者の責任です。鍵を 暗号化されていない経路でそのまま送ってしまうと、暗号化の意味が なくなります。
- ソルトはランダム生成ですが、鍵導出の反復回数は固定です。 ソルトを暗号化のたびにランダム化したことで、事前計算(レインボーテーブル) による攻撃の使い回しは防げますが、PBKDF2の反復回数(400,000回)自体は 固定です。鍵自体の強度(長さ・ランダム性)に強く依存する点は変わりません。
- ヘッダのマスクは「見た目の均一化」であり、暗号強度の向上そのものではありません。 version・平文長のバイトをソルト由来のマスクでXORしているのは、暗号文の 先頭が毎回同じパターンになり「SIGABA-EXで作られた暗号文だ」と外部から 推測されることを防ぐためです。マスクの導出に秘密鍵は使っておらず (ソルト自体が公開情報のため)、これによって鍵やノンス、タグの安全性が 変わるわけではありません。
- 前方秘匿性(Forward Secrecy)はありません。 同じ鍵を使い続ける限り、その鍵が後から漏洩すると 過去にやり取りした暗号文もすべて復号可能になります。
- メタデータの保護は部分的です(v7でパディングにより緩和)。 v7から、暗号化前に平文を256byte単位でパディングするようになったため、 同じ256byteブロックに収まる長さの平文であれば、暗号文の長さから元の 文字数を区別することはできません(例:「はい」も、それより長い数十文字 程度の文章も、同じ長さの暗号文になります)。ただし、これは平文の長さの 「粒度を粗くする」対策であり、完全な秘匿ではありません。256byteを超える 長文では、おおよそ何ブロック分かは暗号文の長さから引き続き推測できます。 また、誰が誰と通信したか、いつ通信したかといった情報もこのツール自体 では保護しません。
- ブラウザ環境・端末の安全性が前提です。 ブラウザの開発者ツール、マルウェア、キーロガーなどにより、入力した平文・ 鍵コードが盗み見られるリスクはこのツールの対象範囲外です。
想定する利用シーン
個人間・小規模チーム内での、学習目的や日常的なメモ・メッセージの簡易的な
保護を想定しています。暗号化・改ざん検知そのものはAES-256-GCMという
NIST標準AEADに、ブラウザネイティブの実装(crypto.subtle)を
通じて委ねていますが、鍵導出方式(ランダムソルト、反復回数400,000回)や
暗号文のヘッダ構造はこのプロジェクト独自の設計です。国家レベルの攻撃者や
高度な標的型攻撃からの保護、法令上の機密情報保護義務がある用途には
使用しないでください。そのような用途には、監査済みの商用・OSSライブラリ
の利用を検討してください。
使い方
- 画面上部で鍵の方式を選びます。合言葉モード (既定)では、人間が読める半角英数16〜32文字の鍵を「合言葉」欄に 入力します(「合言葉生成」ボタンでも生成できます)。 乱数鍵モードに切り替えると、256bitの完全 ランダムな鍵(Base64URL・43文字)を「乱数鍵生成」ボタンで生成 できます(誤って弱い鍵を打ち込んでしまわないよう、乱数鍵モードは 生成ボタンで作った値のみ暗号化に使えます)。
- 左側の「平文 → 暗号文」パネルに文章を入力し、「暗号化」を押します。
- 生成された暗号文を、鍵を共有した相手にコピーして送ります。
- 相手は同じ鍵を入力し、右側の「暗号文 → 平文」パネルに暗号文を貼り付けて 「復号化」を押すと、平文が復元されます(復号時は鍵の方式を選ぶ必要は ありません。暗号文のヘッダから自動的に判別されます)。
鍵が違う場合や、暗号文が途中で改ざん・破損した場合は、タグ検証エラーとして 表示され、平文は復元されません。
合言葉や乱数鍵は暗号文と一緒に相手に送らないでください。
暗号文をLINEやメールで送る場合でも、合言葉や乱数鍵は対面で伝える、電話など別の通信経路を利用するなど、暗号文とは異なる経路で安全に共有してください。
このページの内容は、リポジトリ内の技術文書(SECURITY.md)と同じものです。 サーバーの設定によっては.mdファイルを直接開くと文字化けする場合があるため、 このページ(HTML)を正式な閲覧先としてご利用ください。