aboutsummaryrefslogtreecommitdiff
path: root/analysis/scalarmults/epare/error_model.py
diff options
context:
space:
mode:
Diffstat (limited to 'analysis/scalarmults/epare/error_model.py')
-rw-r--r--analysis/scalarmults/epare/error_model.py109
1 files changed, 109 insertions, 0 deletions
diff --git a/analysis/scalarmults/epare/error_model.py b/analysis/scalarmults/epare/error_model.py
new file mode 100644
index 0000000..a7f5edd
--- /dev/null
+++ b/analysis/scalarmults/epare/error_model.py
@@ -0,0 +1,109 @@
+from dataclasses import dataclass
+from functools import partial, total_ordering
+from typing import Any, Optional, Type, Union, Literal
+
+
+def check_equal_multiples(k, l, q):
+ """Checks whether the two multiples input into the formula are equal modulo q (the order of the base)."""
+ return (k % q) == (l % q)
+
+
+def check_divides(k, l, q):
+ """Checks whether q (the order of the base) divides any of the multiples input into the formula."""
+ return (k != 0) and (l != 0) and (k % q == 0) or (l % q == 0)
+
+
+def check_half_add(k, l, q):
+ return (q % 2 == 0) and ((k-l) % (q//2)) == 0
+
+
+def check_affine(k, q):
+ """Checks whether q (the order of the base) divides the multiple that is to be converted to affine."""
+ return k % q == 0
+
+
+def check_any(*checks, q=None):
+ """Merge multiple checks together. The returned check function no longer takes the `q` parameter."""
+ def check_func(k, l):
+ for check in checks:
+ if check(k, l, q):
+ return True
+ return False
+ return check_func
+
+
+# These checks can be applied to add formulas. See the formulas notebook for background on them.
+checks_add = {
+ "equal_multiples": check_equal_multiples,
+ "divides": check_divides,
+ "half_add": check_half_add
+}
+
+# This check can be applied to conversion to affine.
+checks_affine = {
+ "affine": check_affine
+}
+
+@dataclass(frozen=True)
+@total_ordering
+class ErrorModel:
+ """
+ An ErrorModel describes the behavior of an implementation with regards to errors on exceptional
+ inputs to its addition formulas, to-affine conversion or general scalar multiplication.
+
+ :param checks: A set of names of checks (from checks_add and checks_affine) that the implementation performs.
+ Note that these may not be checks that the implementation explicitly performs, only that it behaves w.r.t.
+ errors as if it were doing these checks, due to the formulas it chose and any actual checks it has.
+ :param check_condition: Either "all" or "necessary". Specifies whether the checks are applied to all points
+ that the implementation computes during a scalar multiplication or only those that end up being used -- thus
+ affect -- the final result. If an implementation does not perform any dummy operations, these two are the same.
+ :param precomp_to_affine: Specifies whether the implementation converts all results of the precomputation step
+ to affine form. If it does, it means that additional checks on all outputs of the precomputation are done as
+ they have to be "convertible" to affine form.
+ """
+ checks: set[str]
+ check_condition: Union[Literal["all"], Literal["necessary"]]
+ precomp_to_affine: bool
+
+ def __init__(self, checks: set[str], check_condition: Union[Literal["all"], Literal["necessary"]], precomp_to_affine: bool):
+ for check in checks:
+ if check not in checks_add:
+ raise ValueError(f"Unknown check: {check}")
+ checks = set(checks)
+ checks.add("affine") # always done in our model
+ object.__setattr__(self, "checks", checks)
+ if check_condition not in ("all", "necessary"):
+ raise ValueError("Wrong check_condition")
+ object.__setattr__(self, "check_condition", check_condition)
+ object.__setattr__(self, "precomp_to_affine", precomp_to_affine)
+
+ def check_add(self, q):
+ """Get the add formula check function for the given q."""
+ if self.checks == {"affine"}:
+ return lambda k, l: False
+ return check_any(*map(lambda name: checks_add[name], filter(lambda check: check in checks_add, self.checks)), q=q)
+
+ def check_affine(self, q):
+ """Get the to-affine check function for the given q."""
+ return partial(check_affine, q=q)
+
+ def __lt__(self, other):
+ if not isinstance(other, ErrorModel):
+ return NotImplemented
+ return str(self) < str(other)
+
+ def __str__(self):
+ cs = []
+ if "equal_multiples" in self.checks:
+ cs.append("em")
+ if "divides" in self.checks:
+ cs.append("d")
+ if "half_add" in self.checks:
+ cs.append("ha")
+ if "affine" in self.checks:
+ cs.append("a")
+ precomp = "+pre" if self.precomp_to_affine else ""
+ return f"({','.join(cs)}+{self.check_condition}{precomp})"
+
+ def __hash__(self):
+ return hash((tuple(sorted(self.checks)), self.check_condition, self.precomp_to_affine)) \ No newline at end of file