streamXOR static method

List<int> streamXOR(
  1. List<int> key,
  2. List<int> nonce,
  3. List<int> src,
  4. List<int> dst, {
  5. int nonceInplaceCounterLength = 0,
  6. int seekBytes = 0,
})

Encrypts or decrypts data by XORing it with the output of the ChaCha stream cipher.

Parameters:

  • key: The 256-bit (32-byte) encryption key`.
  • nonce: The nonce data, which must be either 8, 12, or 16 bytes in length depending on the value of nonceInplaceCounterLength.
  • src: The source data to be encrypted or decrypted.
  • dst: The destination data where the result will be written.
  • nonceInplaceCounterLength: An optional parameter to specify the length of the nonce inplace counter (0 for no counter, 16 bytes if a counter is included in the nonce).

Throws:

  • ArgumentException.invalidBytesArgumentLength(reason: if the key size is not 32 bytes, if the destination is shorter than the source, or if the nonce length is invalid.

Implementation

static List<int> streamXOR(
  List<int> key,
  List<int> nonce,
  List<int> src,
  List<int> dst, {
  int nonceInplaceCounterLength = 0,
  int seekBytes = 0,
}) {
  // We only support 256-bit keys.
  if (key.length != 32) {
    throw ArgumentException.invalidOperationArguments(
      "streamXOR",
      name: "key",
      reason: "Invalid key bytes length.",
    );
  }

  if (dst.length < src.length) {
    throw ArgumentException.invalidOperationArguments(
      "streamXOR",
      name: "dst",
      reason: "Invalid destination bytes length.",
    );
  }

  List<int> nc;
  int counterLength;

  if (nonceInplaceCounterLength == 0) {
    if (nonce.length != 8 && nonce.length != 12) {
      throw ArgumentException.invalidOperationArguments(
        "streamXOR",
        name: "nonce",
        reason: "Invalid nonce bytes length.",
      );
    }
    nc = List<int>.filled(16, 0);
    counterLength = nc.length - nonce.length;
    nc.setAll(counterLength, nonce);
  } else {
    if (nonce.length != 16) {
      throw ArgumentException.invalidOperationArguments(
        "streamXOR",
        name: "nonce",
        reason: "Invalid nonce bytes length.",
      );
    }
    nc = nonce;
    counterLength = nonceInplaceCounterLength;
  }
  BinaryOps.zero(dst);
  final block = List<int>.filled(64, 0);

  final int blockSkip = seekBytes ~/ 64;
  final int byteSkip = seekBytes % 64;

  if (blockSkip != 0) {
    for (int b = 0; b < blockSkip; b++) {
      for (int i = 0; i < src.length; i += 64) {
        _core(block, nc, key);

        for (int j = 0; j < 64 && i + j < src.length; j++) {
          dst[i + j] = (src[i + j] & BinaryOps.mask8) ^ block[j];
        }

        _incrementCounter(nc, 0, counterLength);
      }
    }
  }
  int srcOffset = 0;

  if (byteSkip != 0) {
    _core(block, nc, key);

    // XOR only after byteSkip
    for (
      int j = byteSkip;
      j < 64 && srcOffset < src.length;
      j++, srcOffset++
    ) {
      dst[srcOffset] = (src[srcOffset] & BinaryOps.mask8) ^ block[j];
    }

    _incrementCounter(nc, 0, counterLength);
  }

  for (; srcOffset < src.length; srcOffset += 64) {
    _core(block, nc, key);

    for (int j = 0; j < 64 && srcOffset + j < src.length; j++) {
      dst[srcOffset + j] = (src[srcOffset + j] & BinaryOps.mask8) ^ block[j];
    }

    _incrementCounter(nc, 0, counterLength);
  }

  BinaryOps.zero(block);

  if (nonceInplaceCounterLength == 0) {
    BinaryOps.zero(nc);
  }

  return dst;
}