expand static method

Uint8List expand({
  1. required Uint8List prk,
  2. Uint8List? info,
  3. required int length,
})

Computes the Expand step to produce length bytes from a given prk.

prk Pseudorandom key from extract (must be hashLen bytes). info Optional context/application-specific information (can be empty). length Desired output keying material length (0 < length ≤ 255*hashLen).

RETURNS: OKM (length bytes).

HINT:

  • For two 32B keys, call with length=64 and then split.

Implementation

static Uint8List expand({
  required Uint8List prk,
  Uint8List? info,
  required int length,
}) {
  if (prk.length != hashLen) {
    throw ArgumentError.value(prk.length, 'prk.length', 'PRK must be $hashLen bytes.');
  }
  if (length <= 0 || length > 255 * hashLen) {
    throw ArgumentError.value(length, 'length', 'Must be in range 1..${255 * hashLen}.');
  }

  final Uint8List infoBytes = (info == null) ? Uint8List(0) : info;
  final int nBlocks = (length + hashLen - 1) ~/ hashLen; // ceil
  final out = Uint8List(length);

  // Iterative T(i) generation
  Uint8List previousT = Uint8List(0);
  var offset = 0;
  for (int i = 1; i <= nBlocks; i++) {
    // T(i) = HMAC(PRK, T(i-1) || info || counterByte)
    final counter = Uint8List.fromList([i & 0xff]);
    final t = HmacSha256.computeParts(
      prk,
      [previousT, infoBytes, counter],
    );

    final copyLen = (offset + hashLen <= length) ? hashLen : (length - offset);
    out.setRange(offset, offset + copyLen, t);
    offset += copyLen;

    // Zeroize previousT and replace with new t for next round.
    Bytes.secureZero(previousT);
    previousT = t;
  }

  // Best effort: wipe the last T(i)
  Bytes.secureZero(previousT);

  return out;
}