pbkdf2HmacSha1 function

Uint8List pbkdf2HmacSha1(
  1. Uint8List password,
  2. Uint8List salt,
  3. int iterations,
  4. int length,
)

PBKDF2-HMAC-SHA1, length bytes of key from password

Implementation

Uint8List pbkdf2HmacSha1(
    Uint8List password, Uint8List salt, int iterations, int length) {
  if (iterations < 1) {
    throw ArgumentError.value(iterations, 'iterations', 'Must be positive');
  }
  final mac = HmacSha1(password);
  final sha = mac._digest;
  final w = sha._w;
  final h = sha._state;
  final pads = [mac._inner, mac._outer];
  final out = Uint8List(length);
  final index = Uint8List(4);
  final first = Uint8List(20);
  final u = Uint32List(5);
  final t = Uint32List(5);
  for (var block = 1, at = 0; at < length; block++, at += 20) {
    index[0] = block >>> 24;
    index[1] = (block >>> 16) & 0xff;
    index[2] = (block >>> 8) & 0xff;
    index[3] = block & 0xff;
    mac
      ..update(salt, 0, salt.length)
      ..update(index, 0, 4)
      ..finish(first, 0);
    for (var j = 0; j < 5; j++) {
      final o = 4 * j;
      u[j] = (first[o] << 24) |
          (first[o + 1] << 16) |
          (first[o + 2] << 8) |
          first[o + 3];
      t[j] = u[j];
    }
    // Each later message is the 20-byte MAC before it, so both hashes of an
    // iteration are one block of words with 84 bytes as the length. Skipping
    // update and finish took PBKDF2 from 1.71 to 1.32 ms on M4
    for (var i = 1; i < iterations; i++) {
      for (final pad in pads) {
        w.setAll(0, u);
        w[5] = 0x80000000;
        w.fillRange(6, 15, 0);
        w[15] = 84 * 8;
        h.setAll(0, pad);
        sha._compressBlock();
        u.setAll(0, h);
      }
      for (var j = 0; j < 5; j++) {
        t[j] ^= u[j];
      }
    }
    sha._resume(mac._inner);
    for (var j = 0; j < 20 && at + j < length; j++) {
      out[at + j] = (t[j >>> 2] >>> (24 - 8 * (j & 3))) & 0xff;
    }
  }
  return out;
}