QRコードに埋め込むことができる非常に小さなOpenPGP暗号化ファイルを作成しようとしています。
ただし、たとえばOpenSSLと比較すると、GnuPGは「a」の単純な入力に対して非常に大きな結果を生成するようです。
$ echo -n a|openssl enc -aes-256-ctr|wc -c
17
$ echo -n a|gpg --symmetric -o-|wc -c
71
マニュアルを読むと、違いの1つは、OpenSSLにはデフォルトで8バイトのヘッダーと8バイトのソルトしか含まれていないのに対し、GnuPGにはソルト、チェックサム、圧縮も含まれていることです。これらをオフにすると、ファイルサイズは小さくなりますが、それでも大きくなります。
$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o-|wc -c
35
OpenPGPで暗号化されたメッセージをさらに最適化する方法はありますか(AESを有効にしたまま)?
GnuPGのバイナリ形式はかなりスペース効率が良いですが、絶対に最小のメッセージサイズではなく、柔軟性のために構築されています(通常、実際のメッセージは数バイトよりはるかに大きいです)。最小の「通常の」OpenPGPメッセージは31バイトの大きさですが、追加の作業を行うことで26バイトに削減できます。これは、シングルバイトコンテンツのOpenPGPv4メッセージとして可能な最小のものです。
RFC 4880を見ると、メッセージの最小の長さを導き出すことができますが、これを下回ることはできません。
作成したコマンドの出力を見てみましょう。
$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o-|gpg --list-packets
gpg: Note: simple S2K mode (0) is strongly discouraged
gpg: AES encrypted data
gpg: encrypted with 1 passphrase
gpg: WARNING: message was not integrity protected
最初のパケットは 対称鍵暗号化セッション鍵パケット です。これは、文字列からキーへのメカニズムを使用してパスフレーズによって暗号化されたセッションキーのコピーを保持します。 OpenSSLはこれを行いませんが、パスフレーズの代わりにセッションキーを提供し、セッションキーを個別に提供しない限り、これをスキップすることはできません(以下で説明します)。このパケットは6バイトの大きさで、次のものから構築されます。
# off=0 ctb=8c tag=3 hlen=2 plen=4
:symkey enc packet: version 4, cipher 7, s2k 0, hash 10
これで、暗号化されたデータパケットが開始されます。を含む:
# off=6 ctb=c9 tag=9 hlen=2 plen=26 new-ctb
:encrypted data packet:
length: 26
OpenPGPは、常にメッセージを リテラルデータパケット に格納します。これにより、メタデータが追加されます。圧縮を無効にすると、少なくとも追加の圧縮ヘッダーが削除されます。このパケットは最終的にさらに9バイトを追加します。
# off=26 ctb=cb tag=11 hlen=2 plen=6 new-ctb
:literal data packet:
mode b (62), created 1503680075, name="",
raw data: 0 bytes
まとめ:文字列からキーへの導出を省略し、パスフレーズの代わりにセッションキーを直接使用しない限り、さらに1バイトを保存することはできません。
GnuPGでは、--show-session-key
および--override-session-key
を使用してセッションキーを読み取って設定できます。 メッセージ構成の章 を読んで、有効なOpenPGPメッセージがセッションキーの暗号化を定義するパケットをまったく必要としないことに実際に驚いた。 GnuPGは確かにこの種の操作をサポートしていますが、これはOpenPGPを使用する非常に難解な方法であるため、他の実装には賭けません。
OpenPGP Message :- Encrypted Message | Signed Message |
Compressed Message | Literal Message.
Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data.
Encrypted Data :- Symmetrically Encrypted Data Packet |
Symmetrically Encrypted Integrity Protected Data Packet
そうすることで、対称鍵暗号化セッション鍵パケットの6バイトを節約できます。
GnuPGに事前定義されたセッションキーを使用させる方法が見つかりませんでした。ただし、対称的に暗号化されたメッセージを生成し、復号化中にセッションキーを抽出してから、メッセージを分割することができます。
メッセージの暗号化:
$ echo -n a|gpg --symmetric --compress-algo none --disable-mdc --s2k-mode 0 -o message.gpg
セッションキーの抽出(パスフレーズを要求します):
$ gpg --show-session-key 0 --decrypt message.gpg
gpg: AES encrypted data
gpg: encrypted with 1 passphrase
gpg: session key: '7:F7FBBA6E0636F890E56FBBF3283E524C'
agpg: WARNING: message was not integrity protected
OpenPGPメッセージを個々のパケットに分割します。
$ gpgsplit message.gpg
このフォルダには、暗号化されたmessage.gpg
、暗号化されていないmessage
、対称鍵で暗号化されたセッションキーパケット000001-003.sym_enc
、最後に暗号化されたデータパケット000002-009.encrypted
の4つのファイルが保持されます。
$ ls -l
total 16
-rw-r--r-- 1 jenserat jenserat 6 Aug 25 19:36 000001-003.sym_enc
-rw-r--r-- 1 jenserat jenserat 29 Aug 25 19:36 000002-009.encrypted
-rw-r--r-- 1 jenserat jenserat 1 Aug 25 19:04 message
-rw-r--r-- 1 jenserat jenserat 35 Aug 25 19:33 message.gpg
個々のパケットファイルをconcat
enateして、message.gpg
を取得することもできます。これらの2つのファイルはmessage.gpg
の一部に分割されています。上記のサイズと正確に一致するファイルサイズを確認してください(もちろん、gpgsplit
はパスフレーズを認識しないため、リテラルデータパケットのサイズは暗号化されたデータパケットに含まれます)。
この手順はかなり簡単です。
$ gpg --override-session-key '7:F7FBBA6E0636F890E56FBBF3283E524C' --decrypt 000002-009.encrypted
agpg: WARNING: message was not integrity protected
出力である警告メッセージの前にあるa
を見落とさないでください。
GnuPGは2つの警告メッセージを出力しました。
gpg:注:単純なS2Kモード(0)は強くお勧めしません
これは、単純なS2Kモードでは、ハッシュやソルトを使用しないため、パスフレーズに対するブルートフォース攻撃や辞書攻撃が安価で簡単になるためです。
もちろん、同じパスフレーズを使用して暗号化された複数のファイルに同じセッションキーを使用することもできますが、その結果に注意してください。
gpg:警告:メッセージは完全性保護されていませんでした
この警告メッセージは、復号化当事者がこの事実を認識できずに、攻撃者によってメッセージが変更された可能性があることを示しています。これは、--disable-mdc
が原因です。もちろん、ファイルの暗号化されたチェックサムのためにいくつかのバイトが保存されます。 16進エディタで最後のバイトを変更することにより、自分で試すことができます。