翁子皓 2016 / 6 / 2
Bcrypt has P-box and S-boxes with about 4 KB of size,
constantly accessed and modified throughout the algorithm.
To run Bcrypt on GPU, each core will have to store it in the GPU main RAM. They will compete for usage of the memory bus. So Bcrypt can't run on GPU with full parallelism.
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
$2a$ : Bcrypt 10 : rounds
N9qo... : base64 encoded salt
IjZAg... : base64 encoded hash
Uses 1 P-box and 4 S-boxes each round
re-calculates P-box and S-box each round
typedef struct {
unsigned int pbox[256];
unsigned int sbox[4][256];
} blowfish_context_t;
void encryptblock(blowfish_context_t *ctx, unsigned int *hi, unsigned int *lo)
{
int i, temp;
for(i = 0; i < 16; i++) {
*hi ^= ctx->pbox[i];
*lo ^= BLOWFISH_F(*hi);
temp = *hi, *hi = *lo, *lo = temp;
}
temp = *hi, *hi = *lo, *lo = temp;
*lo ^= ctx->pbox[16];
*hi ^= ctx->pbox[17];
}
#define BLOWFISH_F(x) \
(((ctx->sbox[0][x >> 24] + \
ctx->sbox[1][(x >> 16) & 0xFF]) ^ \
ctx->sbox[2][(x >> 8) & 0xFF]) + \
ctx->sbox[3][x & 0xFF])
void initiate(blowfish_context_t *ctx, unsigned char *key)
{
// first initialize pbox, sbox from PI
int i, j, k;
for(i = 0, j = 0; i < 18; i++) {
//XOR P with key treating the key as cyclic
ctx->pbox[i] ^= key;
}
unsigned int hi = 0, lo = 0;
for(i = 0; i < 18; i += 2) {
blowfish_encryptblock(ctx, &hi, &lo);
ctx->pbox[i] = hi;
ctx->pbox[i + 1] = lo;
}
for(i = 0; i < 4; i++) {
for(j = 0; j < 256; j += 2) {
blowfish_encryptblock(ctx, &hi, &lo);
ctx->sbox[i][j] = hi;
ctx->sbox[i][j + 1] = lo;
}
}
}
bcrypt(round, salt, input)
state = EksBlowfishSetup(round, salt, input)
ctext = "OrpheanBeholderScryDoubt"
repeat (64)
ctext = EncryptECB(state, ctext)
return Concatenate(round, salt, ctext)
EksBlowfishSetup(round, salt, key)
state = InitState()
state = ExpandKey(state, salt, key)
repeat (1 << round)
state = ExpandKey(state, 0, key)
state = ExpandKey(state, 0, salt)
return state
expensive key schedule Blowfish
ExpandKey(state, salt, key)
for(n = 1..18)
Pn = key ^ Pn //treat the key as cyclic
ctext = Encrypt(salt[0..63])
P1 = ctext[0..31]
P2 = ctext[32..63]
for(n = 2..9)
//encrypt using the current key schedule and treat the salt as cyclic
ctext = Encrypt(ctext ^ salt)
P2n-1 = ctext[0..31]
P2n = ctext[32..63]
for(i = 1..4)
for(n = 0..127)
ctext = Encrypt(ctext ^ salt)
Si[2n] = ctext[0..31]
Si[2n+1] = ctext[32..63]
return state
Modified Blowfish key schedule
Concatenate(round, salt, ctext)
// base64_encode(salt): 22 chars
// base64_encode(ctext): 31 chars
// total: 60 chars
return "$2a$" + base64_encode(salt) + base64_encode(ctext)
Put everything together