Z会コラボのサマーウォーズの暗号を解いてみた

サマーウォーズの劇場公開から15周年!健二が解いた“あの問題”に挑戦!「Solve Me」キャンペーンの問題を解いてみました。
問題はこちら:

10ケタの数字が5行。
画像の下部の「https://www.zkai.co.jp/???_????_????_???_?????」にアクセスすれば答えが得られる様子。
この問題を解読するには二つのアプローチがあります。
- 数学的アプローチ
- ハッキング的アプローチ
数学的アプローチ
数字を2ケタで区切ると最大の数字がアルファベットの文字数と同じ26なので、単純なROT13と思いました。pythonで適当にコードを書いて、一通りシフトしてみましたがそれっぽい答えは得られず。
ヒントを見てみると、アルファベットと数字の対応表と【暗号化ルール】y=mod(ax+b,27)とありました。
それと、意味深な日時。06.27 18:00 06.28 12:00 06.28 18:00これは次のヒントの発表日時ですね。
これだけではいまいち分からないので、もう一つのヒント:
「AI」を暗号化すると「ZA」
これでひらめきました。
y=mod(ax+b,27)のaとbを求める必要があります。
「AI」を「ZA」に変換するには、以下のような変換が行われたはずです:
- 01 → 26 (A → Z)
- 09 → 01 (I → A)
式にすると:
{mod(a⋅1+b,27)=26
mod(a⋅9+b,27)=1
a+b≡26(mod27)
よって、b≡26−a(mod27)
次に、下の方程式を代入して a を求めます:
9a+b≡1(mod27)
ここに b=26−aを代入すると:
9a+(26−a)≡1(mod27)
解くと:
8a+26≡1(mod27)
8a≡−25(mod27)
8a≡2(mod27)(27−25=2)
a を求めるためには、8の逆元を求めます。逆元は、8と27の間で最小の自然数 k を使って
8k≡1(mod27)
拡張ユークリッドのアルゴリズムで解けます。
8の逆元は、8−1≡17(mod27)
これを使うと、
a≡2⋅17(mod27)
a≡34(mod27)
a≡7(mod27)
よって、a=7,b=19 です。
次のpythonコードで解読できます:
def decrypt(y, a=7, b=19, mod=27):
a_inv = pow(a, -1, mod)
x = (a_inv * (y - b)) % mod
return x
def num_to_char(num):
if num == 0:
return '_'
else:
return chr(num + ord('a') - 1)
message = [
[24, 21, 0, 19, 2],
[26, 14, 1, 13, 19],
[18, 16, 10, 20, 17],
[19, 26, 10, 0, 19],
[2, 1, 10, 26, 1]
]
for row in message:
for num in row:
decrypt_num = decrypt(num)
print( num_to_char(decrypt_num) ,end='' )
https://www.zkai.co.jp/the_magic_words_are_miraiで解読成功ページにアクセスできます。

ハッキング的アプローチ
Z会のHPのソースを見てみると、YoastSEOプラグインのタグやwp-contentなどのディレクトリ構造からWordPressで構築されている事が分かります。

WordPressにはREST APIが用意されており、エンドポイントにリクエストを送ることで記事の取得が可能です。
YoastSEOでnoindex,nofollowに設定してもREST APIからは削除されません。
次のエンドポイントで固定ページの一覧が取得できます。
https://www.zkai.co.jp/wp-json/wp/v2/pages
取得した情報の中に解読成功ページのURLがあります。
https://www.zkai.co.jp/the_magic_words_are_mirai/

REST APIから固定ページを10件取得するpythonコード:
import requests
wordpress_url = 'https://www.zkai.co.jp'
endpoint = f'{wordpress_url}/wp-json/wp/v2/pages'
response = requests.get(endpoint)
if response.status_code == 200:
pages = response.json()
for page in pages:
title = page['title']['rendered']
url = page['link']
print(f'Title: {title}\nURL: {url}\n')
else:
print(f'Failed to retrieve pages: {response.status_code}')
出力:
Title: 解読成功サイト
URL: https://www.zkai.co.jp/the_magic_words_are_mirai/
Title: 解読成功おめでとう!
URL: https://www.zkai.co.jp/deciphering-completed/
Title: Z会グループ|『サマーウォーズ』15周年特別コラボ
URL: https://www.zkai.co.jp/home/cm/summerwars15th/
Title: 2024年7月1日更新:個人情報の開示等の手続きについて
URL: https://www.zkai.co.jp/home/policy/20240701detail/
Title: 2024年7月1日更新:【株式会社増進会ホールディングス】 個人情報保護方針、個人情報の取り扱いについて
URL: https://www.zkai.co.jp/home/policy/20240701zoshinkai/
Title: 2024年7月1日更新:【株式会社Z会ソリューションズ】 個人情報保護方針、個人情報の取り扱いについて
URL: https://www.zkai.co.jp/home/policy/20240701zs/
Title: 2024年7月1日更新:【株式会社Z会エデュース】 個人情報保護方針、個人情報の取り扱いについて
URL: https://www.zkai.co.jp/home/policy/20240701educe/
Title: 2024年7月1日更新:【株式会社Z会】 個人情報保護方針、個人情報の取り扱いについて
URL: https://www.zkai.co.jp/home/policy/20240701privacy/
Title: 公民連携推進事業
URL: https://www.zkai.co.jp/home/koumin/
Title: 新・Z会専用タブレット(中高生対象)
URL: https://www.zkai.co.jp/tablet-chuukou/spec/
おわり
金曜ロードショーで見るたびに涙出そう。