世界を動かす技術を、日本語で。

スクリーム暗号

概要

  • Unicodeの「A」バリエーションを活用した SCREAM CIPHER の紹介
  • アルファベットごとに異なる Unicode文字 へマッピング
  • Pythonによる エンコード・デコード関数 の実装例
  • 変換結果の例と 復号 の仕組み
  • 体験的に楽しめる 暗号化手法 の解説

SCREAM CIPHERとは

  • SCREAM CIPHER は、英語アルファベットをそれぞれ異なる Unicodeの「A」系文字 に置き換える暗号
  • 通常の stream cipher とは異なり、ユニークな「叫び」表現を実現
  • 各アルファベット大文字・小文字に対応する 特殊文字へのマッピング を定義
  • 例: "A" → "A" (ラウンドトリップ)、 "B" → "Á""C" → "Ă" など
  • 小文字にも対応するため、 辞書を拡張 している

Python実装例

  • CIPHER :英字からUnicode特殊文字へのマッピング辞書

  • UNCIPHER :逆変換用の辞書(特殊文字→元の英字)

  • SCREAM関数 :テキストを特殊文字に変換する関数

  • unscream関数 :特殊文字から元のテキストに復号する関数

    • def SCREAM(text: str) -> str:
          return "".join(CIPHER.get(ch, ch) for ch in text)
      
      def unscream(scream: str) -> str:
          return "".join(UNCIPHER.get(ch, ch) for ch in scream)
      

変換例と使い方

  • "SCREAM CIPHER" をSCREAM CIPHERで変換すると:
    • 変換後 :ǠĂȦẶAẦ ĂǍÄẴẶȦ
    • 復号後 :"SCREAM CIPHER"
  • 英文や単語を 叫び声のような特殊文字列 に変換可能
  • 元の英文への 復号も一対一対応 で容易

応用・注意点

  • 暗号 としての強度は低いため、実用的なセキュリティ用途には不向き
  • ネタや ジョーク暗号、テキスト装飾、SNS投稿などに最適
  • Unicode非対応のシステムでは 文字化けの可能性 あり

まとめ

  • Unicodeの多様性を活かした 遊び心ある暗号化手法
  • Pythonで簡単に実装・体験可能
  • 友達やSNSで 話題作り に活用可能

Hackerたちの意見

なんかよくわからないけど、あれこれいじってみた。

Ǎ ăẫẩǡǎắặȧ ǎạ a ăẵaậậặẩẳặ áặằẫȧặ ạẵặ ảẵẫậặ ẵȁầaẩ ȧaăặ! Aẩắ Ǎ aǎẩ'ạ ẳẫẩẩa ậẫǡặ.....

最後の単語はROT13だと思う!今、解読できるね!そして、最後から二番目は「like」かも。

256個以上のUnicode結合マークがあって、2バイトのUTF-8エンコーディングになってるんだ。いくつか選んで、俺が「zalgo256」って呼ぶエンコーディングを定義したよ。https://gist.github.com/DavidBuchanan314/07da147445a90f7a049... 結合文字がどんなに積み重なっても、1つのグラフェムクラスタとしてカウントされるから、アプリがグラフェムクラスタで文字列の長さを制限してるなら、そこに無限のデータを詰め込めるんだ。ただし、バイト表現では「たった」2倍のオーバーヘッドがあるけどね。残念ながらHNは一部のコードポイントをフィルタリングしてるから、ここではデモできないんだ。基準文字として「A」を選んだから、ダイアクリティカルマークが積み重なって、SCREAM暗号に似た美的感覚があるけど、ちょっとzalgoっぽい感じだね。

Gistのコメントでデモしたらいいかも!それ見てみたいな。

これで偶然にThe Oneを呼び出さないか心配?

HNは一部の結合文字をフィルタリングしてるの?それは変だね、シンボルや絵文字のブロックと比べると。あと、ユニコードの正規化付録を思い出したけど、正当なグラフエムクラスターは31コードポイント以下になるはずだって。「30の値は、言語的または技術的な使用に必要なものを大きく超えるように選ばれている。」

https://xkcd.com/3054/

ああ、これでスクリーム暗号の基準が2つできちゃうのか。https://xkcd.com/927/

ああ、これXKCDからのやつだ(2025年2月)。OPがそれを言わなかったのはちょっと変だね。

https://www.dcode.fr/scream-cipher-xkcd

