bcrypt
パスワードは最長72文字しか使えないので高度?
パラメータ
cost 繰り返し数のビット長 2^cost 回
password 72文字(576bit)まで有効 \0を付加する
出力
2a utf-8対応 または 2b バグ修正が比較的最新
cost 入力と同じ
salt 128bit のtext符号化 22文字程度 ( 128 / 6 )
checksum 192bitから8bit減らして符号化、なぜ減らした?
$2b${cost}${salt}{checksum}
Blowfish の初期化(expandKey)をsaltを利用できるように拡張する
encrypt の前でXOR salt を加える saltの位置は128bitあるので前64bit, 後64bit を使う度に入れ換える
salt付きのexpandKey実行後、1 << cost 回の { expandKey(key); expandKey(salt); } で攪拌する
Bin は net.siisise.lang.Bin で配列のXORなどができる
code:Blowfish.initBcrypt
void initBcrypt( int cost, byte[] salt, byte[] keyz ) {
initCipher(); // 配列の初期化
expandKey(salt, keyz);
long rounds = 1l << cost;
for ( long l = 0; l < rounds; l++) {
expandKey(keyz); // expandKey(new byte16, keyz) と同じ expandKey(salt); // expandKey(new byte16, salt) と同じ }
}
code:Blowfish:expandKey(salt,key)
void expandKey(byte[] byteSalt, byte[] keyz) {
int[] salt = Bin.btoi(byteSalt); // int4の列に変換する // P-indexの攪拌
int keyIndex = 0;
for (int i = 0; i < 16 + 2; i++) {
int v = 0;
for (int j = 0; j < 4; j++) { // 4バイトを32bitに
keyIndex %= keyz.length;
}
}
//
int[] d = new int2; // l と r だったもの for (int i = 0; i < 16 + 2; i += 2) {
Bin.xorl(d, salt, i % 4, 2); // salt の high と low を交互に d に XORする
d = encrypt(d);
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 256; j += 2) {
Bin.xorl(d, salt, (j + 2) % 4, 2); // 奇数個の続きなので後ろから
d = encrypt(d);
}
}
}
code:Bcrypt.encode
String encode(int cost, byte[] salt, String pass) {
// BlowfishのBcrypt用初期化
Blowfish fish = new Blowfish();
byte[] bytePass = pass.getBytes(StandardCharsets.UTF_8);
byte[] bytezPass = Arrays.copyOf(bytePass, bytePass.length + 1); // \0 追加
fish.initBcrypt(cost, salt, bytezPass); // 鍵の設定っぽいもの
String ctext = "OrpheanBeholderScryDobt"; // 3block
int[] btext = Bin.btoi(ctext.getBytes(StandardCharsets.UTF_8)); // 適度にASCIIっぽいbyte列をintに変換
for (int i = 0; i < 64; i++ ) { // cost, salt, passの設定されたBlowfish ECB modeで64回暗号化
btext = fish.encrypt(btext);
}
// BASE64っぽいもので btext をbyte列に戻して1バイト減らした23バイトをエンコードする
String checksum64 = BASE64.BCrypt(Bin.itob(btext), 0, 23);
String salt64 = BASE64.BCrypt(salt);
return "$2b$" + cost + "$" + salt64 + checksum64;
}
BASE64の亜種で変換する。MCF共通なのかどうか不明だが古いcrypt系の並びとは違ったのでBASE64.BCrypt を作っておく。 table:BASE64亜種
BASE64 Aa0+/ =
URL Aa0-_
BCrypt ./Aa0
PASSWORD crypt(旧) ./0Aa =
HEX64 0aA-_
BASE64亜種の並び順はこんな感じ