From 929c45bd6a9374fbe4e3da7da6ecd6dd9464cd6e Mon Sep 17 00:00:00 2001
From: kev-liao <kliao6@illinois.edu>
Date: Thu, 6 Sep 2018 15:11:32 -0500
Subject: [PATCH] hi

---
 mp1/zkp-assignment.ipynb | 665 +++++++++++++++++++++++++++++++++++++++
 mp1/zkp-assignment.py    | 540 +++++++++++++++++++++++++++++++
 2 files changed, 1205 insertions(+)
 create mode 100644 mp1/zkp-assignment.ipynb
 create mode 100644 mp1/zkp-assignment.py

diff --git a/mp1/zkp-assignment.ipynb b/mp1/zkp-assignment.ipynb
new file mode 100644
index 0000000..cc923ac
--- /dev/null
+++ b/mp1/zkp-assignment.ipynb
@@ -0,0 +1,665 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Zero Knowledge Proofs in Python\n",
+    "\n",
+    "Examples of discrete-log zero-knowledge proofs implemented in Python\n",
+    "\n",
+    "More specifically, these are non-interactive, zero-knowledge,\n",
+    "proofs of knowledge. They can be analyzed and proven secure\n",
+    "in the random oracle model (the random oracle here is instantiated\n",
+    "with the SHA2 hash function).\n",
+    "\n",
+    "Lecture notes:\n",
+    "   http://soc1024.ece.illinois.edu/teaching/ece498am/fall2017/\n",
+    "   https://www.cs.jhu.edu/~susan/600.641/scribes/lecture10.pdf\n",
+    "   https://www.cs.jhu.edu/~susan/600.641/scribes/lecture11.pdf\n",
+    "   http://soc1024.web.engr.illinois.edu/teaching/ece598am/fall2016/zkproofs.pdf\n",
+    "   \n",
+    "You must fill in the portions labelled #TODO. See the README.md in this\n",
+    "directory for submission instructions. Points are awarded as marked.\n",
+    "Total possible points (not including bonus): 105"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Import Elliptic Curves\n",
+    "\n",
+    "The zero-knowledge proof schemes we work through here \n",
+    " can work with any DLog group. This implementation makes use of\n",
+    "the secp256k1 elliptic curve group. We call an element of this group\n",
+    "(i.e., a point on the curve), simply a Point.\n",
+    "\n",
+    "The order of this group, p, is a 256-bit prime number. Furthermore, p\n",
+    "happens to be extremely close to 2^256. Because of this, we can sample\n",
+    "exponents easily by choosing a random 32-byte number, and with high probability,\n",
+    "will be within [0,p).\n",
+    "   uint256_from_str(rnd_bytes(32)) is an exponent.\n",
+    "\n",
+    "Sometimes this will be represented by the object Fp, which automatically handles\n",
+    "arithmetic modulo p. The underlying 'long' value can be extracted as `p.n` if \n",
+    "`type(p) is Fp`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "import secp256k1\n",
+    "from secp256k1 import Point, q, Fq, order, p, Fp, G, curve, ser, deser, uint256_from_str, uint256_to_str, make_random_point\n",
+    "import os, random\n",
+    "\n",
+    "# p is the order (the # of elements in) the group, i.e., the number of points on the curve\n",
+    "# order = p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141\n",
+    "print order\n",
+    "print Fp  # Fp is the group of exponents (integers mod p)\n",
+    "\n",
+    "# ser/deser: convert Point -> string and vice versa\n",
+    "#   ser : Point -> str, deser : str -> Point"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "print repr(G)\n",
+    "print repr(p * G)\n",
+    "print deser(ser(G))\n",
+    "\n",
+    "Hx = Fq(0x8E3871A594F9AF7A1F357A0793124AAF3358B0F020983678BCD411EE6AF387A5L)\n",
+    "Hy = Fq(0x83BFAA8176272D0E4D7AD3577F3A0A3B70D7E1BFC2B638CA2807562F2CA85F59L)\n",
+    "H = Point(curve, Hx,Hy)\n",
+    "# H = random_point(seed=sha2(\"H\")) # An alternate generator"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Random Oracle Functions\n",
+    "We need ways of sampling random strings, random integers in Z_p,\n",
+    "and random points in the group."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "## Default random oracle\n",
+    "def make_random_oracle():\n",
+    "    \"\"\"This function returns a new random oracle, `RO`.\n",
+    "    The random oracle maps arbitrary-length input strings `s` to\n",
+    "    a 32-byte digest. It is initialized with an empty dictionary. \n",
+    "    Each time RO is queried with a new value, a\n",
+    "    random response is sampled and stored.\"\"\"\n",
+    "\n",
+    "    _mapping = {}\n",
+    "    def RO(s):\n",
+    "        assert type(s) is str\n",
+    "        if not s in _mapping:\n",
+    "            _mapping[s] = os.urandom(32)\n",
+    "        return _mapping[s]\n",
+    "    return RO\n",
+    "\n",
+    "# Random oracle instantiated with SHA2 hash\n",
+    "def sha2(x):\n",
+    "    from Crypto.Hash import SHA256\n",
+    "    return SHA256.new(x).digest()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Preliminary example: Proof of knowledge of discrete logarithm\n",
+    "\n",
+    "In this part, we provide a scheme offers a discrete log proof of `ZKP{ (a): A = a*G }`.\n",
+    "\n",
+    "Note that the statement `A` is a parameter to the scheme, as it must\n",
+    "be known to both the prover and verifier.\n",
+    "\n",
+    "The Prover takes several additional arguments:\n",
+    "\n",
+    " - `rnd_bytes`, such that `rnd_bytes(n)` returns an `n`-byte random string. By default, will use the operating system os.urandom. \n",
+    "\n",
+    "    (Note: as this function is non-blocking, may be a poor choice if the OS runs out of entropy)\n",
+    "\n",
+    " - RO, a random oracle, such that `RO(s)` where `s` is an arbitrary length string, returns a randomly chosen value. By default, will use the sha2 hash.\n",
+    "\n",
+    "These can be overridden in later section as part of the security proof constructions."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def dlog_prover(A, a, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    assert a*G == A\n",
+    "\n",
+    "    # blinding factor\n",
+    "    k = uint256_from_str(rnd_bytes(32)) % order\n",
+    "\n",
+    "    # commitment\n",
+    "    K = k*G\n",
+    "\n",
+    "    # Invoke the random oracle to receive a challenge\n",
+    "    c = uint256_from_str(RO(ser(K)))\n",
+    "\n",
+    "    # response\n",
+    "    s = Fp(k + c*a)\n",
+    "\n",
+    "    return (K,s)\n",
+    "\n",
+    "\n",
+    "def dlog_verifier(A, prf, RO=sha2):\n",
+    "    (K,s) = prf\n",
+    "    assert type(A) is type(K) is Point\n",
+    "    assert type(s) is Fp\n",
+    "\n",
+    "    # Recompute c w/ the information given\n",
+    "    c = uint256_from_str(RO(ser(K)))\n",
+    "\n",
+    "    # Check the verification condition\n",
+    "    assert s.n *G == K + c*A\n",
+    "    return True\n",
+    "\n",
+    "\n",
+    "def dlog_test():\n",
+    "    a = uint256_from_str(os.urandom(32))\n",
+    "    A = a*G\n",
+    "    prf = dlog_prover(A, a)\n",
+    "    assert dlog_verifier(A, prf)\n",
+    "    print 'Dlog correctness test complete!'\n",
+    "\n",
+    "dlog_test()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 1: Make a Pedersen commitment to your crypto egg. \n",
+    " Provide a ZK proof that your commitment is correct.\n",
+    "\n",
+    "   Zk{ (x,r): X = x*G, C = x*G + r*H }\n",
+    "\n",
+    "By completing this proof, you prove you still have knowledge of your egg!\n",
+    "without revealing which.\n",
+    "\n",
+    "The verifier is provided for you. (Since we will publicly verify the proofs). You must complete the prover."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def make_pedersen_commitment(x, rnd_bytes=os.urandom):\n",
+    "    r = uint256_from_str(rnd_bytes(32))\n",
+    "    C = x * G + r * H\n",
+    "    return C, r\n",
+    "\n",
+    "def pedersen_prover(C, X, x, r, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    \"\"\"\n",
+    "    Params: \n",
+    "       x and r are elements of Fp\n",
+    "       C,X are Points\n",
+    "    Returns:\n",
+    "       prf, of the form (KX,KC,sx,sr)\n",
+    "    \"\"\"\n",
+    "    assert X == x * G\n",
+    "    assert C == x * G + r * H\n",
+    "\n",
+    "    # TODO: fill in your code here (10 points)\n",
+    "\n",
+    "    return (KX,KC,sx,sr)\n",
+    "\n",
+    "def pedersen_verifier(C, X, prf, RO=sha2):\n",
+    "    (KX,KC,sx,sr) = prf\n",
+    "    assert type(KX) == type(KC) == Point\n",
+    "    assert type(sx) == type(sr) == Fp\n",
+    "\n",
+    "    # Recompute c w/ the information given\n",
+    "    c = uint256_from_str(RO(ser(KX) + ser(KC)))\n",
+    "\n",
+    "    assert sx.n *G            == KX + c*X\n",
+    "    assert sx.n *G + sr.n *H  == KC + c*C\n",
+    "    return True\n",
+    "\n",
+    "def pedersen_test():\n",
+    "\n",
+    "    x = uint256_from_str(os.urandom(32))\n",
+    "    X = x * G\n",
+    "    C,r = make_pedersen_commitment(x)\n",
+    "\n",
+    "    prf = pedersen_prover(C, X, x, r)\n",
+    "    (KX, KC, sx, sr) = prf\n",
+    "    print repr((ser(C), ser(KX),ser(KC),uint256_to_str(sx.n).encode('hex'),uint256_to_str(sr.n).encode('hex')))\n",
+    "\n",
+    "    assert pedersen_verifier(C, X, prf)\n",
+    "    print \"Pedersen correctness test complete!\"\n",
+    "\n",
+    "pedersen_test()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 2. Arithmetic relations\n",
+    "\n",
+    "Example: a more complicated discrete log proof\n",
+    "      Zk{ (a, b):  A=a*G, B=b*G,  C = (a*(b+3)) * G }\n",
+    "\n",
+    "First rewrite as:\n",
+    "      Zk{ (a, b):  A=a*G, B=b*G,  (C-3*A) = b*A) }\n",
+    "\n",
+    "You need to implement a prover and verifier for the above scheme."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def arith_prover(a, b, A, B, C, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    \"\"\"\n",
+    "    Params: \n",
+    "       a and b are elements of Fp\n",
+    "       A, B, C are Points\n",
+    "    Returns:\n",
+    "       prf, of the form (KA,KB,KC,sa,sb)\n",
+    "\n",
+    "    Must satisfy verify_proof2(A, B, C, prf)\n",
+    "    Must be zero-knowledge\n",
+    "    \"\"\"\n",
+    "    assert a*G == A\n",
+    "    assert b*G == B\n",
+    "    assert (a*(b+3))*G == C\n",
+    "\n",
+    "    # TODO: fill in your code here (10 points)\n",
+    "\n",
+    "def arith_verifier(A, B, C, prf, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    (KA,KB,KC,sa,sb) = prf\n",
+    "    assert type(KA) == type(KB) == type(KC) == Point\n",
+    "    assert type(sa) == type(sb) == Fp\n",
+    "\n",
+    "    # TODO: fill in your code here (10 points)\n",
+    "\n",
+    "def arith_test():\n",
+    "    # Randomly choose \"a\" and \"b\"\n",
+    "    a = uint256_from_str(os.urandom(32))\n",
+    "    b = uint256_from_str(os.urandom(32))\n",
+    "    A = a*G\n",
+    "    B = b*G\n",
+    "    C = (a*(b+3)) * G\n",
+    "\n",
+    "    prf = arith_prover(a, b, A, B, C)\n",
+    "    assert arith_verifier(A, B, C, prf)\n",
+    "    print \"Arithmetic Relation correctness test complete\"\n",
+    "\n",
+    "arith_test()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 3. OR composition\n",
+    "\n",
+    "In this part you will need to prove knowledge of one of two possible secrets,\n",
+    "\n",
+    "   Zk{ (a,b): A = a*G    OR    B = b*G }\n",
+    "\n",
+    "without revealing which one it is you know.\n",
+    "\n",
+    "The verifier is provided for you."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def OR_prover(A, B, x, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    assert x*G == A or x*G == B\n",
+    "\n",
+    "    # TODO: Fill your code in here (20 points)\n",
+    "\n",
+    "    return (KA,KB,sa,sb,ca,cb)\n",
+    "\n",
+    "def OR_verifier(A, B, prf, RO=sha2):\n",
+    "    (KA,KB,sa,sb,ca,cb) = prf\n",
+    "    assert type(KA) is type(KB) is Point\n",
+    "    assert type(sa) is type(sb) is Fp\n",
+    "\n",
+    "    # Check the challenges are correctly constrained\n",
+    "    c = uint256_from_str(RO(ser(KA) + ser(KB)))\n",
+    "    assert (ca + cb) % p == c\n",
+    "\n",
+    "    # Check each proof the same way\n",
+    "    assert sa.n *G == KA + ca*A\n",
+    "    assert sb.n *G == KB + cb*B\n",
+    "\n",
+    "    return True\n",
+    "\n",
+    "def OR_test1():\n",
+    "    # Try first way\n",
+    "    a = uint256_from_str(os.urandom(32))\n",
+    "    A = a*G\n",
+    "    B = make_random_point()\n",
+    "\n",
+    "    prf = OR_prover(A, B, a)\n",
+    "    assert OR_verifier(A, B, prf)\n",
+    "    print \"OR composition correctness 1 test complete!\"\n",
+    "\n",
+    "def OR_test2():\n",
+    "    # Try second way\n",
+    "    b = uint256_from_str(os.urandom(32))\n",
+    "    A = make_random_point()\n",
+    "    B = b*G\n",
+    "\n",
+    "    prf = OR_prover(A, B, b)\n",
+    "    assert OR_verifier(A, B, prf)\n",
+    "    print \"OR composition correctness 2 test complete!\"\n",
+    "\n",
+    "OR_test1()\n",
+    "OR_test2()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 4. Schnorr signature\n",
+    "\n",
+    "  We can write a Schnor signature as:\n",
+    "\n",
+    "     SoK[m] { (x): X = x*G }\n",
+    "\n",
+    "  Similar to part 1, except we the challenge is derived from the message."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def schnorr_sign(x, m, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    assert type(x) is str\n",
+    "    assert type(m) is str\n",
+    "\n",
+    "    # TODO: Your code goes here (10 points)\n",
+    "\n",
+    "def schnorr_verify(X, m, sig, RO=sha2):\n",
+    "    assert type(X) is Point\n",
+    "    assert type(sig) is str and len(sig) is 65\n",
+    "    (K,s) = deser(sig[:33].encode('hex')), uint256_from_str(sig[33:])\n",
+    "    c = uint256_from_str(RO(ser(K) + sha2(m)))\n",
+    "    assert s *G == K + c*X\n",
+    "    return True\n",
+    "\n",
+    "def schnorr_test():\n",
+    "    msg = \"hello\"\n",
+    "\n",
+    "    x = os.urandom(32)\n",
+    "    X = uint256_from_str(x) * G\n",
+    "\n",
+    "    sig = schnorr_sign(x, msg)\n",
+    "    assert schnorr_verify(X, msg, sig)\n",
+    "    print \"Schnorr Test complete\"\n",
+    "\n",
+    "schnorr_test()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 5. Range proofs\n",
+    "\n",
+    "- Create a proof that C is a commitment to a, and a is in the range [0,31].\n",
+    "\n",
+    "    Zk{ (a, r): C = g^a h^r and  0 <= a <= 31 }\n",
+    "\n",
+    "  Hint: You can implement this by creating commitments to the binary expansion\n",
+    "    of a, and then proving the following:\n",
+    "\n",
+    "    Zk{ (b0, b1, ... b4, r, r0, r1, ..., r4):\n",
+    "               A = g^(b0 + 2*b1 + ... + 16*b4) h^r\n",
+    "               and  (C0 = g^(b0) h^r0) ...\n",
+    "               and  (C0 = g h^r0 OR C0 = h^r0) ... }\n",
+    "\n",
+    "  Hint: to avoid too much repetition, consider implementing the Bonus question first."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def range_prover(a, r, C, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    assert type(C) is Point\n",
+    "    assert a*G + r*H == C\n",
+    "\n",
+    "    # TODO: fill in your code here (10 points)\n",
+    "\n",
+    "def range_verifier(C, prf, rnd_bytes=os.urandom, RO=sha2):\n",
+    "    assert type(C) is Point\n",
+    "\n",
+    "    # TODO: fill in your code here (10 points)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 6: Extractor and simulator\n",
+    "\n",
+    "In this part, you will implement in code a portion of the security proof\n",
+    "for the discrete log proof scheme from the Preliminary."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "def dlog_extractor(A, Adv):\n",
+    "    assert type(A) is Point\n",
+    "\n",
+    "    ## Step 1: run the adversary to generate a proof while recording\n",
+    "    ## the random bits and oracle queries\n",
+    "\n",
+    "    # TODO: Fill your code in here (5 points)\n",
+    "\n",
+    "    ## Step 2: run the adversary again, replaying the random bits,\n",
+    "    ## and intercepting the call to the random oracle\n",
+    "\n",
+    "    # TODO: Fill your code in here (5 points)\n",
+    "\n",
+    "    ## Step 3: Extract a witness from the two proofs and oracle queries\n",
+    "\n",
+    "    # TODO: Fill your code in here (5 points)\n",
+    "\n",
+    "def dlog_test_extractor():\n",
+    "    # Make a test case based on running the real prover\n",
+    "    a = uint256_from_str(os.urandom(32))\n",
+    "    A = a * G\n",
+    "\n",
+    "    def Adv(A, rnd_bytes, RO):\n",
+    "        assert A == a * G\n",
+    "        return dlog_prover(A, a, rnd_bytes, RO)\n",
+    "\n",
+    "    a_ = dlog_extractor(A, Adv)\n",
+    "    assert a == a_\n",
+    "    print 'Extractor test complete!'\n",
+    "\n",
+    "def dlog_test_extractor_harder():\n",
+    "    # Make a test case based on running a \"picky\" prover\n",
+    "    a = uint256_from_str(os.urandom(32))\n",
+    "    A = a * G\n",
+    "\n",
+    "    def Adv(A, rnd_bytes, RO):\n",
+    "        assert A == a * G\n",
+    "\n",
+    "        while True:\n",
+    "            # The \"picky\" prover loops until it is happy\n",
+    "\n",
+    "            # Make a whimsical decision\n",
+    "            coin = rnd_bytes(1)\n",
+    "            if ord(coin) < 128: continue\n",
+    "\n",
+    "            k = uint256_from_str(rnd_bytes(32)) % order\n",
+    "            K = k*G\n",
+    "            c = uint256_from_str(RO(ser(K)))\n",
+    "\n",
+    "            # I only like challenges that end with 3 zero bits\n",
+    "            if c & 0b111 != 0: continue\n",
+    "\n",
+    "            # OK I'm satisfied\n",
+    "            s = Fp(k + c*a)\n",
+    "            return (K, s)\n",
+    "\n",
+    "    a_ = dlog_extractor(A, Adv)\n",
+    "    assert a == a_\n",
+    "    print 'Extractor test complete!'\n",
+    "\n",
+    "dlog_test_extractor()\n",
+    "dlog_test_extractor_harder()\n",
+    "\n",
+    "def dlog_simulator(A, rnd_bytes):\n",
+    "    \"\"\"\n",
+    "    Returns:\n",
+    "    - (prf, RO)\n",
+    "    - prf, a tuple of the form (K,s),\n",
+    "        where K is a Point\n",
+    "        and s is an element of Fp\n",
+    "    - RO is a random oracle function\n",
+    "\n",
+    "    The following must hold:\n",
+    "    - RO is a mapping from to 32-bit strings. Its output should be indistinguishable from an ordinary random oracle generated from `make_random_oracle`.\n",
+    "    \"\"\"\n",
+    "    # TODO: Fill in your code here (10 points)\n",
+    "\n",
+    "def dlog_test_simulator():\n",
+    "    rnd_bytes = os.urandom\n",
+    "    # Test with a randomly generated point on curve\n",
+    "    A = make_random_point(rnd_bytes)\n",
+    "\n",
+    "    (prf, RO) = dlog_simulator(A, rnd_bytes)\n",
+    "\n",
+    "    # The proof must verify\n",
+    "    assert dlog_verifier(A, prf, RO=RO)\n",
+    "    print \"DLOG simulator test complete!\"\n",
+    "\n",
+    "dlog_test_simulator()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Bonus Challenge: Expression compiler\n",
+    "\n",
+    "  Can you take a description of a ZK scheme in Camenisch-Stadler notation,\n",
+    "  and automatically generate the protocol for it?\n",
+    "\n",
+    "  Read the ZKPDL paper for inspiration (that is a much more full-featured system!)\n",
+    "\n",
+    "  To complete this bonus, the goal should be to support the following\n",
+    "  grammar for expressing ZK constraint systems:\n",
+    "\n",
+    "    ZK := EQN | ZK and ZK | ZK or ZK\n",
+    "    EQN := BEXP = BEXP\n",
+    "    BEXP := BEXP * BEXP | BEXP / BEXP | BEXP ^ EEXP | BIDENT\n",
+    "    EEXP := EEXP + EEXP | EEXP - EEXP | EEXP * EEXP | EEXP / EEXP | EIDENT | ELIT\n",
+    "    BIDENT := 'G', 'H', ...\n",
+    "    EIDENT := 'a','b', ...\n",
+    "    ELIT := 1,2,3, ...\n",
+    "\n",
+    "  Example:\n",
+    "   \"Zk{ (a,b): A = G^(a) * (H^10)^(3*b)\"\n",
+    "\n",
+    "  would be expressed as:\n",
+    "  ZK(\n",
+    "   EQN( \n",
+    "    BEXP( BIDENT('A') )\n",
+    "   = \n",
+    "    BEXP( \n",
+    "     BEXP( BIDENT('G') ^ EEXP( EIDENT('a') ) )\n",
+    "    *\n",
+    "     BEXP( \n",
+    "      BEXP( BEXP(BIDENT('H')) ^ EEXP(ELIT(10)) )\n",
+    "     ^\n",
+    "      EEXP(ELIT(3) * EIDENT('b'))\n",
+    "     )\n",
+    "    )\n",
+    "   )\n",
+    "  )\n",
+    "\n",
+    "  Hints:\n",
+    "  - Build an abstract syntax tree to traverse.\n",
+    "  - Flatten AND and OR to a single list\n",
+    "  - Apply operations to left and right sides until left side has no exponents\n",
+    "  - To handle multiplications, introduce ephemeral commitments (like in Part 5)\n",
+    "\n",
+    "    For example, to prove\n",
+    "      Zk{ (a,b,c): A = g^a, B = g^b, C = g^c, D = g^(abc) }\n",
+    "\n",
+    "    you could introduce an ephemeral commitment:\n",
+    "\n",
+    "      E = g^(ab) h^r, for random r\n",
+    "\n",
+    "    Then the extended proof is:\n",
+    "      Zk{ (a,b,c,r,r2): A = g^a, B = g^b, C = g^c, E = A^b h^r, D = E^c h^r2 }\n",
+    "\n",
+    "    where the prover sets r2 = -(c*r).\n",
+    "\n",
+    "    This now has the property that the only EEXP terms are EEXP(EIDENT(_))."
+   ]
+  }
+ ],
+ "metadata": {},
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/mp1/zkp-assignment.py b/mp1/zkp-assignment.py
new file mode 100644
index 0000000..4c7c691
--- /dev/null
+++ b/mp1/zkp-assignment.py
@@ -0,0 +1,540 @@
+"""
+# Zero Knowledge Proofs in Python
+
+Examples of discrete-log zero-knowledge proofs implemented in Python
+
+More specifically, these are non-interactive, zero-knowledge,
+proofs of knowledge. They can be analyzed and proven secure
+in the random oracle model (the random oracle here is instantiated
+with the SHA2 hash function).
+
+Lecture notes:
+   http://soc1024.ece.illinois.edu/teaching/ece498am/fall2017/
+   https://www.cs.jhu.edu/~susan/600.641/scribes/lecture10.pdf
+   https://www.cs.jhu.edu/~susan/600.641/scribes/lecture11.pdf
+   http://soc1024.web.engr.illinois.edu/teaching/ece598am/fall2016/zkproofs.pdf
+   
+You must fill in the portions labelled #TODO. See the README.md in this
+directory for submission instructions. Points are awarded as marked.
+Total possible points (not including bonus): 105
+"""
+
+
+"""
+## Import Elliptic Curves
+
+The zero-knowledge proof schemes we work through here 
+ can work with any DLog group. This implementation makes use of
+the secp256k1 elliptic curve group. We call an element of this group
+(i.e., a point on the curve), simply a Point.
+
+The order of this group, p, is a 256-bit prime number. Furthermore, p
+happens to be extremely close to 2^256. Because of this, we can sample
+exponents easily by choosing a random 32-byte number, and with high probability,
+will be within [0,p).
+   uint256_from_str(rnd_bytes(32)) is an exponent.
+
+Sometimes this will be represented by the object Fp, which automatically handles
+arithmetic modulo p. The underlying 'long' value can be extracted as `p.n` if 
+`type(p) is Fp`.
+"""
+
+import secp256k1
+from secp256k1 import Point, q, Fq, order, p, Fp, G, curve, ser, deser, uint256_from_str, uint256_to_str, make_random_point
+import os, random
+
+# p is the order (the # of elements in) the group, i.e., the number of points on the curve
+# order = p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
+print order
+print Fp  # Fp is the group of exponents (integers mod p)
+
+# ser/deser: convert Point -> string and vice versa
+#   ser : Point -> str, deser : str -> Point
+
+"""
+"""
+print repr(G)
+print repr(p * G)
+print deser(ser(G))
+
+Hx = Fq(0x8E3871A594F9AF7A1F357A0793124AAF3358B0F020983678BCD411EE6AF387A5L)
+Hy = Fq(0x83BFAA8176272D0E4D7AD3577F3A0A3B70D7E1BFC2B638CA2807562F2CA85F59L)
+H = Point(curve, Hx,Hy)
+# H = random_point(seed=sha2("H")) # An alternate generator
+
+"""
+## Random Oracle Functions
+We need ways of sampling random strings, random integers in Z_p,
+and random points in the group.
+"""
+
+## Default random oracle
+def make_random_oracle():
+    """This function returns a new random oracle, `RO`.
+    The random oracle maps arbitrary-length input strings `s` to
+    a 32-byte digest. It is initialized with an empty dictionary. 
+    Each time RO is queried with a new value, a
+    random response is sampled and stored."""
+    
+    _mapping = {}
+    def RO(s):
+        assert type(s) is str
+        if not s in _mapping:
+            _mapping[s] = os.urandom(32)
+        return _mapping[s]
+    return RO
+
+# Random oracle instantiated with SHA2 hash
+def sha2(x):
+    from Crypto.Hash import SHA256
+    return SHA256.new(x).digest()
+
+
+"""
+## Preliminary example: Proof of knowledge of discrete logarithm
+
+In this part, we provide a scheme offers a discrete log proof of `ZKP{ (a): A = a*G }`.
+
+Note that the statement `A` is a parameter to the scheme, as it must
+be known to both the prover and verifier.
+
+The Prover takes several additional arguments:
+
+ - `rnd_bytes`, such that `rnd_bytes(n)` returns an `n`-byte random string. By default, will use the operating system os.urandom. 
+
+    (Note: as this function is non-blocking, may be a poor choice if the OS runs out of entropy)
+
+ - RO, a random oracle, such that `RO(s)` where `s` is an arbitrary length string, returns a randomly chosen value. By default, will use the sha2 hash.
+
+These can be overridden in later section as part of the security proof constructions.
+"""
+def dlog_prover(A, a, rnd_bytes=os.urandom, RO=sha2):
+    assert a*G == A
+
+    # blinding factor
+    k = uint256_from_str(rnd_bytes(32)) % order
+
+    # commitment
+    K = k*G
+
+    # Invoke the random oracle to receive a challenge
+    c = uint256_from_str(RO(ser(K)))
+
+    # response
+    s = Fp(k + c*a)
+
+    return (K,s)
+
+
+def dlog_verifier(A, prf, RO=sha2):
+    (K,s) = prf
+    assert type(A) is type(K) is Point
+    assert type(s) is Fp
+
+    # Recompute c w/ the information given
+    c = uint256_from_str(RO(ser(K)))
+
+    # Check the verification condition
+    assert s.n *G == K + c*A
+    return True
+
+
+def dlog_test():
+    a = uint256_from_str(os.urandom(32))
+    A = a*G
+    prf = dlog_prover(A, a)
+    assert dlog_verifier(A, prf)
+    print 'Dlog correctness test complete!'
+
+dlog_test()
+
+"""
+## Part 1: Make a Pedersen commitment to your crypto egg. 
+ Provide a ZK proof that your commitment is correct.
+
+   Zk{ (x,r): X = x*G, C = x*G + r*H }
+
+By completing this proof, you prove you still have knowledge of your egg!
+without revealing which.
+
+The verifier is provided for you. (Since we will publicly verify the proofs). You must complete the prover.
+"""
+
+def make_pedersen_commitment(x, rnd_bytes=os.urandom):
+    r = uint256_from_str(rnd_bytes(32))
+    C = x * G + r * H
+    return C, r
+
+def pedersen_prover(C, X, x, r, rnd_bytes=os.urandom, RO=sha2):
+    """
+    Params: 
+       x and r are elements of Fp
+       C,X are Points
+    Returns:
+       prf, of the form (KX,KC,sx,sr)
+    """
+    assert X == x * G
+    assert C == x * G + r * H
+
+    # TODO: fill in your code here (10 points)
+
+    return (KX,KC,sx,sr)
+
+def pedersen_verifier(C, X, prf, RO=sha2):
+    (KX,KC,sx,sr) = prf
+    assert type(KX) == type(KC) == Point
+    assert type(sx) == type(sr) == Fp
+
+    # Recompute c w/ the information given
+    c = uint256_from_str(RO(ser(KX) + ser(KC)))
+
+    assert sx.n *G            == KX + c*X
+    assert sx.n *G + sr.n *H  == KC + c*C
+    return True
+
+def pedersen_test():
+    
+    x = uint256_from_str(os.urandom(32))
+    X = x * G
+    C,r = make_pedersen_commitment(x)
+
+    prf = pedersen_prover(C, X, x, r)
+    (KX, KC, sx, sr) = prf
+    print repr((ser(C), ser(KX),ser(KC),uint256_to_str(sx.n).encode('hex'),uint256_to_str(sr.n).encode('hex')))
+    
+    assert pedersen_verifier(C, X, prf)
+    print "Pedersen correctness test complete!"
+
+pedersen_test()
+
+
+"""
+## Part 2. Arithmetic relations
+
+Example: a more complicated discrete log proof
+      Zk{ (a, b):  A=a*G, B=b*G,  C = (a*(b+3)) * G }
+
+First rewrite as:
+      Zk{ (a, b):  A=a*G, B=b*G,  (C-3*A) = b*A) }
+
+You need to implement a prover and verifier for the above scheme.
+"""
+
+def arith_prover(a, b, A, B, C, rnd_bytes=os.urandom, RO=sha2):
+    """
+    Params: 
+       a and b are elements of Fp
+       A, B, C are Points
+    Returns:
+       prf, of the form (KA,KB,KC,sa,sb)
+
+    Must satisfy verify_proof2(A, B, C, prf)
+    Must be zero-knowledge
+    """
+    assert a*G == A
+    assert b*G == B
+    assert (a*(b+3))*G == C
+
+    # TODO: fill in your code here (10 points)
+
+def arith_verifier(A, B, C, prf, rnd_bytes=os.urandom, RO=sha2):
+    (KA,KB,KC,sa,sb) = prf
+    assert type(KA) == type(KB) == type(KC) == Point
+    assert type(sa) == type(sb) == Fp
+
+    # TODO: fill in your code here (10 points)
+
+def arith_test():
+    # Randomly choose "a" and "b"
+    a = uint256_from_str(os.urandom(32))
+    b = uint256_from_str(os.urandom(32))
+    A = a*G
+    B = b*G
+    C = (a*(b+3)) * G
+
+    prf = arith_prover(a, b, A, B, C)
+    assert arith_verifier(A, B, C, prf)
+    print "Arithmetic Relation correctness test complete"
+
+arith_test()
+
+"""
+## Part 3. OR composition
+
+In this part you will need to prove knowledge of one of two possible secrets,
+
+   Zk{ (a,b): A = a*G    OR    B = b*G }
+
+without revealing which one it is you know.
+
+The verifier is provided for you.
+"""
+
+def OR_prover(A, B, x, rnd_bytes=os.urandom, RO=sha2):
+    assert x*G == A or x*G == B
+
+    # TODO: Fill your code in here (20 points)
+
+    return (KA,KB,sa,sb,ca,cb)
+
+def OR_verifier(A, B, prf, RO=sha2):
+    (KA,KB,sa,sb,ca,cb) = prf
+    assert type(KA) is type(KB) is Point
+    assert type(sa) is type(sb) is Fp
+
+    # Check the challenges are correctly constrained
+    c = uint256_from_str(RO(ser(KA) + ser(KB)))
+    assert (ca + cb) % p == c
+
+    # Check each proof the same way
+    assert sa.n *G == KA + ca*A
+    assert sb.n *G == KB + cb*B
+
+    return True
+
+def OR_test1():
+    # Try first way
+    a = uint256_from_str(os.urandom(32))
+    A = a*G
+    B = make_random_point()
+
+    prf = OR_prover(A, B, a)
+    assert OR_verifier(A, B, prf)
+    print "OR composition correctness 1 test complete!"
+
+def OR_test2():
+    # Try second way
+    b = uint256_from_str(os.urandom(32))
+    A = make_random_point()
+    B = b*G
+
+    prf = OR_prover(A, B, b)
+    assert OR_verifier(A, B, prf)
+    print "OR composition correctness 2 test complete!"
+
+OR_test1()
+OR_test2()
+
+
+"""
+## Part 4. Schnorr signature
+
+  We can write a Schnor signature as:
+
+     SoK[m] { (x): X = x*G }
+
+  Similar to part 1, except we the challenge is derived from the message.
+"""
+def schnorr_sign(x, m, rnd_bytes=os.urandom, RO=sha2):
+    assert type(x) is str
+    assert type(m) is str
+
+    # TODO: Your code goes here (10 points)
+
+def schnorr_verify(X, m, sig, RO=sha2):
+    assert type(X) is Point
+    assert type(sig) is str and len(sig) is 65
+    (K,s) = deser(sig[:33].encode('hex')), uint256_from_str(sig[33:])
+    c = uint256_from_str(RO(ser(K) + sha2(m)))
+    assert s *G == K + c*X
+    return True
+
+def schnorr_test():
+    msg = "hello"
+
+    x = os.urandom(32)
+    X = uint256_from_str(x) * G
+    
+    sig = schnorr_sign(x, msg)
+    assert schnorr_verify(X, msg, sig)
+    print "Schnorr Test complete"
+
+schnorr_test()
+
+
+"""
+## Part 5. Range proofs
+
+- Create a proof that C is a commitment to a, and a is in the range [0,31].
+
+    Zk{ (a, r): C = g^a h^r and  0 <= a <= 31 }
+
+  Hint: You can implement this by creating commitments to the binary expansion
+    of a, and then proving the following:
+
+    Zk{ (b0, b1, ... b4, r, r0, r1, ..., r4):
+               A = g^(b0 + 2*b1 + ... + 16*b4) h^r
+               and  (C0 = g^(b0) h^r0) ...
+               and  (C0 = g h^r0 OR C0 = h^r0) ... }
+
+  Hint: to avoid too much repetition, consider implementing the Bonus question first.
+
+"""
+def range_prover(a, r, C, rnd_bytes=os.urandom, RO=sha2):
+    assert type(C) is Point
+    assert a*G + r*H == C
+    
+    # TODO: fill in your code here (10 points)
+
+def range_verifier(C, prf, rnd_bytes=os.urandom, RO=sha2):
+    assert type(C) is Point
+
+    # TODO: fill in your code here (10 points)
+
+
+"""
+## Part 6: Extractor and simulator
+
+In this part, you will implement in code a portion of the security proof
+for the discrete log proof scheme from the Preliminary.
+"""
+def dlog_extractor(A, Adv):
+    assert type(A) is Point
+
+    ## Step 1: run the adversary to generate a proof while recording
+    ## the random bits and oracle queries
+
+    # TODO: Fill your code in here (5 points)
+
+    ## Step 2: run the adversary again, replaying the random bits,
+    ## and intercepting the call to the random oracle
+
+    # TODO: Fill your code in here (5 points)
+
+    ## Step 3: Extract a witness from the two proofs and oracle queries
+    
+    # TODO: Fill your code in here (5 points)
+
+def dlog_test_extractor():
+    # Make a test case based on running the real prover
+    a = uint256_from_str(os.urandom(32))
+    A = a * G
+    
+    def Adv(A, rnd_bytes, RO):
+        assert A == a * G
+        return dlog_prover(A, a, rnd_bytes, RO)
+
+    a_ = dlog_extractor(A, Adv)
+    assert a == a_
+    print 'Extractor test complete!'
+
+def dlog_test_extractor_harder():
+    # Make a test case based on running a "picky" prover
+    a = uint256_from_str(os.urandom(32))
+    A = a * G
+    
+    def Adv(A, rnd_bytes, RO):
+        assert A == a * G
+
+        while True:
+            # The "picky" prover loops until it is happy
+
+            # Make a whimsical decision
+            coin = rnd_bytes(1)
+            if ord(coin) < 128: continue
+
+            k = uint256_from_str(rnd_bytes(32)) % order
+            K = k*G
+            c = uint256_from_str(RO(ser(K)))
+
+            # I only like challenges that end with 3 zero bits
+            if c & 0b111 != 0: continue
+
+            # OK I'm satisfied
+            s = Fp(k + c*a)
+            return (K, s)
+
+    a_ = dlog_extractor(A, Adv)
+    assert a == a_
+    print 'Extractor test complete!'
+
+dlog_test_extractor()
+dlog_test_extractor_harder()
+
+def dlog_simulator(A, rnd_bytes):
+    """
+    Returns:
+    - (prf, RO)
+    - prf, a tuple of the form (K,s),
+        where K is a Point
+        and s is an element of Fp
+    - RO is a random oracle function
+    
+    The following must hold:
+    - RO is a mapping from to 32-bit strings. Its output should be indistinguishable from an ordinary random oracle generated from `make_random_oracle`.
+    """
+    # TODO: Fill in your code here (10 points)
+
+def dlog_test_simulator():
+    rnd_bytes = os.urandom
+    # Test with a randomly generated point on curve
+    A = make_random_point(rnd_bytes)
+
+    (prf, RO) = dlog_simulator(A, rnd_bytes)
+
+    # The proof must verify
+    assert dlog_verifier(A, prf, RO=RO)
+    print "DLOG simulator test complete!"
+
+dlog_test_simulator()
+
+
+"""
+## Bonus Challenge: Expression compiler
+
+  Can you take a description of a ZK scheme in Camenisch-Stadler notation,
+  and automatically generate the protocol for it?
+
+  Read the ZKPDL paper for inspiration (that is a much more full-featured system!)
+
+  To complete this bonus, the goal should be to support the following
+  grammar for expressing ZK constraint systems:
+
+    ZK := EQN | ZK and ZK | ZK or ZK
+    EQN := BEXP = BEXP
+    BEXP := BEXP * BEXP | BEXP / BEXP | BEXP ^ EEXP | BIDENT
+    EEXP := EEXP + EEXP | EEXP - EEXP | EEXP * EEXP | EEXP / EEXP | EIDENT | ELIT
+    BIDENT := 'G', 'H', ...
+    EIDENT := 'a','b', ...
+    ELIT := 1,2,3, ...
+
+  Example:
+   "Zk{ (a,b): A = G^(a) * (H^10)^(3*b)"
+
+  would be expressed as:
+  ZK(
+   EQN( 
+    BEXP( BIDENT('A') )
+   = 
+    BEXP( 
+     BEXP( BIDENT('G') ^ EEXP( EIDENT('a') ) )
+    *
+     BEXP( 
+      BEXP( BEXP(BIDENT('H')) ^ EEXP(ELIT(10)) )
+     ^
+      EEXP(ELIT(3) * EIDENT('b'))
+     )
+    )
+   )
+  )
+
+  Hints:
+  - Build an abstract syntax tree to traverse.
+  - Flatten AND and OR to a single list
+  - Apply operations to left and right sides until left side has no exponents
+  - To handle multiplications, introduce ephemeral commitments (like in Part 5)
+
+    For example, to prove
+      Zk{ (a,b,c): A = g^a, B = g^b, C = g^c, D = g^(abc) }
+
+    you could introduce an ephemeral commitment:
+
+      E = g^(ab) h^r, for random r
+
+    Then the extended proof is:
+      Zk{ (a,b,c,r,r2): A = g^a, B = g^b, C = g^c, E = A^b h^r, D = E^c h^r2 }
+
+    where the prover sets r2 = -(c*r).
+
+    This now has the property that the only EEXP terms are EEXP(EIDENT(_)).
+"""
-- 
GitLab