Re: DXアーカイブの脆弱性と改善提案 ( No.4 ) |
- 名前:管理人 日時:2018/07/31 00:21
> 8127さん
すみません、物凄く初歩的なことをお訊ねしてしまうのですが、
> ・ユーザーの入力した短いパスから1024byteのバイナリキーを得る方法として、MD5等の一方向ハッシュ関数を使う
MD5 というと 098f6bcd4621d373cade4e832627b4f6 のような 32文字のものをイメージするのですが、
どのようにして短いパスから 1024文字を得るのでしょうか?
> yumetodoさん
> に、鍵文字列の扱いについて追記したほうがいいのではないかと思います。共通鍵暗号の事前知識がないとDXArchiveの安全性を誤解してしまうように思います。
そうですね
本件の対応で多少は安全性が高くなると思うので、その度合いに応じて『専用のツールで簡単に解除されてしまいますので、
何もしないよりマシ程度です』のような表現にするか『ソフトを解析されたら解除されてしまいます』くらいの表現にするか決めたいと思います
> MD5とかSHA-1のような安全ではないものではなくSHA-256などを利用したいところ。
SHA-256 はファイル全体を鍵を使って暗号化するイメージなのですが、現在のDXアーカイブのように
ランダムアクセスも可能なように実装することはできるのでしょうか?
不勉強ですみません… m(_ _;m
|
Re: DXアーカイブの脆弱性と改善提案 ( No.5 ) |
- 名前:8127 日時:2018/07/31 00:42
遅くなりました。長文ですみません・・・
>yumetodo 様
>鍵文字列の扱いについて追記したほうがいいのではないかと思います。
完全に同意します。鍵文字列だけconstexpr関数とかで隠せばかなり安全性は高まりそうです。
(APIフックで鍵文字列の登録関数を乗っ取られたら無理ですが、まあそこまで対策しなくてもいいのではと思います)
>MD5とかSHA-1のような安全ではないものではなくSHA-256などを利用したいところ。
MD5は強衝突耐性が突破されたと言われ(wikipediaより引用)、これは証明書への利用時に悪用されるようです。(文書と偽文書のハッシュが同一になり改竄)
ですが、今回の用途にその安全性は必要でしょうか?
出力ハッシュ128byteのうち、どこかのbyteがばれた時(入力PWが短い文字列と仮定して)他のbyte列を推定するのはMD5ですら"困難"ではないでしょうか?
("困難"=パスワード総当たり以外の解法が無い)
>結局共通鍵なので、プログラム割って鍵を取り出すことが理論的に可能なので無意味かなと。
DXライブラリで作られたゲーム本体が生データにアクセスできる必要があるので、
そのアクセス方法をリバースエンジニアリングして真似ることで共通鍵に限らずどんな暗号化方法でも解析されてしまうと思います。
(公開鍵/秘密鍵を利用してもゲーム.exeに秘密鍵を埋めないといけなくなると思います)
なんか反論してますがdisる意図はありません・・・すみません
>管理人 様
実装の検討ありがとうございます。
MD5などの一方向ハッシュはそこそこ重いのでパフォーマンスの部分だけ心配です・・・(小さい画像に大量にアクセスする場合など)
同一のdata.dxaにまとめられている100個の画像00.png〜99.pngにアクセスする場合、xor処理や従来の12byteのキーの生成は100回行われるのでしょうか?
※私は暗号理論の専門家でもなんでもないので変なことを言っていたら誰かマサカリを投げてください・・・
|
Re: DXアーカイブの脆弱性と改善提案 ( No.6 ) |
- 名前:8127 日時:2018/07/31 00:43
同時に更新してしまったようなので読んでからまた投稿します。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.7 ) |
- 名前:8127 日時:2018/07/31 01:02
>管理人様
>MD5 というと 098f6bcd4621d373cade4e832627b4f6 のような 32文字のものをイメージするのですが、
まずお詫びですが、1024byteは長い文字の例で何の根拠もなく適当に言っただけで、
実際は安全性とパフォーマンスのトレードオフからよく検討した方がいいと思います。
>どのようにして短いパスから 1024文字を得るのでしょうか?
ユーザーが入力したPWをsとすると
・s+"適当な文字列"をMD5の入力に使い始めの32byteを得る ※"適当な文字列"はハードコーディングするので解析すればわかる値
・s+"適当な文字列2"をMD5の入力に使い次の32byteを得る
・・・
を(1024/32)回繰り返すのでいいと思います。
始めの32byteが、運悪く全部生データのゼロ部分に当たったせいでばれたとしても、さすがに「s+"適当な文字列"」を推定されるほどMD5は脆弱でないと信じたいです・・・
※MD5の"弱"衝突耐性はまだ破れてない ->「与えられたhash値に対し、それを返す入力を一つ見つけること」が不可能
-> ならば、s+"適当な文字列"を割ることは十分(数学の用語)不可能であろう、という議論です(wikipediaのMD5と暗号学的ハッシュ関数を参照)
|
Re: DXアーカイブの脆弱性と改善提案 ( No.8 ) |
- 名前:名無し 日時:2018/07/31 15:42
xorキーにHash関数("パスワード" + "ファイル名")を使い暗号化するのはどうでしょう。
Hash値から元パスワードは逆算できないので、ヘッダーからHash値が逆算されても別Hash値を使っている他のファイル暗号化を解くことはできません。
当然パスワードを知っていればファイル名もわかるので合わせてHash値を計算でき復号できます。
速度もファイルごとに文字結合一回とHash計算一回増えるだけなので大した影響がなく
xorキーを長くする、ヘッダーの暗号化をやめるなど他へ悪影響の出る手段もとらなくて済みます。
Hash関数はSHA256やMD5のどちらでもいいかと(どちらにしろパスワードは隠し通せないわけですし)
とはいえ、8127さんの発言を見る限りオリジナルHash関数のようなので
Hash値からパスワードが逆算されてしまったり、Hash値への演算でファイル名部分を後から付け足せると問題なので
オリジナルHash関数はやめてせめてMD5にした方が無難そうです(個人的にはSHA256がお勧めです)
|
Re: DXアーカイブの脆弱性と改善提案 ( No.9 ) |
- 名前:8127 日時:2018/07/31 23:17
名無し様、
Hash関数("パスワード" + "ファイル名")で暗号化するのはとても良い方法だと思います。
("ファイル名"は"folder/42.png"みたいなものを指すと解釈しました。)
問題はファイルごとに違うキーになることがDxlibの実装の都合上、面倒でないかですね。
速度は調べたところ、
MD5(16byte)・・・1とする ※32文字は16進数で表したとき
sha1(20byte)・・・1.2くらい
sha2系統(32byte等)・・・2.2くらい
ほどのようです。
出力byteは長い方が安全上いいので効率上あまり変わらないとおもいます(ならsha2がいい)
|
Re: DXアーカイブの脆弱性と改善提案 ( No.10 ) |
- 名前:管理人 日時:2018/08/01 01:53
> 8127さん
> 同一のdata.dxaにまとめられている100個の画像00.png〜99.pngにアクセスする場合、xor処理や従来の12byteのキーの生成は100回行われるのでしょうか?
xor処理は毎回行いますが、12byteのキーの生成はDXアーカイブファイルを開いたときの1回だけです
> ユーザーが入力したPWをsとすると
> ・s+"適当な文字列"をMD5の入力に使い始めの32byteを得る ※"適当な文字列"はハードコーディングするので解析すればわかる値
> ・s+"適当な文字列2"をMD5の入力に使い次の32byteを得る
> ・・・
> を(1024/32)回繰り返すのでいいと思います。
ご解説ありがとうございます、理解できました
こちらは "パスワード" + "ファイル名" を使用する場合も 1024byte 分くらい Hashの長さがあった方が良いでしょうか?
しかし、こちらが長ければ長いほどHash値の計算に時間が掛かってしまうのですよね…
> 問題はファイルごとに違うキーになることがDxlibの実装の都合上、面倒でないかですね。
そこまで面倒ではないと思いますが、現状より負荷は間違いなく高くなるので、それがどのくらいの
処理負荷増になるかが気になるところです…
> 名無しさん
ご提案ありがとうございます
> xorキーにHash関数("パスワード" + "ファイル名")を使い暗号化するのはどうでしょう。
> Hash値から元パスワードは逆算できないので、ヘッダーからHash値が逆算されても別Hash値を使っている他のファイル暗号化を解くことはできません。
> 当然パスワードを知っていればファイル名もわかるので合わせてHash値を計算でき復号できます。
なるほど、現在の解析方法( ヘッダー内の固定値を利用して Hash値を探る )でヘッダー部分のHash値が仮に逆算できたとしても
得られるのはヘッダーとファイル名のリストなどだけで、パスワードが分からない限りはファイル本体の暗号化を解くことはできないというわけですね…
これなら『せめてソフト毎にexeを解析しない限りはdxaの中身を見られないようにしたい』を実現できそうです
> 速度もファイルごとに文字結合一回とHash計算一回増えるだけなので大した影響がなく
> xorキーを長くする、ヘッダーの暗号化をやめるなど他へ悪影響の出る手段もとらなくて済みます。
"パスワード" + "ファイル名" で SHA256 を使用した場合はひとつの Hash値が 64byte ですが、求められた
Hash値とファイルのデータを xor する( 64byte毎に xorされる値はループする )、で十分でしょうか?
> とはいえ、8127さんの発言を見る限りオリジナルHash関数のようなので
現在の Hash関数は1バイト毎にシフトやNot演算をしている程度で各byteの前後の絡みが無く
『ヘッダーから Hash値が割り出される=パスワードがバレる』となっている( バイナリエディタや
メモ帳で開いただけではパスワードの文字列がバレない程度 )ので
必ず Hash関数は SHA256 等のちゃんとしたものに変更します
|
Re: DXアーカイブの脆弱性と改善提案 ( No.11 ) |
- 名前:名無し 日時:2018/08/01 11:10
>"パスワード" + "ファイル名" で SHA256 を使用した場合はひとつの Hash値が 64byte ですが、求められた
>Hash値とファイルのデータを xor する( 64byte毎に xorされる値はループする )、で十分でしょうか?
十分だと思います。
というよりは元と合わせて12byteまで削ってもいいぐらいかと思います。
パスワード解析難度>パスワードなし展開難度なのが問題と理解しているので、不等号さえ逆にできれば難易度そのものは最低限でいいはず。
もちろんパスワード解析難度自体も向上させる予定があるなら別ですが、たぶん無理でしょうし。
Hash関数も数学の問題を解くとまた不等号が逆転する可能性を危惧しているだけなので、それらに自信を持てるならもっと低級で高速なものでも問題ないです。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.12 ) |
- 名前:8127 日時:2018/08/01 12:19
>管理人様
>こちらは "パスワード" + "ファイル名" を使用する場合も 1024byte 分くらい Hashの長さがあった方が良いでしょうか?
以前私が提案した方法(1024byte等の長い共通のキーでxor)は、自分で脆弱性を見つけてしまった(・・・)ので使わないでください。
同人ゲームには利用したフリー素材のクレジットがついていますが、それを使って.dxaに含まれている素材をwebで見つけDLし、
.dxa内での位置を一つずつ変えてxorを取りながらxorの結果が1024byteでループする位置を探すことで、.exeを解析せずにキーを割ることができます。
要するに、.dxaに含まれる素材が1つ分かるだけで全部を取り出せてしまうのです。
追加で要望ですが、もしsha256やMD5を実装するのでしたらユーザー側も使えるように関数追加していただくとありがたいです。
(セーブデータのチェックサムなどに需要があると思われる)
void HashSha256(const void *buf, int size);
>名無し様
>というよりは元と合わせて12byteまで削ってもいいぐらいかと思います。
もし5byteくらいまで削ってしまうと、.pngのヘッダに必ず含まれるバイナリ(8byte:0x89504E470D0A1A0A)を使って上記の方法で全部の.pngを抜き出せると思います。
12byteならまあ安全かと思いますが、個人的には安全を取ってsha256の出力32byteをそのまま使うことを推したいです。
MD5の16byteでも十分だと思います。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.13 ) |
- 名前:管理人 日時:2018/08/02 01:46
> 名無しさん
> >Hash値とファイルのデータを xor する( 64byte毎に xorされる値はループする )、で十分でしょうか?
>
> 十分だと思います。
> というよりは元と合わせて12byteまで削ってもいいぐらいかと思います。
ご返答ありがとうございます
一応 8127さんのご指摘にあるような危険性がありそうなので 32byteの長さで xor しようと思います
> 8127さん
> 以前私が提案した方法(1024byte等の長い共通のキーでxor)は、自分で脆弱性を見つけてしまった(・・・)ので使わないでください。
> 同人ゲームには利用したフリー素材のクレジットがついていますが、それを使って.dxaに含まれている素材をwebで見つけDLし、
> .dxa内での位置を一つずつ変えてxorを取りながらxorの結果が1024byteでループする位置を探すことで、.exeを解析せずにキーを割ることができます。
> 要するに、.dxaに含まれる素材が1つ分かるだけで全部を取り出せてしまうのです。
この方法なら "パスワード" + "ファイル名" で得られる 32byte の Hash値で xor してもフリー素材のファイルは復元されるのでは?
と思ったのですが、ファイル毎に Hash値が異なれば既に元ファイルが分かっているフリー素材ファイルが dxa内から得られるだけで
他のファイルは復元できないというわけですね( 全部フリー素材でしたら全部得られますが既に手元にある生ファイルと同じものが dxa内から
得られるだけで全く意味がない )
Hash値を得る処理の負荷はまだ測っていませんが、1ファイルにつき 32回計算するよりはかなり処理が少ないので現実的な
処理負荷になる気がします
> 追加で要望ですが、もしsha256やMD5を実装するのでしたらユーザー側も使えるように関数追加していただくとありがたいです。
> (セーブデータのチェックサムなどに需要があると思われる)
> void HashSha256(const void *buf, int size);
了解です、検討してみます
8127さん、名無しさん、ご提案から具体的な処理内容についてのご相談までさせていただきありがとうございます m(_ _)m
早速作業に取り掛かりたいのですが、ここにきて更に忙しさに拍車が掛かってきたので、最速でも作業を始められるのは
8月11日以降となりそうです…
申し訳ありませんが1週間強( 一回の週末で終わらなければ2週間以上 )ほどお待ちください m(_ _;m
|
Re: DXアーカイブの脆弱性と改善提案 ( No.14 ) |
- 名前:8127 日時:2018/08/02 11:14
管理人様、
ご返信ありがとうございます。
気長に待っています。
>ここにきて更に忙しさに拍車が掛かってきたので
ぜひお体にはお気を付けください。管理人様あってこそのDxlibですし。
あとHashSha256の返り値はvoidではないです・・・
構造体を値返しするか(cでは返り値の直接構築とかない?ので遅くなる?)、代入するアドレスを引数で与えるかはお任せします。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.15 ) |
- 名前:管理人 日時:2018/08/03 02:55
> ぜひお体にはお気を付けください。管理人様あってこそのDxlibですし。
お気遣いありがとうございます
無理はしないようにします
> あとHashSha256の返り値はvoidではないです・・・
言及していただきありがとうございます
No.12 のお書き込みの戻り値が void だったので何かカラクリがあるのかと少し考えてしまいました (^ ^;
> 構造体を値返しするか(cでは返り値の直接構築とかない?ので遅くなる?)、代入するアドレスを引数で与えるかはお任せします。
恐らく Hash値の 32byteを代入するメモリ領域の先頭アドレスを引数として渡す方式にすると思います
|
Re: DXアーカイブの脆弱性と改善提案 ( No.16 ) |
- 名前:yumetodo 日時:2018/08/03 03:19
ifdefでくくるなどして、例の
gist.github.com/yumetodo/238c52d4382db93e1978743cf299ba4d
のようなconstexpr関数をDxLib側に入れてしまう(=DxLib.hに記述or読み込まれる)することは可能でしょうか?
このコードはBoost Software Licenseにしてますが、もしDxLibに入れるのに必要ならCC0に変更します。
gcc/clangは言うまでもなく、VSもVisual Studio 2015以降ならconstexprが使えるので、
bcc32/bcc32c.exeとかいうゴミはさておき、多くの環境で利用できるのではないかなと思います。
constexprがない環境向けにもコードほぼ変えずに動くように先程書き換えたので(実行時に暗号化するというただのカカシ)、
templateが使えれば動くかなという感じです。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.17 ) |
- 名前:名無し 日時:2018/08/03 05:22
そのinferior_encrypted_stringは特定レジスタに31を加算する機械語(add BYTE PTR [ecx],0x1fなど)が吐かれるのですが
これはかなり特徴的で、手元で数アプリ調べた範囲だと出現数0でした。
なので技術力が少しあれば解析の基点に出来ます、自動抜き出しも作れるでしょう。
それらを踏まえた評価としてはバイナリエディタで眺める解析対策程度にしかならず、お守りよりは良い程度と考えられます。
上記について承知のうえで入れるとの判断であれば異議を唱える気はありませんが
windows以外にも輸出されうるわけですし、#ifを並べるコードはメンテナンスコストの上昇が無視できない可能性があります。
その場合無理をしてまで入れるべきかというと自分は懐疑的ですね。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.18 ) |
- 名前:8127 日時:2018/08/03 15:18
"パスワード" + "ファイル名" で得られる 32byteのHash値を繰り返してxorするよりは、
Hash値をseedとするメルセンヌツイスターを使って次のxorキーを得ると周期性が消え、既知平文攻撃も効かなくなります。
当然相応に重たくなります。(もしファイルアクセス時間>>復号化時間 なら問題にならないはずです)
>>そのinferior_encrypted_stringは特定レジスタに31を加算する機械語(add BYTE PTR [ecx],0x1fなど)が吐かれるのですが
複数の対策があると思います。
・レジスタ演算で最適化されないようにする(私の技術が足りないのでconstexpr関数で最適化抑制ができるかを知らない・・・)
・31の部分をユーザーがテンプレートで渡せるようにする(もちろん、文字コード的にダメな数字はstatic_assertで弾く感じで)
まあ、どんなに強い対策をしても.exeがある以上原理的には解析を免れないので、どこまでやるかは管理人様にお任せします。
あと、私もハマリそうになったので、以下の文章を目立つ場所(DXアーカイブの説明書など)に記載した方がいいと思います。
「以前のアーカイブからはパスワードが逆算される可能性が高いので、新しいアーカイブを使う場合は、以前と暗号化パスワードを変えてください。」
|
Re: DXアーカイブの脆弱性と改善提案 ( No.19 ) |
- 名前:名無し 日時:2018/08/03 16:45
メルセンヌツイスターは2KBほど出力を観察すると全ての乱数列を計算することが可能です(※暗号用途ではない疑似乱数なため
xorなので厳密に2KBで問題になることは少ないでしょうが、既知平文攻撃や周期性という方向では大した意味を持たないでしょう。
それらの懸念をするのであればAES-GCMなど正規の暗号化アルゴリズムを使うべきです。
しかしこれらは低速ですし、管理人様の実装やメンテナンスのコストが高く
ソースコード公開済みかつ秘匿情報も持てないために初歩的な解析で突破されるのは変わらず割に合いません。
解析対策とは費用対効果を考えて行うべきもので、ただ対策を積み重ねればいいわけではないからです。
なので自分はメルセンヌツイスターなどについてははっきりと反対です。
----
>・レジスタ演算で最適化されないようにする(私の技術が足りないのでconstexpr関数で最適化抑制ができるかを知らない・・・)
コンパイラの最適化処理は頻繁に動作が変わるためメンテナンスが大変なので止めた方が無難かと。
>・31の部分をユーザーがテンプレートで渡せるようにする(もちろん、文字コード的にダメな数字はstatic_assertで弾く感じで)
何を渡すべきかを正しく判断できる人間ばかりとは限りません。
例えばどの数字が大丈夫でどの数字がまずいかを全てはっきりと答えられる人間は(私も含めて)この場にはいないでしょう。
そもそもその数字の意味が分からず、サンプルをコピペするだけの開発者の方が圧倒的に多いはずです。
一般的な開発者のことを考えればその対策は避けるべきです。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.20 ) |
- 名前:8127 日時:2018/08/03 21:38
名無し様、
ご返信ありがとうございます。
>既知平文攻撃や周期性という方向では大した意味を持たないでしょう。
メルセンヌツイスターについて調べてみましたが完全にその通りです。私の知識がありませんでした・・・
管理人様の実装コストが増える割に得るものは小さいと思うので、
私も名無し様と同様メルセンヌツイスター等は不要と考えます。
>例えばどの数字が大丈夫でどの数字がまずいかを全てはっきりと答えられる人間は(私も含めて)この場にはいないでしょう。
(もうこの議論は不要かもしれないですが一応)
0〜31なら大丈夫でそれ以外はダメです。(0は暗号文=平文になので実質1〜31)
|
Re: DXアーカイブの脆弱性と改善提案 ( No.21 ) |
- 名前:管理人 日時:2018/08/04 01:55
> yumetodoさん
> ifdefでくくるなどして、例の
> gist.github.com/yumetodo/238c52d4382db93e1978743cf299ba4d
> のようなconstexpr関数をDxLib側に入れてしまう(=DxLib.hに記述or読み込まれる)することは可能でしょうか?
yumetodoさんも #if などで対応されている通りまだ安定していない機能で、今後も新環境や新バージョンで
記述の仕方に変化がある可能性があるので( あと PS4/PSVita/NintendoSwitch の対応状況も調べないといけない… )
DXライブラリには入れず、ミニテクニックコーナーでの紹介になると思います
> 名無しさん、8127さん
メルセンヌツイスターのご情報や inferior_encrypted_string についてのご見解ありがとうございます
とりあえず https://dxlib.xsrv.jp/cgi/patiobbs/patio.cgi?mode=view&no=4371 の No.2 の私の考えたコードと
( こちらは 0x32 の値が 0x1f の誤りというバグがありますが ) yumetodoさんのコードをバイナリエディタ対策の
方法として紹介する記述をミニテクニックコーナーに追加しようと思います
|
Re: DXアーカイブの脆弱性と改善提案 ( No.22 ) |
- 名前:ギウ 日時:2018/08/04 13:28
(横から失礼します)
私はdxaを使ってないので勘違いがあるかもですが、
1)先に自前の暗号化ソフトでファイルを暗号化
2)dxaでそのファイルを圧縮
3)ゲーム側で、dxaから展開後に任意のコールバックを呼んでもらう
※DXライブラリ側は解凍後に「予め設定されたコールバックを呼ぶ処理を追加する」だけ。
とすれば、管理人様の負担を減らしつつ、各使用者ごとに好きな暗号化(速度重視とか安全性重視とか)できて良いのではと思いました。
|
Re: DXアーカイブの脆弱性と改善提案 ( No.23 ) |
- 名前:コーラ 日時:2018/08/04 19:54
個人的な意見ですが、
オープンソースに安全性を担保してもらうというのはちょっと間違っているかなと思います。
こういうのは程度の問題で結局いたちごっこになってしまうので
しっかり暗号化をかけたいという人には自前で暗号化をかけてもらって
安全性の担保は利用者側で行ってもらうという扱いにした方が良いと思いました。
DXArchiveSetMemImage()という関数を使えば自前で好きな暗号化をかけることが出来ると思います。
|