{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "rsaNk.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "authorship_tag": "ABX9TyNs17sm1WEvHs2dGv38ySe7",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/NuarkNoir/UPC/blob/master/4sem/isaip/02/RSA/rsaNk.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kx3VZRiUWJxc"
      },
      "source": [
        "## Алгоритм шифрования RSA"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sbpO6gadYhw3"
      },
      "source": [
        "import random\n",
        "\n",
        "def n_bit_random(n):\n",
        "    return(random.randrange(2**(n-1)+1, 2**n-1))\n",
        "\n",
        "first_primes_list = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, \n",
        "                     53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, \n",
        "                     109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, \n",
        "                     173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, \n",
        "                     233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, \n",
        "                     293, 307, 311, 313, 317, 331, 337, 347, 349]\n",
        "\n",
        "def check_low_level_prime(n):\n",
        "    while True: \n",
        "        prime_candidate = n_bit_random(n)  \n",
        "        for divisor in first_primes_list:  \n",
        "            if prime_candidate % divisor == 0 and divisor**2 <= prime_candidate: \n",
        "                break\n",
        "            return prime_candidate \n",
        "\n",
        "def check_miller_rabin_primality(mrc):\n",
        "    max_divisions_by_two = 0\n",
        "    ec = mrc-1\n",
        "    while ec % 2 == 0: \n",
        "        ec >>= 1\n",
        "        max_divisions_by_two += 1\n",
        "    assert(2**max_divisions_by_two * ec == mrc-1) # lame\n",
        "  \n",
        "    def trial_composite(round_tester): \n",
        "        if pow(round_tester, ec, mrc) == 1: \n",
        "            return False\n",
        "        for i in range(max_divisions_by_two): \n",
        "            if pow(round_tester, 2**i * ec, mrc) == mrc-1: \n",
        "                return False\n",
        "        return True\n",
        "  \n",
        "    trials_amount = 20 \n",
        "    for i in range(trials_amount): \n",
        "        round_tester = random.randrange(2, mrc) \n",
        "        if trial_composite(round_tester): \n",
        "            return False\n",
        "    return True\n",
        "\n",
        "def n_bit_random_prime(n):\n",
        "    while True:\n",
        "        possible_prime = check_low_level_prime(n) \n",
        "        if not check_miller_rabin_primality(possible_prime): \n",
        "            continue\n",
        "        return possible_prime"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "UKJWDO3Pvqri"
      },
      "source": [
        "import math\n",
        "\n",
        "import decimal\n",
        "decimal.getcontext().prec=10000\n",
        "_d = lambda x: decimal.Decimal(x)\n",
        "\n",
        "# Modular exponentiation goes brrrrr \n",
        "def modular_pow(base, exponent, modulus):\n",
        "    base, modulus = _d(base), _d(modulus)\n",
        "    base %= modulus\n",
        "    result = 1\n",
        "    while exponent > 0:\n",
        "        if int(exponent) & 1:\n",
        "            result = (result * base) % modulus\n",
        "        base = (base * base) % modulus\n",
        "        exponent //= 2\n",
        "    return result\n",
        "\n",
        "def encrypt(message, exponent, modulus):\n",
        "    out = []\n",
        "    for c in message:\n",
        "        encv = modular_pow(ord(c), exponent, modulus)\n",
        "        out.append(encv)\n",
        "    return out\n",
        "\n",
        "def decrypt(encrypted, pkey, modulus):\n",
        "    out = []\n",
        "    for num in encrypted:\n",
        "        decv = modular_pow(num, pkey, modulus)\n",
        "        out.append(chr(decv))\n",
        "    return \"\".join(out)\n",
        "\n",
        "def lcm(a, b):\n",
        "    return abs(a*b) // math.gcd(a, b)\n",
        "\n",
        "def choose_e(lcmv):\n",
        "    while 1:\n",
        "        e = random.randrange(3, 2**16+2)\n",
        "        if e < lcmv and math.gcd(e, lcmv) == 1:\n",
        "            return e\n",
        "\n",
        "def extended_gcd(a, b):\n",
        "    x, old_x = 0, 1\n",
        "    y, old_y = 1, 0\n",
        "\n",
        "    while (b != 0):\n",
        "        quotient = a // b\n",
        "        a, b = b, a - quotient * b\n",
        "        old_x, x = x, old_x - quotient * x\n",
        "        old_y, y = y, old_y - quotient * y\n",
        "\n",
        "    return a, old_x, old_y"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bw4RrEVrY5CD",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "faa2c44a-3614-4bdc-9a16-2b82714f9034"
      },
      "source": [
        "key_length = 1024\n",
        "%time P = n_bit_random_prime(key_length)\n",
        "%time Q = n_bit_random_prime(key_length)"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "CPU times: user 3.67 s, sys: 999 µs, total: 3.67 s\n",
            "Wall time: 3.69 s\n",
            "CPU times: user 1.85 s, sys: 0 ns, total: 1.85 s\n",
            "Wall time: 1.86 s\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jmogw7cUubMA"
      },
      "source": [
        "n = P * Q\n",
        "lcmv = lcm(P-1, Q-1)\n",
        "e = choose_e(lcmv)\n",
        "\n",
        "gcd, x, y = extended_gcd(e, lcmv)\n",
        "if x < 0:\n",
        "    d = x + lcmv\n",
        "else:\n",
        "    d = x"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wJr-rgmGCwZg",
        "outputId": "72707a66-fe1c-4ec0-c682-4cd741a50eee"
      },
      "source": [
        "print(e, d, n, sep=\"\\n\")"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "39649\n",
            "2775085486572169457933031624471639527709572371571563930751706363455686679103931887012786872357300821852072462055701332168753464477069850100477857634950881057962034588705265582670348431618430187943675082038338808823502290083334757934315038640497969786069367822629393372740634217481080439281756759203535191518030611113366713687329262086331416243914608547464393822243234244247787598602422663859280454972323707900579310354345134163802897882709119063046579316638215239586955495026262129316401981036727648176233721051089840544139490182413169518916207283731389315379326286341277949065063422651145928466610428684652315668195\n",
            "23566779236161770643112862787707714768319744752887788874531340221608793304492584597473313112218174480194085827590012234139629339174956445928785901695962763659830088046458803074421870909628916257832886158798721043310543454720481166030983536298954914657023031763702456252981131493715209010905445417269332126191376334319895522288189118297669317432037971085289460034222690931966559137798412723327254054307131802134542456875495143040890127834885396847838923210976349174614092437902214219214789194798418440795059024409693565307454667852500406186883883445142228234703191640206646086208037120337436442497385687546078418876013\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K-hHG4wMWXRB"
      },
      "source": [
        "## Тестирование алгоритма шифрования"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "uKS9PEF0wu0M",
        "outputId": "0555f9a9-d86a-4063-fed7-1ae8da28b2e8"
      },
      "source": [
        "messages = [\n",
        "    \"sosait\", \"test\", \"somewhat long message\", \"really long message repeated 10 times \"*10\n",
        "]\n",
        "for message in messages:\n",
        "    encrypted_message = encrypt(message, e, n)\n",
        "    decrypted_message = decrypt(encrypted_message, d, n)\n",
        "    \n",
        "    assert message == decrypted_message, \"Cypher error!\""
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "sosait\n",
            "Encrypted message will be really long\n",
            "sosait\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "v9TcbW1b822_"
      },
      "source": [
        "import os\n",
        "import pickle\n",
        "import IPython\n",
        "from google.colab import output, files\n",
        "\n",
        "def prepare_file(filename, data):\n",
        "    with open(filename, \"wb+\") as f:\n",
        "        pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)\n",
        "    return filename\n",
        "\n",
        "def download_public():\n",
        "    files.download(prepare_file(\"public.key\", [e, n]))\n",
        "\n",
        "def download_private():\n",
        "    files.download(prepare_file(\"private.key\", [d, n]))\n",
        "\n",
        "def download_unlink(filename):\n",
        "    try:\n",
        "        os.remove(filename)\n",
        "    except OSError:\n",
        "        pass\n",
        "\n",
        "output.register_callback(\"notebook.download_public\", download_public)\n",
        "output.register_callback(\"notebook.download_private\", download_private)\n",
        "output.register_callback(\"notebook.download_unlink\", download_unlink)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "bmO9YlMLAjvr"
      },
      "source": [
        "import os\n",
        "import pickle\n",
        "import IPython\n",
        "from google.colab import output, files\n",
        "\n",
        "def check_correctness():\n",
        "    download_unlink(\"public.key\")\n",
        "    download_unlink(\"private.key\")\n",
        "    \n",
        "    print(\"Select `public.key` and `private.key` files\")\n",
        "    uploaded = files.upload()\n",
        "    \n",
        "    eu, nf = pickle.loads(uploaded[\"public.key\"])\n",
        "    du, ns = pickle.loads(uploaded[\"private.key\"])\n",
        "    \n",
        "    try:\n",
        "        assert(nf == ns)\n",
        "    except AssertionError:\n",
        "        print(\"Keys modules are not equal!\")\n",
        "        return\n",
        "\n",
        "    nu = nf\n",
        "\n",
        "    test_message = \"test messages for days\"\n",
        "\n",
        "    try:\n",
        "        encm = encrypt(test_message, eu, nu)\n",
        "        decm = decrypt(encm, du, nu)\n",
        "    except AssertionError:\n",
        "        print(\"Test message not correctly encrypted\")\n",
        "    else:\n",
        "        print(\"Everything seems correct. Encryption and decryption worked well.\")\n",
        "    \n",
        "output.register_callback(\"notebook.check_correctness\", check_correctness)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "resources": {
            "http://localhost:8080/nbextensions/google.colab/files.js": {
              "data": "[encoded data, 6912 characters]",
              "ok": true,
              "headers": [
                [
                  "content-type",
                  "application/javascript"
                ]
              ],
              "status": 200,
              "status_text": ""
            }
          },
          "base_uri": "https://localhost:8080/",
          "height": 161
        },
        "id": "tKBqI0PV8wQ7",
        "outputId": "f0dc10d0-fa3b-4b75-a55a-c85b76154e2e"
      },
      "source": [
        "%%javascript\n",
        "(async function() {\n",
        "    let dPublic = document.createElement(\"button\");\n",
        "    let dPrivate = document.createElement(\"button\");\n",
        "    let dCheckC = document.createElement(\"button\");\n",
        "\n",
        "    dPublic.innerText = \"Download public key\";\n",
        "    dPrivate.innerText = \"Download private key\";\n",
        "    dCheckC.innerText = \"Check Correctness\";\n",
        "\n",
        "    dPublic.addEventListener('click', async (event) => {\n",
        "        await google.colab.kernel.invokeFunction(\"notebook.download_public\", [], {});\n",
        "        await google.colab.kernel.invokeFunction(\"notebook.download_unlink\", [\"public.key\"], {});\n",
        "    });\n",
        "\n",
        "    dPrivate.addEventListener('click', async (event) => {\n",
        "        await google.colab.kernel.invokeFunction(\"notebook.download_private\", [], {});\n",
        "        await google.colab.kernel.invokeFunction(\"notebook.download_unlink\", [\"private.key\"], {});\n",
        "    });\n",
        "\n",
        "    dCheckC.addEventListener('click', async (event) => {\n",
        "        await google.colab.kernel.invokeFunction(\"notebook.check_correctness\", [], {});\n",
        "    });\n",
        "\n",
        "    document.querySelector(\"#output-area\").appendChild(dPublic);\n",
        "    document.querySelector(\"#output-area\").appendChild(dPrivate);\n",
        "    document.querySelector(\"#output-area\").appendChild(dCheckC);\n",
        "})();"
      ],
      "execution_count": null,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "application/javascript": [
              "(async function() {\n",
              "    let dPublic = document.createElement(\"button\");\n",
              "    let dPrivate = document.createElement(\"button\");\n",
              "    let dCheckC = document.createElement(\"button\");\n",
              "\n",
              "    dPublic.innerText = \"Download public key\";\n",
              "    dPrivate.innerText = \"Download private key\";\n",
              "    dCheckC.innerText = \"Check Correctness\";\n",
              "\n",
              "    dPublic.addEventListener('click', async (event) => {\n",
              "        await google.colab.kernel.invokeFunction(\"notebook.download_public\", [], {});\n",
              "        await google.colab.kernel.invokeFunction(\"notebook.download_unlink\", [\"public.key\"], {});\n",
              "    });\n",
              "\n",
              "    dPrivate.addEventListener('click', async (event) => {\n",
              "        await google.colab.kernel.invokeFunction(\"notebook.download_private\", [], {});\n",
              "        await google.colab.kernel.invokeFunction(\"notebook.download_unlink\", [\"private.key\"], {});\n",
              "    });\n",
              "\n",
              "    dCheckC.addEventListener('click', async (event) => {\n",
              "        await google.colab.kernel.invokeFunction(\"notebook.check_correctness\", [], {});\n",
              "    });\n",
              "\n",
              "    document.querySelector(\"#output-area\").appendChild(dPublic);\n",
              "    document.querySelector(\"#output-area\").appendChild(dPrivate);\n",
              "    document.querySelector(\"#output-area\").appendChild(dCheckC);\n",
              "})();"
            ],
            "text/plain": [
              "<IPython.core.display.Javascript object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Select `public.key` and `private.key` files\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "\n",
              "     <input type=\"file\" id=\"files-c3b9fdda-1c82-4ed8-b689-480e37bb0f1e\" name=\"files[]\" multiple disabled\n",
              "        style=\"border:none\" />\n",
              "     <output id=\"result-c3b9fdda-1c82-4ed8-b689-480e37bb0f1e\">\n",
              "      Upload widget is only available when the cell has been executed in the\n",
              "      current browser session. Please rerun this cell to enable.\n",
              "      </output>\n",
              "      <script src=\"/nbextensions/google.colab/files.js\"></script> "
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Saving private.key to private.key\n",
            "Saving public.key to public.key\n",
            "Everything seems correct. Encryption and decryption worked well.\n"
          ],
          "name": "stdout"
        }
      ]
    }
  ]
}