{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dfa82713-08fc-47fc-aad9-53f24142d090",
   "metadata": {},
   "source": [
    "# Attacking a Discrete Knapsack Public Key Cryptosystem using LLL lattice reduction\n",
    "In this example we will use our library to construct a **Discrete Knapsack Public Key Cryptosystem** and then attack it and retrieve **private key** using **LLL lattice reduction**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2ab7943-14d9-4676-8003-c0c8b7902886",
   "metadata": {},
   "source": [
    "# Discrete Knapsack Public Key Cryptosystem\n",
    "In this cryptosystem we assume security by using **Discrete Knapsack Packing Problem** which is **NP-hard**.  \n",
    "In theory this should a be very strong assurance. However in practice in order to create a **trapdoor** we will have to use special kind of sequences (superincreasing sequences), that will allow us to find and exploit vulnerabilities of the cryptosystem without actually solving the underlying general NP-hard problem.\n",
    "\n",
    "## Discrete Knapsack Packing Problem\n",
    "Given a list of positive integers $(M_1, M_2, ..., M_n)$ and another integer $S$ find a subset of the elements in the list that sums to $S$.  \n",
    "This problem in general is very hard to solve. However if we use so called **superincreasing sequences** as our list, we can use straightforward greedy algorithm to find a solution.  \n",
    "We say that list of positive integers $(r_1, r_2, ... , r_n)$ is **superincreasing** if\n",
    "$$\n",
    "r_{i+1} \\geq 2r_i \\quad \\text{for all} \\; 1 \\leq i \\leq n - 1\n",
    "$$\n",
    "If we are asked to solve knapsack problem $(M, S)$, where $M$ is integer sequence and $S$ is the sum we have to obtain then if $M$ is superincreasing sequence we can find a solution with following algorithm.\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\text{1.} & \\text{find the largest element of} \\; M \\; \\text{that is smaller than} \\; S. \\newline\n",
    "\\text{2.} & \\text{subtract this element from} \\; S. \\newline\n",
    "\\text{3.} & \\text{repeat until the problem is solved}.\n",
    "\\end{aligned}\n",
    "$$\n",
    "\n",
    "The idea behind **Discrete Knapsack Cryptosystems** is quite simple. If we could construct superincreasing sequence and somehow obstruct it to the public eye, then it would appear that the best way to solve it is by using some kind of exponential algorithm. We could however easily find the solution by utilizing the greedy algorithm on unobstucted sequence.\n",
    "\n",
    "## Key creation\n",
    "Let's choose some superincreasing sequence $\\boldsymbol{r} = (r_1, ...., r_n)$ and two large secret integers $A$ and $B$ satisfying\n",
    "$$\n",
    "    B > 2r_n \\quad \\text{and} \\quad \\gcd(A,B) = 1.\n",
    "$$\n",
    "We can then create new sequence $\\boldsymbol{M}$ that is not superincreasing with the following method\n",
    "$$\n",
    "M_i \\equiv Ar_i \\mod B \\quad \\text{for all} \\; 1 \\leq i \\leq n\n",
    "$$\n",
    "$(\\boldsymbol{r}, A, B)$ is our **private key** and  \n",
    "$\\boldsymbol{M}$ is our **public key**."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "97f530ef-3074-430e-9e4a-f5b4bc59adf2",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import itertools\n",
    "import pqlattice as pq\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "80339c13-99e1-4cf9-96a7-fcc625c424eb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "private key: ([3, 11, 24, 50, 115], 113, 250)\n",
      "public key: [89, 243, 212, 150, 245]\n"
     ]
    }
   ],
   "source": [
    "is_superincreasing = lambda xs: all((j >= 2 * i for i,j in itertools.pairwise(xs)))\n",
    "\n",
    "r = [3,11,24,50,115]\n",
    "A = 113\n",
    "B = 250\n",
    "\n",
    "assert is_superincreasing(r)\n",
    "assert B > 2 * r[-1]\n",
    "assert math.gcd(A,B) == 1\n",
    "\n",
    "M = [(A * ri) % B for ri in r]\n",
    "\n",
    "assert not is_superincreasing(M)\n",
    "\n",
    "private_key = (r, A, B)\n",
    "public_key = M\n",
    "\n",
    "print(\"private key:\", private_key)\n",
    "print(\"public key:\", public_key)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f91da9ed-500d-4353-95d8-53ae7f8d28a3",
   "metadata": {},
   "source": [
    "# Encryption\n",
    "Our **plaintext** has a form of binary vector $\\boldsymbol{x}$  \n",
    "$i_{th}$ coefficient of the vector indicates if we count the $i_{th}$ sequence element into the knapsack sum.  \n",
    "The obtained sum $S$ is our ciphertext."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d8b0481c-5f66-4f68-b3c9-702a38394213",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "plaintext: [1, 0, 1, 0, 1]\n",
      "ciphertext: 546\n"
     ]
    }
   ],
   "source": [
    "x = [1,0,1,0,1]\n",
    "S = sum((xi * Mi for xi, Mi in zip(x, M) if xi))\n",
    "\n",
    "plaintext = x\n",
    "ciphertext = S\n",
    "\n",
    "print(\"plaintext:\", plaintext)\n",
    "print(\"ciphertext:\", ciphertext)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cec80ee-0a27-4f2c-a348-b1b96f04295b",
   "metadata": {},
   "source": [
    "# Decryption\n",
    "Upon receiving the sum $S$. We can transform it into \"space\" of our **superincreasing sequence**\n",
    "$$\n",
    "S' \\equiv A^{-1} S \\mod{B}\n",
    "$$\n",
    "Then we use the greedy algorithm on our **secret superincreasing sequence** to find the binary vector $\\boldsymbol{y}$ that will be our decrypted message."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f03f8c37-83ec-41b9-8603-5f61a5ac1dd5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "decrypted message: [1, 0, 1, 0, 1]\n"
     ]
    }
   ],
   "source": [
    "r, A, B = private_key\n",
    "M = public_key\n",
    "S = ciphertext\n",
    "\n",
    "Sprim = (pq.integer.modinv(A, B) * S) % B\n",
    "\n",
    "\n",
    "y = [0 for _ in r]\n",
    "for i, ri in enumerate(r[::-1]):\n",
    "    if ri <= Sprim:\n",
    "        y[i] = 1\n",
    "        Sprim = Sprim - ri\n",
    "\n",
    "y = y[::-1]\n",
    "print(\"decrypted message:\", y)\n",
    "assert y == x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34bd3b84-59ee-43cc-bda2-e84f98b743e1",
   "metadata": {},
   "source": [
    "# Attacking the cryptosystem"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "996d50fa-a8d2-4c3d-8dec-00802c3ec0e5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Subset sum lattice\n",
      "Matrix of integers with shape: 6 x 6\n",
      "====================================\n",
      "     [0]  [1]  [2]  [3]  [4]  [5]\n",
      "[0]    2    0    0    0    0   89\n",
      "[1]    0    2    0    0    0  243\n",
      "[2]    0    0    2    0    0  212\n",
      "[3]    0    0    0    2    0  150\n",
      "[4]    0    0    0    0    2  245\n",
      "[5]    1    1    1    1    1  546\n",
      "short vector in LLL reduced basis:\n",
      "w =  [-1 1 -1 1 -1 0]\n",
      "[-1 0 -1 0 -1]\n",
      "-546\n",
      "-546\n"
     ]
    }
   ],
   "source": [
    "n = len(M)\n",
    "A = pq.as_integer(np.identity(n + 1) * 2)\n",
    "A[-1] = 1\n",
    "A[:-1,-1] = M\n",
    "A[-1,-1] = S\n",
    "print(\"Subset sum lattice\")\n",
    "pq.show(A)\n",
    "\n",
    "A_lll = pq.lattice.lll(A)\n",
    "w = A_lll[0]\n",
    "print(\"short vector in LLL reduced basis:\")\n",
    "print(\"w = \", w)\n",
    "x = pq.as_integer(w @ np.linalg.inv(A.astype(float)))\n",
    "\n",
    "x, y = x[:-1], x[-1]\n",
    "print(x)\n",
    "print(np.dot(np.array(M), x))\n",
    "print(-y * S)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.12.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
