{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from os import urandom\n",
    "from hashlib import sha512"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## algorithm"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def rsa_generate_keys():\n",
    "    return [\n",
    "        65537,\n",
    "        58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137\n",
    "    ], [\n",
    "        32639742054323523661031580828650534544392003478949839063736255562124081596351847364013089886417596950354636310108218358259943735367279937975211699593540109138569129405212055903155962561652878992005591100527818545966603574053221236696683939389678915058929150433015761702105657992264877747720954135956649973789334911071168428227464085150820871588160770978551544646965210798269197906675922224772713666123225990305644372957419486169245295190574189157389340237417783311258488777336686103120891002317113842264416737708675921812070527474901946450952078789439410581693777829144977217172397092723130874770379072485175449578961,\n",
    "        58967658369561163583995664151705537612631456941226585145001736155445085885436956133402962616775555500479429922140321605063456075222335023020218578571558003435174909963319619244821157746252197885628802071763470174413201522569356053296685834595362968800778468737693074613267684084217204017873750446802044584084498581219849973790017343888256411013653688556278788070745635045095995056877259642839730825907965544973672656542601570609068817838234644958846427643088478240335082249677864789882511592486797239674160452077169411971273434857626735582274817190984442183721945999865859466422472845277588368259261760233826535480137\n",
    "    ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def bxor(x, y): \n",
    "    return bytes(i ^ j for i, j in zip(x, y))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def rsa_encrypt(plaintext, public_key):\n",
    "    # iv[64] -> h1[64] -> h2[64] -> h3[64]\n",
    "    iv = urandom(64)\n",
    "    h1 = sha512(iv).digest()\n",
    "    h2 = sha512(h1).digest()\n",
    "    h3 = sha512(h2).digest()\n",
    "    \n",
    "    # x[192] := pt[192] ^ (h1|h2|h3)[192]\n",
    "    pt = int.to_bytes(plaintext, 192, 'big')\n",
    "    x192 = bxor(pt, h1 + h2 + h3)\n",
    "    \n",
    "    # x[64] := iv[64] ^ x[192->64]\n",
    "    h4 = sha512(x192).digest()\n",
    "    x64 = bxor(iv, h4)\n",
    "\n",
    "    # x[256] := x[192]|x[64]\n",
    "    x256 = int.from_bytes(x192 + x64, 'big')\n",
    "\n",
    "    # rsa\n",
    "    return pow(x256, *public_key)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def rsa_decrypt(ciphertext, secret_key):\n",
    "    # rsa\n",
    "    x256 = pow(ciphertext, *secret_key)\n",
    "    \n",
    "    # x[192]|x[64] := x[256]\n",
    "    x256 = int.to_bytes(x256, 256, 'big')\n",
    "    x192, x64 = x256[:192], x256[192:]\n",
    "    \n",
    "    # iv[64] := x[64] ^ x[192->64]\n",
    "    h4 = sha512(x192).digest()\n",
    "    iv = bxor(x64, h4)\n",
    "\n",
    "    # iv[64] -> h1[64] -> h2[64] -> h3[64]\n",
    "    h1 = sha512(iv).digest()\n",
    "    h2 = sha512(h1).digest()\n",
    "    h3 = sha512(h2).digest()\n",
    "    \n",
    "    # pt[192] := x[192] ^ (h1|h2|h3)[192]\n",
    "    pt = bxor(x192, h1 + h2 + h3)\n",
    "    \n",
    "    # plaintext\n",
    "    return int.from_bytes(pt, 'big')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## RSA keys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "public_key, secret_key = rsa_generate_keys()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## dummy message #1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4649656022129871410084758708819082040705568659517051556579109783035475319993774294723543806613482815096122822053878200544644228348778357274817339362282168110066486550282665463011704367534599444948593483500223005519493074230630668627398499549531921631775796975543367931028635911620992746826279895004397331478316347706112728194245433531056959434149744818592679608607592411699224535481159970074779969735262841301579300176942131772715376140763764664390765116238642078898123587380387775631210841037675092874380628290147828147232010081353547182250999150829887572214726851792733663068257347681398919656884923709141501978704"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ciphertext = rsa_encrypt(0, public_key)\n",
    "plaintext = rsa_decrypt(ciphertext, secret_key)\n",
    "assert plaintext == 0\n",
    "ciphertext"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## dummy message #2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2415677512818998864159808469324913238979977385466951039435814444646249478008271176744226855135893423769242743432612025572303707000584164729306951233275711285198489275761956645351340457938947496004725650289180405814005966176789418536522136905331566410303015764282230218004512184067175477403903585752213765089378529719666774931767484072795951172112218811810533028840096512259305459794315163197200014614447695564147069690167232406952338511982673649011356652117898446604052520883179441268892168919553290957240304588682467546257412075550433499736393924753499707514346858917183759726898666883430903794585695045734222633925"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ciphertext = rsa_encrypt(0, public_key)\n",
    "plaintext = rsa_decrypt(ciphertext, secret_key)\n",
    "assert plaintext == 0\n",
    "ciphertext"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "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.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}