Bcrypt

翁子皓 2016 / 6 / 2

Design

  • key-dependent S-box
  • highly complex key schedule
  • pre-process about 4 KB of text

Limit

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.

Format

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
$2a$ : Bcrypt
10 :   rounds
N9qo... : base64 encoded salt
IjZAg... : base64 encoded hash
2^{10}
2102^{10}

Algorithm

Blowfish

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])

Initalize

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

bcrypt(round, salt, input)
  state = EksBlowfishSetup(round, salt, input)
  ctext = "OrpheanBeholderScryDoubt"
  repeat (64)
    ctext = EncryptECB(state, ctext)
  return Concatenate(round, salt, ctext)

Overview

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

EksBlowfishSetup

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

ExpandKey

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)

Concatenate

Put everything together

Bcrypt

By Gordon Ueng

Bcrypt

  • 820