diff --git a/dev_tools/autogenerate-bloqs-notebooks-v2.py b/dev_tools/autogenerate-bloqs-notebooks-v2.py index c6fbef714..5c8b56353 100644 --- a/dev_tools/autogenerate-bloqs-notebooks-v2.py +++ b/dev_tools/autogenerate-bloqs-notebooks-v2.py @@ -68,12 +68,6 @@ ] -# -------------------------------------------------------------------------- -# ----- Root Bloqs ----------------------------------------------------- -# -------------------------------------------------------------------------- -ROOT_BLOQS = ['cryptography/ecc/ecc.ipynb'] - - def _all_nbspecs() -> Iterable[NotebookSpecV2]: for _, nbspecs in NB_BY_SECTION: yield from nbspecs @@ -108,7 +102,6 @@ def write_toc(): ] toc_lines = header + _get_toc_section_lines('Concepts', CONCEPTS, maxdepth=1) - toc_lines += _get_toc_section_lines('Root Bloqs', ROOT_BLOQS, maxdepth=1) bloqs_dir = SOURCE_DIR / 'bloqs' for section, nbspecs in NB_BY_SECTION: entries = [str(nbspec.path.relative_to(bloqs_dir)) for nbspec in nbspecs] diff --git a/dev_tools/qualtran_dev_tools/notebook_specs.py b/dev_tools/qualtran_dev_tools/notebook_specs.py index 46734ef48..268b54f3c 100644 --- a/dev_tools/qualtran_dev_tools/notebook_specs.py +++ b/dev_tools/qualtran_dev_tools/notebook_specs.py @@ -138,6 +138,19 @@ GIT_ROOT = get_git_root() SOURCE_DIR = GIT_ROOT / 'qualtran/' +# -------------------------------------------------------------------------- +# ----- Root Bloqs ----------------------------------------------------- +# -------------------------------------------------------------------------- + +ROOT_BLOQS: List[NotebookSpecV2] = [ + NotebookSpecV2( + title='Elliptic Curves', + module=qualtran.bloqs.cryptography.ecc, + path_stem='ecc_root', + bloq_specs=[qualtran.bloqs.cryptography.ecc.find_ecc_private_key._ECC_BLOQ_DOC], + ) +] + # -------------------------------------------------------------------------- # ----- Basic Gates ---------------------------------------------------- # -------------------------------------------------------------------------- @@ -557,6 +570,7 @@ NotebookSpecV2( title='Elliptic Curve Cryptography', module=qualtran.bloqs.cryptography.ecc, + path_stem='ecc_arithmetic', bloq_specs=[ qualtran.bloqs.cryptography.ecc.find_ecc_private_key._ECC_BLOQ_DOC, qualtran.bloqs.cryptography.ecc.ec_phase_estimate_r._EC_PE_BLOQ_DOC, @@ -928,6 +942,7 @@ ] NB_BY_SECTION = [ + ('Root Bloqs', ROOT_BLOQS), ('Basic Gates', BASIC_GATES), ('Chemistry', CHEMISTRY), ('Arithmetic', ARITHMETIC), diff --git a/docs/bloqs/index.rst b/docs/bloqs/index.rst index 3f0dbbac0..32943cc14 100644 --- a/docs/bloqs/index.rst +++ b/docs/bloqs/index.rst @@ -23,10 +23,10 @@ Bloqs Library state_preparation/state_preparation_via_rotation_tutorial.ipynb .. toctree:: - :maxdepth: 1 + :maxdepth: 2 :caption: Root Bloqs: - cryptography/ecc/ecc.ipynb + cryptography/ecc/ecc_root.ipynb .. toctree:: :maxdepth: 2 @@ -93,7 +93,7 @@ Bloqs Library mod_arithmetic/mod_division.ipynb cryptography/rsa/rsa.ipynb cryptography/ecc/ec_add.ipynb - cryptography/ecc/ecc.ipynb + cryptography/ecc/ecc_arithmetic.ipynb .. toctree:: :maxdepth: 2 diff --git a/qualtran/bloqs/cryptography/ecc/ecc.ipynb b/qualtran/bloqs/cryptography/ecc/ecc_arithmetic.ipynb similarity index 89% rename from qualtran/bloqs/cryptography/ecc/ecc.ipynb rename to qualtran/bloqs/cryptography/ecc/ecc_arithmetic.ipynb index 72a6d8a34..12a98d762 100644 --- a/qualtran/bloqs/cryptography/ecc/ecc.ipynb +++ b/qualtran/bloqs/cryptography/ecc/ecc_arithmetic.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "markdown", - "id": "ed4630ba", + "id": "1aaac3e4", "metadata": { "cq.autogen": "title_cell" }, @@ -31,7 +31,7 @@ { "cell_type": "code", "execution_count": null, - "id": "82580882", + "id": "b24b3daa", "metadata": { "cq.autogen": "top_imports" }, @@ -48,7 +48,7 @@ }, { "cell_type": "markdown", - "id": "60163c9f", + "id": "d20e347c", "metadata": { "cq.autogen": "FindECCPrivateKey.bloq_doc.md" }, @@ -100,7 +100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "92003a1f", + "id": "b593703c", "metadata": { "cq.autogen": "FindECCPrivateKey.bloq_doc.py" }, @@ -109,19 +109,9 @@ "from qualtran.bloqs.cryptography.ecc import FindECCPrivateKey" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6cb48b0a-44c8-40e5-ba1e-d0b3a0745046", - "metadata": {}, - "outputs": [], - "source": [ - "from qualtran.bloqs.cryptography.ecc import ECPoint" - ] - }, { "cell_type": "markdown", - "id": "e7c573eb", + "id": "67e1a021", "metadata": { "cq.autogen": "FindECCPrivateKey.example_instances.md" }, @@ -132,12 +122,14 @@ { "cell_type": "code", "execution_count": null, - "id": "51f1d2bf", + "id": "33570652", "metadata": { "cq.autogen": "FindECCPrivateKey.ecc" }, "outputs": [], "source": [ + "from qualtran.bloqs.cryptography.ecc import ECPoint\n", + "\n", "n, p = sympy.symbols('n p')\n", "Px, Py, Qx, Qy = sympy.symbols('P_x P_y Q_x Q_y')\n", "P = ECPoint(Px, Py, mod=p)\n", @@ -147,7 +139,7 @@ }, { "cell_type": "markdown", - "id": "921c0ce8", + "id": "502f66e1", "metadata": { "cq.autogen": "FindECCPrivateKey.graphical_signature.md" }, @@ -158,7 +150,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d231c54e", + "id": "77177488", "metadata": { "cq.autogen": "FindECCPrivateKey.graphical_signature.py" }, @@ -171,7 +163,7 @@ }, { "cell_type": "markdown", - "id": "73f68a55", + "id": "40e7ced4", "metadata": { "cq.autogen": "FindECCPrivateKey.call_graph.md" }, @@ -182,7 +174,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ff6e72fa", + "id": "720bb7b9", "metadata": { "cq.autogen": "FindECCPrivateKey.call_graph.py" }, @@ -194,19 +186,9 @@ "show_counts_sigma(ecc_sigma)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "510c3f43-5a16-4113-95e4-74e4c1618a68", - "metadata": {}, - "outputs": [], - "source": [ - "show_bloq(ecc.decompose_bloq())" - ] - }, { "cell_type": "markdown", - "id": "fff25268", + "id": "73b11a5b", "metadata": { "cq.autogen": "ECPhaseEstimateR.bloq_doc.md" }, @@ -231,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5220a4ad", + "id": "7c9b2f45", "metadata": { "cq.autogen": "ECPhaseEstimateR.bloq_doc.py" }, @@ -242,7 +224,7 @@ }, { "cell_type": "markdown", - "id": "f3421d38", + "id": "3e5866b7", "metadata": { "cq.autogen": "ECPhaseEstimateR.example_instances.md" }, @@ -253,7 +235,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17642453", + "id": "3d903148", "metadata": { "cq.autogen": "ECPhaseEstimateR.ec_pe" }, @@ -267,7 +249,7 @@ { "cell_type": "code", "execution_count": null, - "id": "4616d33a", + "id": "e8d187cf", "metadata": { "cq.autogen": "ECPhaseEstimateR.ec_pe_small" }, @@ -280,7 +262,7 @@ }, { "cell_type": "markdown", - "id": "33824ce4", + "id": "be0ea83a", "metadata": { "cq.autogen": "ECPhaseEstimateR.graphical_signature.md" }, @@ -291,7 +273,7 @@ { "cell_type": "code", "execution_count": null, - "id": "528b7d0e", + "id": "e784f967", "metadata": { "cq.autogen": "ECPhaseEstimateR.graphical_signature.py" }, @@ -304,7 +286,7 @@ }, { "cell_type": "markdown", - "id": "38b665f4", + "id": "a771bea2", "metadata": { "cq.autogen": "ECPhaseEstimateR.call_graph.md" }, @@ -315,7 +297,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9ce3d4df", + "id": "a98990b8", "metadata": { "cq.autogen": "ECPhaseEstimateR.call_graph.py" }, @@ -329,7 +311,7 @@ }, { "cell_type": "markdown", - "id": "fe222fde", + "id": "eeb676de", "metadata": { "cq.autogen": "ECAddR.bloq_doc.md" }, @@ -363,7 +345,7 @@ { "cell_type": "code", "execution_count": null, - "id": "757bfce9", + "id": "d4de8b11", "metadata": { "cq.autogen": "ECAddR.bloq_doc.py" }, @@ -374,7 +356,7 @@ }, { "cell_type": "markdown", - "id": "999b9be2", + "id": "ae316d61", "metadata": { "cq.autogen": "ECAddR.example_instances.md" }, @@ -385,7 +367,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2336e803", + "id": "5ebfe991", "metadata": { "cq.autogen": "ECAddR.ec_add_r" }, @@ -398,7 +380,7 @@ { "cell_type": "code", "execution_count": null, - "id": "322c447d", + "id": "ef61f868", "metadata": { "cq.autogen": "ECAddR.ec_add_r_small" }, @@ -411,7 +393,7 @@ }, { "cell_type": "markdown", - "id": "abe24e4e", + "id": "79fa11b2", "metadata": { "cq.autogen": "ECAddR.graphical_signature.md" }, @@ -422,7 +404,7 @@ { "cell_type": "code", "execution_count": null, - "id": "5422be2d", + "id": "ce93f3b2", "metadata": { "cq.autogen": "ECAddR.graphical_signature.py" }, @@ -435,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "c4db715e", + "id": "15d726a9", "metadata": { "cq.autogen": "ECAddR.call_graph.md" }, @@ -446,7 +428,7 @@ { "cell_type": "code", "execution_count": null, - "id": "bd38ad5f", + "id": "589fe557", "metadata": { "cq.autogen": "ECAddR.call_graph.py" }, @@ -458,22 +440,9 @@ "show_counts_sigma(ec_add_r_sigma)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d9dc15-7a16-4aeb-9f1a-a5e7a14461ba", - "metadata": {}, - "outputs": [], - "source": [ - "for j in range(1, 20+1):\n", - " bloq = ECAddR(n=5, R=j*P)\n", - " ctrl, x, y = bloq.call_classically(ctrl=1, x=P.x, y=P.y)\n", - " print(f'+[{j:2d}] P -> ({x:2d}, {y:2d})')" - ] - }, { "cell_type": "markdown", - "id": "72c1ad66", + "id": "20d7fe4e", "metadata": { "cq.autogen": "ECWindowAddR.bloq_doc.md" }, @@ -501,7 +470,7 @@ { "cell_type": "code", "execution_count": null, - "id": "289623e8", + "id": "7fe259cf", "metadata": { "cq.autogen": "ECWindowAddR.bloq_doc.py" }, @@ -512,7 +481,7 @@ }, { "cell_type": "markdown", - "id": "dd6c7d57", + "id": "5a31ee0b", "metadata": { "cq.autogen": "ECWindowAddR.example_instances.md" }, @@ -523,7 +492,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2ab0c101", + "id": "1a0c53e4", "metadata": { "cq.autogen": "ECWindowAddR.ec_window_add_r_small" }, @@ -536,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "dafa7d88", + "id": "20afa877", "metadata": { "cq.autogen": "ECWindowAddR.graphical_signature.md" }, @@ -547,7 +516,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39be6000", + "id": "aeb63b25", "metadata": { "cq.autogen": "ECWindowAddR.graphical_signature.py" }, @@ -560,7 +529,7 @@ }, { "cell_type": "markdown", - "id": "a923ec58", + "id": "cc918527", "metadata": { "cq.autogen": "ECWindowAddR.call_graph.md" }, @@ -571,7 +540,7 @@ { "cell_type": "code", "execution_count": null, - "id": "eea0704c", + "id": "345d1410", "metadata": { "cq.autogen": "ECWindowAddR.call_graph.py" }, @@ -586,21 +555,12 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" + "name": "python" } }, "nbformat": 4, diff --git a/qualtran/bloqs/cryptography/ecc/ecc_root.ipynb b/qualtran/bloqs/cryptography/ecc/ecc_root.ipynb new file mode 100644 index 000000000..199dd447a --- /dev/null +++ b/qualtran/bloqs/cryptography/ecc/ecc_root.ipynb @@ -0,0 +1,202 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4521a4ad", + "metadata": { + "cq.autogen": "title_cell" + }, + "source": [ + "# Elliptic Curves\n", + "\n", + "Bloqs for breaking elliptic curve cryptography systems via the discrete log.\n", + "\n", + "Elliptic curve cryptography is a form of public key cryptography based on the finite\n", + "field of elliptic curves. For our purposes, we will denote the group operation as addition\n", + "(whose definition we will explore later) $A + B$. We will denote repeated addition\n", + " as $[k] A = A + \\dots + A$ ($k$ times).\n", + "\n", + "Within this algebra, the cryptographic scheme relates the public and private keys via\n", + "$$\n", + "Q = [k] P\n", + "$$\n", + "for private key $k$, public key $Q$, and a choice of base point $P$. The cryptographic\n", + "security comes from the difficulty of inverting the multiplication. I.e. it is difficult\n", + "to do a discrete logarithm in this field.\n", + "\n", + "Using Shor's algorithm for the discrete logarithm, we can find $k$ in polynomial time\n", + "with a quantum algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2f0f36c", + "metadata": { + "cq.autogen": "top_imports" + }, + "outputs": [], + "source": [ + "from qualtran import Bloq, CompositeBloq, BloqBuilder, Signature, Register\n", + "from qualtran import QBit, QInt, QUInt, QAny\n", + "from qualtran.drawing import show_bloq, show_call_graph, show_counts_sigma\n", + "from typing import *\n", + "import numpy as np\n", + "import sympy\n", + "import cirq" + ] + }, + { + "cell_type": "markdown", + "id": "27e50393", + "metadata": { + "cq.autogen": "FindECCPrivateKey.bloq_doc.md" + }, + "source": [ + "## `FindECCPrivateKey`\n", + "Perform two phase estimations to break elliptic curve cryptography.\n", + "\n", + "This follows the strategy in Litinski 2023. We perform two phase estimations corresponding\n", + "to `ECCAddR(R=P)` and `ECCAddR(R=Q)` for base point $P$ and public key $Q$.\n", + "\n", + "The first phase estimation projects us into a random eigenstate of the ECCAddR(R=P) operator\n", + "which we index by the integer $c$. Per eq. 5 in the reference, these eigenstates take the form\n", + "$$\n", + "|\\psi_c \\rangle = \\sum_j^{r-1} \\omega^{cj}\\ | [j]P \\rangle \\\\\n", + "\\omega = e^{2\\pi i / r} \\\\\n", + "[r] P = P\n", + "$$\n", + "\n", + "This state is a simultaneous eigenstate of the second operator, `ECCAddR(R=Q)`. By\n", + "the definition of the operator, acting it upon $|\\psi_c\\rangle$ gives:\n", + "$$\n", + "|\\psi_c \\rangle \\rightarrow \\sum_j w^{cj} | [j]P + Q \\rangle\\rangle\n", + "$$\n", + "\n", + "The private key $k$ that we wish to recover relates the public key to the base point\n", + "$$\n", + "Q = [k] P\n", + "$$\n", + "so our simultaneous eigenstate can be equivalently written as\n", + "$$\n", + "\\sum_j^{r-1} \\omega^{cj} | [j+k] P \\rangle \\\\\n", + "= \\omega^{-ck} |\\psi_c \\rangle\n", + "$$\n", + "\n", + "Therefore, the measured result of the second phase estimation is $ck$. Since we have\n", + "already measured the random index $c$, we can divide it out to recover the private key $k$.\n", + "\n", + "#### Parameters\n", + " - `n`: The bitsize of the elliptic curve points' x and y registers.\n", + " - `base_point`: The base point $P$ with unknown order $r$ such that $P = [r] P$.\n", + " - `public_key`: The public key $Q$ such that $Q = [k] P$ for private key $k$.\n", + " - `add_window_size`: The number of bits in the ECAdd window.\n", + " - `mul_window_size`: The number of bits in the modular multiplication window. \n", + "\n", + "#### References\n", + " - [How to compute a 256-bit elliptic curve private key with only 50 million Toffoli gates](https://arxiv.org/abs/2306.08585). Litinski. 2023. Figure 4 (a).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcb004a4", + "metadata": { + "cq.autogen": "FindECCPrivateKey.bloq_doc.py" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.cryptography.ecc import FindECCPrivateKey" + ] + }, + { + "cell_type": "markdown", + "id": "60989597", + "metadata": { + "cq.autogen": "FindECCPrivateKey.example_instances.md" + }, + "source": [ + "### Example Instances" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78630e12", + "metadata": { + "cq.autogen": "FindECCPrivateKey.ecc" + }, + "outputs": [], + "source": [ + "from qualtran.bloqs.cryptography.ecc import ECPoint\n", + "\n", + "n, p = sympy.symbols('n p')\n", + "Px, Py, Qx, Qy = sympy.symbols('P_x P_y Q_x Q_y')\n", + "P = ECPoint(Px, Py, mod=p)\n", + "Q = ECPoint(Qx, Qy, mod=p)\n", + "ecc = FindECCPrivateKey(n=n, base_point=P, public_key=Q)" + ] + }, + { + "cell_type": "markdown", + "id": "6d4cc534", + "metadata": { + "cq.autogen": "FindECCPrivateKey.graphical_signature.md" + }, + "source": [ + "#### Graphical Signature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "827c4557", + "metadata": { + "cq.autogen": "FindECCPrivateKey.graphical_signature.py" + }, + "outputs": [], + "source": [ + "from qualtran.drawing import show_bloqs\n", + "show_bloqs([ecc],\n", + " ['`ecc`'])" + ] + }, + { + "cell_type": "markdown", + "id": "272d878d", + "metadata": { + "cq.autogen": "FindECCPrivateKey.call_graph.md" + }, + "source": [ + "### Call Graph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11406e9e", + "metadata": { + "cq.autogen": "FindECCPrivateKey.call_graph.py" + }, + "outputs": [], + "source": [ + "from qualtran.resource_counting.generalizers import ignore_split_join\n", + "ecc_g, ecc_sigma = ecc.call_graph(max_depth=1, generalizer=ignore_split_join)\n", + "show_call_graph(ecc_g)\n", + "show_counts_sigma(ecc_sigma)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qualtran/bloqs/cryptography/ecc/find_ecc_private_key.py b/qualtran/bloqs/cryptography/ecc/find_ecc_private_key.py index f6129af80..e25c64aee 100644 --- a/qualtran/bloqs/cryptography/ecc/find_ecc_private_key.py +++ b/qualtran/bloqs/cryptography/ecc/find_ecc_private_key.py @@ -130,6 +130,8 @@ def cost_attrs(self): @bloq_example def _ecc() -> FindECCPrivateKey: + from qualtran.bloqs.cryptography.ecc import ECPoint + n, p = sympy.symbols('n p') Px, Py, Qx, Qy = sympy.symbols('P_x P_y Q_x Q_y') P = ECPoint(Px, Py, mod=p)