mulAdd method

ProjectiveECCPoint mulAdd(
  1. BigInt selfMul,
  2. ProjectivePointNative otherPoint,
  3. BigInt otherMul
)

Multiplies this point by a scalar value selfMul and adds another point otherPoint multiplied by otherMul.

Parameters:

  • selfMul: Scalar value to multiply this point by.
  • otherPoint: The other point to multiply.
  • otherMul: Scalar value to multiply the otherPoint by.

Returns:

Implementation

ProjectiveECCPoint mulAdd(
  BigInt selfMul,
  ProjectivePointNative otherPoint,
  BigInt otherMul,
) {
  if (otherPoint.isZero() || otherMul == BigInt.zero) {
    return this * selfMul;
  }
  if (selfMul == BigInt.zero) {
    return (otherPoint * otherMul).cast<ProjectiveECCPoint>();
  }
  final other = switch (otherPoint) {
    final ProjectiveECCPoint point => point,
    _ => ProjectiveECCPoint.fromAffine(otherPoint),
  };
  _precomputeIfNeeded();
  other._precomputeIfNeeded();
  if (_precompute.isNotEmpty && other._precompute.isNotEmpty) {
    return (this * selfMul + other * otherMul).cast<ProjectiveECCPoint>();
  }
  final order = this.order;
  if (order != null) {
    selfMul = selfMul % order;
    otherMul = otherMul % order;
  }

  BigInt x3 = BigInt.zero;
  BigInt y3 = BigInt.zero;
  BigInt z3 = BigInt.one;
  final BigInt p = curve.p;
  final BigInt a = curve.a;

  scale();
  final BigInt x1 = _coords[0];
  final BigInt y1 = _coords[1];
  final BigInt z1 = _coords[2];
  other.scale();
  final BigInt x2 = other._coords[0];
  final BigInt y2 = other._coords[1];
  final BigInt z2 = other._coords[2];

  // with NAF we have 3 options: no add, subtract, add
  // so with 2 points, we have 9 combinations:
  // 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B
  // so we need 4 combined points:
  final List<BigInt> mAmB = _addPoints(x1, -y1, z1, x2, -y2, z2, p);

  final List<BigInt> pAmB = _addPoints(x1, y1, z1, x2, -y2, z2, p);

  final List<BigInt> mApB = [pAmB[0], -pAmB[1], pAmB[2]];
  final List<BigInt> pApB = [mAmB[0], -mAmB[1], mAmB[2]];

  if (pApB[1] == BigInt.zero || pApB[2] == BigInt.zero) {
    return (this * selfMul + other * otherMul) as ProjectiveECCPoint;
  }

  List<BigInt> selfNaf = ECDSAUtils.computeNAF(selfMul).reversed.toList();
  List<BigInt> otherNaf = ECDSAUtils.computeNAF(otherMul).reversed.toList();

  if (selfNaf.length < otherNaf.length) {
    selfNaf =
        List.filled(otherNaf.length - selfNaf.length, BigInt.zero) + selfNaf;
  } else if (selfNaf.length > otherNaf.length) {
    otherNaf =
        List.filled(selfNaf.length - otherNaf.length, BigInt.zero) + otherNaf;
  }

  for (int i = 0; i < selfNaf.length; i++) {
    final BigInt A = selfNaf[i];
    final BigInt B = otherNaf[i];

    List<BigInt> result = _double(x3, y3, z3, p, a);

    // conditions ordered from most to least likely
    if (A == BigInt.zero) {
      if (B == BigInt.zero) {
        // Do nothing.
      } else if (B < BigInt.zero) {
        result = _addPoints(result[0], result[1], result[2], x2, -y2, z2, p);
      } else {
        assert(B > BigInt.zero);
        result = _addPoints(result[0], result[1], result[2], x2, y2, z2, p);
      }
    } else if (A < BigInt.zero) {
      if (B == BigInt.zero) {
        result = _addPoints(result[0], result[1], result[2], x1, -y1, z1, p);
      } else if (B < BigInt.zero) {
        result = _addPoints(
          result[0],
          result[1],
          result[2],
          mAmB[0],
          mAmB[1],
          mAmB[2],
          p,
        );
      } else {
        assert(B > BigInt.zero);
        result = _addPoints(
          result[0],
          result[1],
          result[2],
          mApB[0],
          mApB[1],
          mApB[2],
          p,
        );
      }
    } else {
      assert(A > BigInt.zero);
      if (B == BigInt.zero) {
        result = _addPoints(result[0], result[1], result[2], x1, y1, z1, p);
      } else if (B < BigInt.zero) {
        result = _addPoints(
          result[0],
          result[1],
          result[2],
          pAmB[0],
          pAmB[1],
          pAmB[2],
          p,
        );
      } else {
        assert(B > BigInt.zero);
        result = _addPoints(
          result[0],
          result[1],
          result[2],
          pApB[0],
          pApB[1],
          pApB[2],
          p,
        );
      }
    }

    x3 = result[0];
    y3 = result[1];
    z3 = result[2];
  }

  if (y3 == BigInt.zero || z3 == BigInt.zero) {
    return ProjectiveECCPoint.infinity(curve);
  }

  return ProjectiveECCPoint._(curve, [x3, y3, z3], order: order);
}