deriveKey method

  1. @override
Future<SecretKey> deriveKey({
  1. required SecretKey secretKey,
  2. required List<int> nonce,
})
override

Generates a new secret key from a secret key and a nonce.

The nonce ("salt") should be some random sequence of bytes. Nonce does not need to be protected. If possible, you should have a different nonce for each key derivation.

Implementation

@override
Future<SecretKey> deriveKey({
  required SecretKey secretKey,
  required List<int> nonce,
}) async {
  // For performance, we prefer a synchronous MAC algorithm
  final macAlgorithm = this.macAlgorithm.toSync();
  final secretKeyData = await secretKey.extract();

  // Allocate bytes for the output.
  // It must be a multiple of the MAC length.
  final macLength = macAlgorithm.macLength;
  final numberOfBytes = (bits + 7) ~/ 8;
  final result = Uint8List(
    ((numberOfBytes + macLength - 1) ~/ macLength) * macLength,
  );

  // The first input is the nonce + block index
  final nonceAndBlockIndex = Uint8List(nonce.length + 4);
  nonceAndBlockIndex.setAll(0, nonce);
  final firstInput = Uint8List(nonce.length + 4);
  firstInput.setAll(0, nonce);

  final macState = macAlgorithm.newMacSinkSync(
    secretKeyData: secretKeyData,
    nonce: nonce,
  );

  for (var partIndex = 0;
      partIndex < result.lengthInBytes ~/ macLength;
      partIndex++) {
    // First block has big-endian block index appended
    final fi = firstInput.length - 4;
    final blockIndex = partIndex + 1;
    firstInput[fi] = 0xFF & (blockIndex >> 24);
    firstInput[fi + 1] = 0xFF & (blockIndex >> 16);
    firstInput[fi + 2] = 0xFF & (blockIndex >> 8);
    firstInput[fi + 3] = 0xFF & blockIndex;

    // Calculate first block
    final firstMac = macAlgorithm.calculateMacSync(
      firstInput,
      secretKeyData: secretKeyData,
      nonce: nonce,
    );
    final block = Uint8List.fromList(firstMac.bytes);
    final previous = Uint8List(block.length);
    previous.setAll(0, block);

    // Iterate
    for (var i = 1; i < iterations; i++) {
      // Wait a bit to prevent blocking the main event loop
      if (pauseFrequency > 100 &&
          i % pauseFrequency == 0 &&
          pausePeriod.inMicroseconds != 0) {
        await Future.delayed(pausePeriod);
      }
      // Calculate MAC
      macState.initializeSync(
        secretKey: secretKeyData,
        nonce: nonce,
      );
      macState.addSlice(previous, 0, previous.length, true);
      final macBytes = macState.macBytes;

      // XOR with the result
      for (var bi = 0; bi < block.length; bi++) {
        block[bi] ^= macBytes[bi];
      }

      // Update previous block
      for (var i = 0; i < macBytes.length; i++) {
        previous[i] = macBytes[i];
      }
    }
    result.setAll(macLength * partIndex, block);
  }

  // Return bytes
  if (numberOfBytes == result.lengthInBytes) {
    return SecretKey(result);
  }
  return SecretKey(Uint8List.view(
    result.buffer,
    result.offsetInBytes,
    numberOfBytes,
  ));
}