expand static method
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;
}