Open5

Javaで暗号全部実装してみる?

おこめおこめ

DES,AES,RSA,SHA-1,2,3くらいを実装した
AES実装で気になった点 long列で扱うとbyte列で扱うより速い
ビット演算まとめてできる機能がないのかも

おこめおこめ

SHAKE128,SHAKE256は可変長出力で、SHA-3で出力できない? サイズのハッシュを自由に出力できる。
アルゴリズムはKeccakで同じ。長さのパラメータが必要で、内部的には初期化のビット列が少し違う。
RawSHAKE128,RawSHAKE256はKeccakの拡張用の元になる感じのものでSHA-3系と区別されるビット列が足されている。これにさらに足していくとSHAKE128,SHAKE256になる。
cSHAKE128/cSHAKE256 もKeccak 系のアルゴリズムを使用。
長い結果を出力するときはKeccakのスポンジ構造を利用することになる。
SHAKEもcSHAKEも元のデータには長さの情報がないので長さを変えても基本的な出力は変わらず後に出力が足されていく。これをXOFとしてハッシュとちょっと違う感じでまとめている。
SHAKE128,SHAKE256から拡張され、N のファンクション名とS のバリアントを指定することができる。この2つが入ることで同じデータでも目的が違えば別の値が出力される。NとSの両方が省略されるとSHAKE128,SHAKE256と同じ結果を出力する。NとSはビット列で指定可能。

おこめおこめ

ガロア体の実装
AES、GCMなどでガロア体や有限体が出てくるので作る
仕組みはいろいろ難しいがビット演算的なところ
0以外がぐるっと一周するような仕掛けになっている計算方法で、乱数っぽいので暗号によく使われる
AESでは8bit, GCMなどでは128bitが使われたりする。32bitなどもあり。
足し引きと掛け算を主に使えればよく、足し算引き算はXORで計算して桁上がりしないため結果は同じになる。掛け算はビットシフトの組み合わせで何とかなり、桁あふれの処理が特殊。
8bitでは素数的なものをa たとえば3、a^b でbが254ぐらいまでで数値が一周する。
aとbの組み合わせで処理できる場合は a^b ・ a^c 的なものは a^{b+c} というような計算もできる。
割り算はできるかというと微妙に難しい。逆数を計算して掛け算に持っていくという方法で計算はできる。a^bのようなのが把握できている場合は引き算でもよい。
GCMではビット列の順が逆になっているので気づかないといつまでも完成しない。

おこめおこめ

今では少し古くなったもののまだ使われていそうなRSA PRIVATE KEY の暗号解除法、AES対応の解説があんまり見かけないので詳細を調べてみた。
Proc-Type: 4,ENCRYPTED になっている形。3もなくはないが今はない。
基本的な形はPKCS #5 v1.5 の PBKDF1で繰り返しが1回。DESならこれでよかった。
DEK-Info に暗号モードと16進のIVが書かれている。
PKCS #5 のPBKDF1でハッシュ関数がMD5、saltとストレッチ回数1回、パスワード、出力サイズが必要。
PBES1ではアルゴリズム対応がちょっと足りない。
128bitのAESではDEK-Infoのsaltっぽいものも128bitになっているが、OpenSSLのPBKDF1の入力に使うのはIVの先頭64bitなので全部突っ込むと正しい結果が出てこなかった。
素のPBKDF1ではハッシュ長を超える出力ができない。OpenSSLではこれも拡張されている。
鍵導出関数 PBKDF1のOpenSSL風なのが次のような感じ。

int dkLen = keyバイト長
MD md = MD5
byte[] salt = IV の先頭64bit
byte[] dk = 出力先
do {
md.update(pass);
t = md.digest(salt); // salt はIVの先頭64bit
// c = 1 なのでループ省略
dk = dk + t; // という概念
md.update(t); // 次へ渡し
} while (dk.length < dkLen); // OpenSSLでは長さ制限なし
key = dkからdkLenを切り出し

AES-256-CBC なら128bit x 2 でループ2回
key と IV (128bit)をブロック暗号に渡して本文をどうこうすればそれっぽい結果が返ってくる。ブロック暗号系アルゴリズムの場合はPKCS7Paddingも必要。

https://github.com/okomeki/SoftLibCrypto/blob/master/src/main/java/net/siisise/ietf/pkcs5/OpenSSLPBKDF1.java

おこめおこめ

OpenBSD系かな BCrypt と Blowfish を実装。Blowfishはブロック暗号。BCryptはBlowfishを利用するパスワード暗号化。
Blowfishは448bitまでの鍵が利用可能とされているがBCryptなどでは72文字(576bit)まで利用している。
円周率を元にした内部状態PとS的な配列を鍵で攪拌して、そのPとSの表を使ってDSAと似た感じで左右入れ換える形で暗号化するような形式。
IETFのInternet DraftからBlowfishを実装してみたが、いろいろ間違っているようなので他の資料を探して直していく必要があった。テストベクターは正確。

BCryptはパスワード用に定番のカウントcostと乱数saltを持っている。が癖も多いかもしれない。Blowfishにsaltを含めた初期化の処理を追加する。最初からsalt込みでBlowfish作ってみてもいいか。
BCryptのsaltは128bit固定。パスワードを暗号化するのではなくBlowfishの鍵として利用する。
定型文を繰り返し暗号化する形なので出力からパスワード(Blowfishの鍵)を復号することはできない構造。
costは直接くり返す数ではなく、2^costでroundsに変換する。12なら4096回のループ。31まで対応している。
BCryptで入力するパスワードは\0終端も含める形で利用する。
出力はhashといえばhashか? 入力サイズに制限があるのでchecksumぐらいなのか?
出力はshadowパスワードなどの共通形式Modular Crypt Formatで2 2a 2b などのバージョンの形式。
2aでUTF-8パスワード対応、2bで何かのバグ修正。
2b{cost}${salt}{checksum} でバイナリのsaltとchecksumはBASE64亜種でテキスト化する。checksumは1バイト減らしてから出力する謎。saltとパスワードからchecksumが生成できるので照合も簡単。

https://scrapbox.io/standard/Blowfish
https://scrapbox.io/standard/bcrypt
https://github.com/okomeki/SoftLibCrypto/blob/master/src/main/java/net/siisise/security/block/Blowfish.java
https://github.com/okomeki/SoftLibCrypto/blob/master/src/main/java/net/siisise/security/key/BCrypt.java