{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7539d460-1740-4414-a81a-d34eb05be97e",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mikolajlh/pqlattice/blob/main/docs/examples/LWE_primary_attack.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de3d984c-9038-4399-8c3d-4fdbd3a9ded3",
   "metadata": {},
   "source": [
    "# Primary attack on search-LWE problem\n",
    "In this notebook we are going to solve search-LWE by using primary attack.  \n",
    "First let's define the LWE.  \n",
    "Given parameters $n, q \\in \\mathbb{Z} $ and some non-uniform probability distribution $\\chi$ on $\\mathbb{Z}_q$ we define an LWE sample $(\\mathbf{a},b)$ for a secret vector $\\mathbf{s} \\in {\\mathbb{Z}^n_q}$ as follows\n",
    "$$\n",
    "\\begin{align*}\n",
    "\\mathbf{a} &\\leftarrow \\operatorname{Uniform}(\\mathbb{Z}^n_q) \\newline\n",
    "b &:= \\langle \\mathbf{a}, \\mathbf{s}\\rangle + e\n",
    "\\end{align*}\n",
    "$$\n",
    "where error $e$ is sampled from $\\chi$ distribution.\n",
    "$$\n",
    "\\mathbf{e} \\leftarrow \\chi\n",
    "$$\n",
    "As we take more samples, we can arrange them into a matrix/vector form. So for $m$ samples we define matrix $A \\in \\mathbb{Z}^{m\\times n}_q$ to be obtained from uniform distribution and use vector $\\mathbf{e} \\in \\mathbb{Z}^m$ for error\n",
    "$$\n",
    "\\begin{align*}\n",
    "A &\\leftarrow \\operatorname{Uniform}(\\mathbb{Z}^{m\\times n}_q) \\newline\n",
    "\\mathbf{e} &\\leftarrow \\chi^{m} \\newline\n",
    "\\mathbf{b} &:= A\\mathbf{s} + \\mathbf{e}\n",
    "\\end{align*}\n",
    "$$\n",
    "For search-LWE problem, given only pair $(A, \\mathbf{b})$ we need to obtain secret vector $\\mathbf{s}$.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "70ced2cc-d72d-4e0e-8c56-7155a430902a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pqlattice as pq\n",
    "import numpy as np\n",
    "import math\n",
    "pq.settings.set_backend(\"fast\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e952559f-a8cb-4f24-a979-f84e12e673ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 14\n",
    "sigma = 2\n",
    "q = 1000\n",
    "m = 50\n",
    "secret_dist = \"ternary\"\n",
    "\n",
    "possible_values = []\n",
    "if secret_dist == \"binary\":\n",
    "    possible_values = [0, 1]\n",
    "elif secret_dist == \"ternary\":\n",
    "    possible_values = [-1, 0, 1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "731a8b51-a078-4dbb-9c31-cb2d76c34539",
   "metadata": {},
   "outputs": [],
   "source": [
    "lwe = pq.random.LWE(n, q, sigma, secret_dist, 80)\n",
    "secret = lwe.secret\n",
    "A, b = lwe.sample_matrix(m)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5f788474-14a8-4bea-9d7d-dfd0e2f54c8c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Matrix of integers with shape: 65 x 65\n",
      "======================================\n",
      "       [0]   [1]   [2]   [3]   [4]  ...  [60]  [61]  [62]  [63]  [64]\n",
      " [0]  1000     0     0     0     0  ...     0     0     0     0     0\n",
      " [1]     0  1000     0     0     0  ...     0     0     0     0     0\n",
      " [2]     0     0  1000     0     0  ...     0     0     0     0     0\n",
      " [3]     0     0     0  1000     0  ...     0     0     0     0     0\n",
      " [4]     0     0     0     0  1000  ...     0     0     0     0     0\n",
      " ...   ...   ...   ...   ...   ...  ...   ...   ...   ...   ...   ...\n",
      "[60]   398   382   764   104   492  ...     1     0     0     0     0\n",
      "[61]   247   361     4   249   799  ...     0     1     0     0     0\n",
      "[62]   173   606   153   649   685  ...     0     0     1     0     0\n",
      "[63]   968   177   540   508   354  ...     0     0     0     1     0\n",
      "[64]   -16  -549  -665  -753  -979  ...     0     0     0     0     1\n"
     ]
    }
   ],
   "source": [
    "K = pq.lattice.embeddings.kannan(A, b, q)\n",
    "pq.show(K)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "943a267c-5121-4313-b3d8-17fa86b5f574",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "13.30413469565007\n"
     ]
    }
   ],
   "source": [
    "L = pq.lattice.lll(K)\n",
    "print(pq.linalg.norm(L[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1776be0c-d2a9-4400-9e49-929c3ab54c38",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "13.30413469565007\n"
     ]
    }
   ],
   "source": [
    "B = pq.lattice.bkz(L)\n",
    "print(pq.linalg.norm(B[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "52b7132b-03c8-4a39-9c30-691fe60e11a1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Recovered noise:  [-1 -4 -1 -3 0 1 0 0 1 3 2 0 2 1 -1 -1 1 6 0 0 -1 1 0 2 -1 1 0 -2 2 1 1 0\n",
      " 1 0 1 -2 0 -2 -3 1 -1 3 -2 1 -1 3 3 -2 2 -1]\n",
      "\n",
      "Recovered secret: [1 0 1 1 1 0 -1 0 1 0 1 1 1 0]\n",
      "True secret:      [1 0 1 1 1 0 -1 0 1 0 1 1 1 0]\n"
     ]
    }
   ],
   "source": [
    "v = B[0]\n",
    "e = v[:m]\n",
    "s = v[m:m + n]\n",
    "\n",
    "print(\"Recovered noise: \", e)\n",
    "print()\n",
    "print(\"Recovered secret:\", s)\n",
    "print(\"True secret:     \", secret)"
   ]
  }
 ],
 "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
}
