From 8a62a5a2d03610779323b74e2cf059d8fcea35be Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 12 Jan 2024 19:18:42 +0100 Subject: Almost finished ZVP. --- re/zvp.ipynb | 2721 +++++++--------------------------------------------------- 1 file changed, 301 insertions(+), 2420 deletions(-) diff --git a/re/zvp.ipynb b/re/zvp.ipynb index fc633f8..fedb688 100644 --- a/re/zvp.ipynb +++ b/re/zvp.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "id": "17a9e580-5f1e-45c9-8afd-fc35e833b0b0", "metadata": {}, "outputs": [], @@ -25,76 +25,126 @@ "from IPython.display import HTML, display\n", "from tqdm.notebook import tqdm\n", "from anytree import RenderTree\n", + "from concurrent.futures import ProcessPoolExecutor, as_completed\n", "\n", "from pyecsca.ec.model import ShortWeierstrassModel\n", "from pyecsca.ec.coordinates import AffineCoordinateModel\n", "from pyecsca.ec.curve import EllipticCurve\n", "from pyecsca.ec.params import DomainParameters, load_params_ecgen\n", - "from pyecsca.ec.formula import FormulaAction\n", - "from pyecsca.ec.formula.fake import FakeAdditionFormula, FakeDoublingFormula, FakePoint\n", + "from pyecsca.ec.formula import FormulaAction, AdditionFormula, DoublingFormula\n", "from pyecsca.ec.point import Point\n", - "from pyecsca.ec.mod import Mod, gcd\n", + "from pyecsca.ec.mod import Mod, gcd, SymbolicMod\n", "from pyecsca.sca.re.tree import build_distinguishing_tree\n", "from pyecsca.sca.re.rpa import MultipleContext\n", "from pyecsca.sca.re.zvp import zvp_points, compute_factor_set\n", "from pyecsca.ec.context import DefaultContext, local\n", "from pyecsca.ec.mult import LTRMultiplier, AccumulationOrder\n", "from pyecsca.misc.cfg import getconfig\n", - "from pyecsca.ec.error import NonInvertibleError\n", - "from pyecsca.sca.re.zvp import unroll_formula, compute_factor_set, zvp_points" + "from pyecsca.ec.error import NonInvertibleError, UnsatisfiedAssumptionError\n", + "from pyecsca.sca.re.zvp import unroll_formula, compute_factor_set, zvp_points, addition_chain, precomp_zvp_points" ] }, { "cell_type": "code", - "execution_count": 2, - "id": "d8ea0c4d-86e1-46af-ac20-569e6ef5439d", + "execution_count": null, + "id": "c2240ed4-5279-487a-ad90-f6a6798f403c", "metadata": {}, "outputs": [], "source": [ - "cfg = getconfig()\n", - "cfg.ec.mod_implementation = \"gmp\"" + "# TODO: Maybe combine with EPA?\n", + "# TODO: Maybe extend oracle with number or position?" + ] + }, + { + "cell_type": "markdown", + "id": "60547b40-9b8e-409b-8b7e-b3dc266c01d4", + "metadata": {}, + "source": [ + "## Exploration\n", + "First lets explore the behavior of addition formulas. The following two cells pick a coordinate model along with some formulas and symbolically unroll a scalar multiplication." ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "8de124f1-9498-4e55-8b7f-1bd291ccf3fe", "metadata": {}, "outputs": [], "source": [ "model = ShortWeierstrassModel()\n", "coordsaff = AffineCoordinateModel(model)\n", - "coords = model.coordinates[\"projective\"]\n", - "add = coords.formulas[\"add-2007-bl\"]\n", - "dbl = coords.formulas[\"dbl-2007-bl\"]\n", - "neg = coords.formulas[\"neg\"]\n", + "which = \"jacobian\"\n", + "coords = model.coordinates[which]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2193ab53-9dc9-4ba4-89d3-e331e5cc20de", + "metadata": {}, + "outputs": [], + "source": [ + "getconfig().ec.mod_implementation = \"symbolic\"\n", + "x, y, z = symbols(\"x y z\")\n", "\n", "# A 64-bit prime order curve for testing things out\n", "p = 0xc50de883f0e7b167\n", - "a = Mod(0x4833d7aa73fa6694, p)\n", - "b = Mod(0xa6c44a61c5323f6a, p)\n", - "gx = Mod(0x5fd1f7d38d4f2333, p)\n", - "gy = Mod(0x21f43957d7e20ceb, p)\n", + "field = FF(p)\n", + "a = SymbolicMod(Poly(0x4833d7aa73fa6694, x, y, z, domain=field), p)\n", + "b = SymbolicMod(Poly(0xa6c44a61c5323f6a, x, y, z, domain=field), p)\n", + "gx = SymbolicMod(Poly(0x5fd1f7d38d4f2333, x, y, z, domain=field), p)\n", + "gy = SymbolicMod(Poly(0x21f43957d7e20ceb, x, y, z, domain=field), p)\n", "n = 0xc50de885003b80eb\n", "h = 1\n", "\n", - "field = FF(p)\n", - "\n", "infty = Point(coords, X=Mod(0, p), Y=Mod(1, p), Z=Mod(0, p))\n", "g = Point(coords, X=gx, Y=gy, Z=Mod(1, p))\n", "\n", "curve = EllipticCurve(model, coords, p, infty, dict(a=a,b=b))\n", - "params = DomainParameters(curve, g, n, h)" + "params = DomainParameters(curve, g, n, h)\n", + "\n", + "\n", + "add = coords.formulas[\"add-2007-bl\"]\n", + "dbl = coords.formulas[\"dbl-2007-bl\"]\n", + "mult = LTRMultiplier(add, dbl, None, False, AccumulationOrder.PeqRP, True, True)\n", + "\n", + "\n", + "point = Point(coords,\n", + " X=SymbolicMod(Poly(x, x, y, z, domain=field), params.curve.prime),\n", + " Y=SymbolicMod(Poly(y, x, y, z, domain=field), params.curve.prime),\n", + " Z=SymbolicMod(Poly(z, x, y, z, domain=field), params.curve.prime))\n", + "mult.init(params, point)\n", + "res = mult.multiply(5)" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, + "id": "d8ea0c4d-86e1-46af-ac20-569e6ef5439d", + "metadata": {}, + "outputs": [], + "source": [ + "cfg = getconfig()\n", + "cfg.ec.mod_implementation = \"gmp\"" + ] + }, + { + "cell_type": "markdown", + "id": "c3b3ae94-242b-4f36-81e3-e8a64b5af33c", + "metadata": {}, + "source": [ + "## Reverse-engineering\n", + "Now, lets look at using the ZVP attack for reverse-engineering. First pick 10 random curves and one curve with `a = 0`. These curves are not special in any way and just serve to randomize the process, as the existence of ZVP points for a given intermediate value polynomial depends on the curve." + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "0252f8fa-012c-453c-ba23-b40f9686fcce", "metadata": {}, "outputs": [], "source": [ - "curves = list(map(lambda spec: load_params_ecgen(io.BytesIO(spec.encode()), \"projective\"), [\n", + "curves = list(map(lambda spec: load_params_ecgen(io.BytesIO(spec.encode()), \"affine\"), [\n", " \"\"\"[{\"field\":{\"p\":\"0xa7ec3617d4166b2d\"},\"a\":\"0x372994d9d680a83b\",\"b\":\"0xa0a2bf719d8e68c5\",\"order\":\"0xa7ec3618be1dab55\",\"subgroups\":[{\"x\":\"0x1ef15756946a5b6d\",\"y\":\"0x2ca9658f7ab9a558\",\"order\":\"0xa7ec3618be1dab55\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x1ef15756946a5b6d\",\"y\":\"0x2ca9658f7ab9a558\",\"order\":\"0xa7ec3618be1dab55\"}]}]}]\"\"\",\n", " \"\"\"[{\"field\":{\"p\":\"0xa42c1467a1ed04f3\"},\"a\":\"0x55d07340a4572f2d\",\"b\":\"0x0a938c37dfb0b6d5\",\"order\":\"0xa42c14689284d3a7\",\"subgroups\":[{\"x\":\"0x8633981c83ed43a2\",\"y\":\"0x7b5374e9d7997199\",\"order\":\"0xa42c14689284d3a7\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x8633981c83ed43a2\",\"y\":\"0x7b5374e9d7997199\",\"order\":\"0xa42c14689284d3a7\"}]}]}]\"\"\",\n", " \"\"\"[{\"field\":{\"p\":\"0xea0d9cead19016ab\"},\"a\":\"0xcbbfe501c4ef6d92\",\"b\":\"0x5762de777a6d9178\",\"order\":\"0xea0d9cea8cd2c857\",\"subgroups\":[{\"x\":\"0xe7daa3e061c3111b\",\"y\":\"0x56ee59a6845c5e93\",\"order\":\"0xea0d9cea8cd2c857\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0xe7daa3e061c3111b\",\"y\":\"0x56ee59a6845c5e93\",\"order\":\"0xea0d9cea8cd2c857\"}]}]}]\"\"\",\n", @@ -105,2470 +155,301 @@ " \"\"\"[{\"field\":{\"p\":\"0xd47ec1d03a62686d\"},\"a\":\"0xd00a3ee0f5c86b02\",\"b\":\"0x457a5b6c47db38d8\",\"order\":\"0xd47ec1d107db7d6f\",\"subgroups\":[{\"x\":\"0x41ebc3b763f3cd1b\",\"y\":\"0x3d6925f214620e0c\",\"order\":\"0xd47ec1d107db7d6f\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x41ebc3b763f3cd1b\",\"y\":\"0x3d6925f214620e0c\",\"order\":\"0xd47ec1d107db7d6f\"}]}]}]\"\"\",\n", " \"\"\"[{\"field\":{\"p\":\"0xb1c9115c6f40d755\"},\"a\":\"0x79d3ceefafc44ce9\",\"b\":\"0x8316af84264df42b\",\"order\":\"0xb1c9115d17f84a45\",\"subgroups\":[{\"x\":\"0x8b0a274089b53fe5\",\"y\":\"0x3508d33c4beba5ad\",\"order\":\"0xb1c9115d17f84a45\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x8b0a274089b53fe5\",\"y\":\"0x3508d33c4beba5ad\",\"order\":\"0xb1c9115d17f84a45\"}]}]}]\"\"\",\n", " \"\"\"[{\"field\":{\"p\":\"0x8f738fda18cd5dff\"},\"a\":\"0x4747f2f9b8628cbf\",\"b\":\"0x586cdb9378a1389f\",\"order\":\"0x8f738fd8fc7ebed3\",\"subgroups\":[{\"x\":\"0x7ad306c73b64c1b5\",\"y\":\"0x69e3ca555190da4b\",\"order\":\"0x8f738fd8fc7ebed3\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x7ad306c73b64c1b5\",\"y\":\"0x69e3ca555190da4b\",\"order\":\"0x8f738fd8fc7ebed3\"}]}]}]\"\"\",\n", + " \"\"\"[{\"field\":{\"p\":\"0xcfef393139c3007f\"},\"a\":\"0xcfef393139c3007e\",\"b\":\"0x950312812acb155f\",\"order\":\"0xcfef39320179387b\",\"subgroups\":[{\"x\":\"0xae2d2f58ca5b5cf7\",\"y\":\"0xc3a4bf3a1dc10005\",\"order\":\"0xcfef39320179387b\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0xae2d2f58ca5b5cf7\",\"y\":\"0xc3a4bf3a1dc10005\",\"order\":\"0xcfef39320179387b\"}]}]}]\"\"\",\n", + " \"\"\"[{\"field\":{\"p\":\"0x8d79ca36cee026a7\"},\"a\":\"0x8d79ca36cee026a4\",\"b\":\"0x0478c1f80ce2c9c6\",\"order\":\"0x8d79ca35a428c76f\",\"subgroups\":[{\"x\":\"0x2e94a3e38f8b345e\",\"y\":\"0x83e6c6f0cb8f69c4\",\"order\":\"0x8d79ca35a428c76f\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x2e94a3e38f8b345e\",\"y\":\"0x83e6c6f0cb8f69c4\",\"order\":\"0x8d79ca35a428c76f\"}]}]}]\"\"\",\n", " \"\"\"[{\"field\":{\"p\":\"0xceaf446a53f14bc1\"},\"a\":\"0x0000000000000000\",\"b\":\"0x326539376260f173\",\"order\":\"0xceaf446aae275419\",\"subgroups\":[{\"x\":\"0x98fe44948c3f8678\",\"y\":\"0x3d440ee959a912d7\",\"order\":\"0xceaf446aae275419\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x98fe44948c3f8678\",\"y\":\"0x3d440ee959a912d7\",\"order\":\"0xceaf446aae275419\"}]}]}]\"\"\",\n", - " \"\"\"[{\"field\":{\"p\":\"0x9d9119957f02fe3f\"},\"a\":\"0x0106903196d88df9\",\"b\":\"0x0000000000000000\",\"order\":\"0x9d9119957f02fe40\",\"subgroups\":[{\"x\":\"0x191a36b9cd81de96\",\"y\":\"0x10f2c6bded391aa9\",\"order\":\"0x9d9119957f02fe40\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x0000000000000000\",\"y\":\"0x0000000000000000\",\"order\":\"0x2\"},{\"x\":\"0x95913fae9065da0f\",\"y\":\"0x5eeddeee7152d6fb\",\"order\":\"0x276446655fc0bf9\"}]}]}]\"\"\"\n", + " #\"\"\"[{\"field\":{\"p\":\"0x9d9119957f02fe3f\"},\"a\":\"0x0106903196d88df9\",\"b\":\"0x0000000000000000\",\"order\":\"0x9d9119957f02fe40\",\"subgroups\":[{\"x\":\"0x191a36b9cd81de96\",\"y\":\"0x10f2c6bded391aa9\",\"order\":\"0x9d9119957f02fe40\",\"cofactor\":\"0x1\",\"points\":[{\"x\":\"0x0000000000000000\",\"y\":\"0x0000000000000000\",\"order\":\"0x2\"},{\"x\":\"0x95913fae9065da0f\",\"y\":\"0x5eeddeee7152d6fb\",\"order\":\"0x276446655fc0bf9\"}]}]}]\"\"\"\n", "]))" ] }, { - "cell_type": "code", - "execution_count": 5, - "id": "e65c4ab0-a9fa-479b-b109-67968fd67c1c", + "cell_type": "markdown", + "id": "4276de4c-78f4-4cdb-b60e-8c24eabfa00d", "metadata": {}, - "outputs": [], "source": [ - "def simulated_oracle(scalar, affine_point, real_coord_name=\"projective\", real_add_name=\"add-2007-bl\", real_dbl_name=\"dbl-2007-bl\"):\n", - " real_coords = model.coordinates[real_coord_name]\n", - " real_add = real_coords.formulas[real_add_name]\n", - " real_dbl = real_coords.formulas[real_dbl_name]\n", - " real_mult = LTRMultiplier(real_add, real_dbl, None, False, AccumulationOrder.PeqPR, True, True)\n", - " point = affine_point.to_model(params.curve.coordinate_model, params.curve)\n", - " with local(DefaultContext()) as ctx:\n", - " real_mult.init(params, point)\n", - " real_mult.multiply(scalar)\n", - "\n", - " trace = []\n", - "\n", - " def callback(action):\n", - " if isinstance(action, FormulaAction):\n", - " for intermediate in action.op_results:\n", - " trace.append(intermediate.value)\n", - " ctx.actions.walk(callback)\n", - " return any(int(value) == 0 for value in trace)" + "First lets fix a scalar, go over the curves and compute the addition chain to obtain information about which multiples of the input point will go into the formulas." ] }, { "cell_type": "code", - "execution_count": 6, - "id": "d0ef1628-3d87-40be-95a0-3045bb1ee4aa", + "execution_count": null, + "id": "7ab00a5c-2873-40c0-a81c-873350897e46", "metadata": {}, "outputs": [], "source": [ - "adds = list(filter(lambda formula: formula.name.startswith(\"add\"), coords.formulas.values()))\n", - "dbls = list(filter(lambda formula: formula.name.startswith(\"dbl\"), coords.formulas.values()))\n", - "formula_pairs = list(product(adds, dbls))\n", + "scalar = 123456789\n", "\n", - "# Compute all of the factor sets, and adjust those for dbl to the right index.\n", - "add_fsets = {add: compute_factor_set(add) for add in adds}\n", - "dbl_fsets = {}\n", - "for dbl in dbls:\n", - " fset = compute_factor_set(dbl)\n", - " dbl2_fset = set()\n", - " for poly in fset:\n", - " pl = poly.copy()\n", - " for symbol in poly.free_symbols:\n", - " original = str(symbol)\n", - " if original.endswith(\"1\"):\n", - " new = original.replace(\"1\", \"2\")\n", - " pl = pl.subs(original, new)\n", - " dbl2_fset.add(pl)\n", - " dbl_fsets[dbl] = dbl2_fset" + "chains = []\n", + "for i, params in enumerate(curves):\n", + " chain = addition_chain(scalar, params, LTRMultiplier, lambda add,dbl: LTRMultiplier(add, dbl, None, False, AccumulationOrder.PeqRP, True, True))\n", + " chains.append(chain)" + ] + }, + { + "cell_type": "markdown", + "id": "12646534-8ca5-48c1-a4ae-1ad62575821f", + "metadata": {}, + "source": [ + "Now, lets compute the sets of ZVP points, going over all coordinate systems and all of their formulas (that fit the scalar multiplier) and store them into the `point_chains`. These items form individual distinct entries of the distinguishing table." ] }, { "cell_type": "code", - "execution_count": 51, - "id": "b7c7b341-5b15-44fb-b9cf-462b64e7536e", + "execution_count": null, + "id": "91bc36bb-80e8-41d8-8612-7f9e24bdf278", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8f0f73cf1c9442aebfe050641983caf9", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Compute ZVP points: 0%| | 0/12 [00:00 bound:\n", + "formula_classes = [AdditionFormula, DoublingFormula]\n", + "point_chains = {}\n", + "with ProcessPoolExecutor(max_workers=10) as pool:\n", + " futures = []\n", + " args = []\n", + " for coord_name, coords in model.coordinates.items():\n", + " for chain, affine_params in zip(chains, curves):\n", + " try:\n", + " params = affine_params.to_coords(coords)\n", + " except UnsatisfiedAssumptionError:\n", " continue\n", - " chain.append((\"add\", k))\n", - " else:\n", - " chain.append((\"dbl\", k))\n", - " chains.append(chain)\n", + " formula_groups = [list(filter(lambda formula: isinstance(formula, formula_class), coords.formulas.values())) for formula_class in formula_classes]\n", "\n", - "point_sets = {}\n", - "for chain, params in tqdm(zip(chains, curves), desc=\"Compute ZVP points\", total=len(chains)):\n", - " for add in tqdm(adds, desc=\"Compute ZVP points for adds\", leave=False):\n", - " add_fset = add_fsets[add]\n", - " for op, k in tqdm(chain, leave=False):\n", - " if op == \"add\" and (add, k, params) not in point_sets:\n", - " points = set()\n", - " for poly in add_fset:\n", - " try:\n", - " points.update(zvp_points(poly, params.curve, k, params.order))\n", - " except NonInvertibleError:\n", - " pass\n", - " point_sets[(add, k, params)] = points\n", - " for dbl in tqdm(dbls, desc=\"Compute ZVP points for dbls\", leave=False):\n", - " dbl_fset = dbl_fsets[dbl]\n", - " for op, k in tqdm(chain, leave=False):\n", - " if op == \"dbl\" and (dbl, k, params) not in point_sets:\n", - " points = set()\n", - " for poly in dbl_fset:\n", - " try:\n", - " points.update(zvp_points(poly, params.curve, k, params.order))\n", - " except NonInvertibleError:\n", - " pass\n", - " point_sets[(dbl, k, params)] = points" + " for formula_group, formula_class in zip(formula_groups, formula_classes):\n", + " for formula in formula_group:\n", + " futures.append(pool.submit(precomp_zvp_points, chain[:chain_bound], {formula_class.shortname: formula}, params, bound))\n", + " args.append((coords, formula, affine_params))\n", + " for future in tqdm(as_completed(futures), desc=\"Compute\", total=len(futures)):\n", + " j = futures.index(future)\n", + " point_chains[args[j]] = future.result()\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "55cb5e3c-b1f1-443f-9d5f-269de6eb59ec", + "metadata": {}, + "source": [ + "Now, accumulate the rows of the distinguishing table to create the distinguishing map `point_map`." ] }, { "cell_type": "code", - "execution_count": 63, - "id": "f95909c9-8595-4d2d-8963-0c6fcb5728dd", + "execution_count": null, + "id": "b7c7b341-5b15-44fb-b9cf-462b64e7536e", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8af38dfac64442438b532d05557e04c7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Remapping: 0%| | 0/20 [00:00