このxkcdにやられちゃって、実装を進めてたら、アクセントコンバイナーがどんな文字でも使えることに気づいたよ。悪い暗号に悪いステガノグラフィーを追加するのは簡単だね。r̊e̝q̝ůěs̔t͞ p̊e̝a͞c̍e̊ t̠a̗lks

ちょっと前に似たようなことをやったけど、見えない文字を使ってテレグラムメッセージにメタデータをエンコードしてたんだ。https://github.com/sixhobbits/unisteg

なんでこれがTelegramのメッセージに役立つのか、すごく混乱してたけど、READMEの「Why?」の部分は納得できるね。バカな制限に対する素晴らしい対策だ!

Racketバージョンを書くのが楽しかったよ:

#lang racket/base
(require net/base64 threading)
(define FIRST-INVISIBLE-CHAR 917760)
(define (invis-encode str)
  (list->string (for/list ([c (in-list (string->list str))]
                            #:do [(define cnum (char->integer c))]
                            #:when (char (+ cnum FIRST-INVISIBLE-CHAR)))))
(define (invis-decode str)
  (list->string (for/list ([c (in-list (string->list str))]
                            #:do [(define plaintxt-c (- (char->integer c) FIRST-INVISIBLE-CHAR))]
                            #:when (> plaintxt-c 0))
                            (integer->char plaintxt-c))))
(define (hide secret plain)
  (~> (string->bytes/utf-8 secret)
      (base64-encode #"") ; #""を使って改行を防ぐ
      (bytes->string/utf-8)
      (invis-encode)
      (string-append plain _)))
(define (unhide ciphertext)
  (~> (invis-decode ciphertext)
      (string->bytes/utf-8)
      (base64-decode)
      (bytes->string/utf-8)))
(module+ test
  (require rackunit)
  (define secret "this is a s3cret message. ssh")
  (define plaintext "Hey you, nothing to see here.")
  (define to-share (hide secret plaintext))
  (check-equal? (string-length to-share) 69) ; バイト数
  (check-equal? (string-grapheme-count to-share) 29) ; 実際に見えるグラフエムの数
  (check-equal? secret (unhide to-share)))

Ǎầầặắǎaạặậā ạẵẫȁẳẵạ ẫằ ȂẤĂẮ, ǎẩằaăạ a ǟȁǎăấ ǡặaȧăẵ ằẫȧ ạẵǎǡ ạǎạậặ ẳǎàặǡ ầặ ȂẤĂẮ, ǎạ ăẫȁậắ áặ ăẫǎẩăǎắặẩạaậ, áȁạ Ǎ ảẫȁậắ ầặẩạǎẫẩ ǎạ ǎẩ ạẵặ äaẳặ ǎằ Ǎ ảặȧặ āẫȁ.

ROT13を使ってたけど、ポスト量子の代替を探してたから、SCREAMに切り替えるつもり。キュービットは普通のラテン文字の上のちょっとしたクネクネした部分を表現したり、見分けたりできないって一般的に理解されてるからね。この重要な暗号学への貢献、ありがとう!

ChatGPTが母音の頻度を分析するだけで、全部解読できるか気になるな。そして、ネットでアルゴリズムを探そうとするかも。

ROT13を適用する際の重要なポイントは、繰り返しの回数だよね。偶数回の適用は間違いなく安全だし、奇数回の方がさらに良いんだ。今、分数回転の実装を作ってるところで、準備ができたらShow HNに投稿するつもりだよ。

ストリーム暗号がワンタイムパッド(「完全な秘密」を提供する)に最も近いってのが面白いよね。でも、これは全くセキュリティがないモノアルファベット置換暗号なんだ。

ストリーム暗号がブロック暗号(ほとんどの暗号がそれで作られてる)に比べて特に安全だって言いたいみたいだけど、それは違うよ。

これらのほとんどは短母音/長母音のマークだけど、最後のは(たぶん)インプロージョンだね。あとは前方/後方と広い/狭いA。スウェーデン語の「Å」はただのバカな「O」だよ。だって彼らは「O」を「U」と発音し、「U」を「Y」と発音し始めたから。-- これらの叫び声、発音できる?

最後のやつはオゴネクだね。普通は鼻母音を示すんだよ。

暗号化のロジックを書く必要はないよ。CIPHER, UNCIPHER = str.maketrans(CIPHER), str.maketrans(UNCIPHER) print(s := 'STREAM CIPHER'.translate(CIPHER)) print(s.translate(UNCIPHER))

いいね!!

Oのバリエーションでこれをやれば、ゴーストたちも喜ぶよ。