diff --git a/Lab01Basics/Lab01Code.py b/Lab01Basics/Lab01Code.py index 19edd01..b0549a4 100644 --- a/Lab01Basics/Lab01Code.py +++ b/Lab01Basics/Lab01Code.py @@ -35,16 +35,16 @@ def encrypt_message(K, message): plaintext = message.encode("utf8") - ## YOUR CODE HERE + ## ADD CODE HERE return (iv, ciphertext, tag) def decrypt_message(K, iv, ciphertext, tag): - """ Decrypt a cipher text under a key K - - In case the decryption fails, throw an exception. + """ + Decrypt a cipher text under a key K + In case the decryption fails, throw an exception. """ - ## YOUR CODE HERE + ## ADD CODE HERE return plain.encode("utf8") @@ -56,7 +56,7 @@ def decrypt_message(K, iv, ciphertext, tag): # - Implement Scalar multiplication (double & add). # - Implement Scalar multiplication (Montgomery ladder). # -# MUST NOT USE ANY OF THE petlib.ec FUNCIONS. Only petlib.bn! +# MUST NOT USE ANY OF THE petlib.ec FUNCTIONS. Only petlib.bn! from petlib.bn import Bn @@ -89,7 +89,8 @@ def is_point_on_curve(a, b, p, x, y): def point_add(a, b, p, x0, y0, x1, y1): - """Define the "addition" operation for 2 EC Points. + """ + Define the "addition" operation for 2 EC Points. Reminder: (xr, yr) = (xq, yq) + (xp, yp) is defined as: @@ -106,10 +107,11 @@ def point_add(a, b, p, x0, y0, x1, y1): return (xr, yr) def point_double(a, b, p, x, y): - """Define "doubling" an EC point. - A special case, when a point needs to be added to itself. + """ + Define "doubling" an EC point. + A special case, when a point needs to be added to itself. - Reminder: + Reminder: lam = (3 * xp ^ 2 + a) * (2 * yp) ^ -1 (mod p) xr = lam ^ 2 - 2 * xp yr = lam * (xp - xr) - yp (mod p) @@ -134,13 +136,12 @@ def point_scalar_multiplication_double_and_add(a, b, p, x, y, scalar): Q = Q + P P = 2 * P return Q - """ Q = (None, None) P = (x, y) for i in range(scalar.num_bits()): - pass ## ADD YOUR CODE HERE + pass ## ADD CODE HERE return Q @@ -160,13 +161,12 @@ def point_scalar_multiplication_montgomerry_ladder(a, b, p, x, y, scalar): R0 = R0 + R1 R1 = 2 R1 return R0 - """ R0 = (None, None) R1 = (x, y) for i in reversed(range(0,scalar.num_bits())): - pass ## ADD YOUR CODE HERE + pass ## ADD CODE HERE return R0 @@ -184,8 +184,10 @@ def point_scalar_multiplication_montgomerry_ladder(a, b, p, x, y, scalar): from petlib.ecdsa import do_ecdsa_sign, do_ecdsa_verify def ecdsa_key_gen(): - """ Returns an EC group, a random private key for signing - and the corresponding public key for verification""" + """ + Returns an EC group, a random private key for signing + and the corresponding public key for verification + """ G = EcGroup() priv_sign = G.order().random() pub_verify = priv_sign * G.generator() @@ -196,7 +198,7 @@ def ecdsa_sign(G, priv_sign, message): """ Sign the SHA256 digest of the message using ECDSA and return a signature """ plaintext = message.encode("utf8") - ## YOUR CODE HERE + ## ADD CODE HERE return sig @@ -204,7 +206,7 @@ def ecdsa_verify(G, pub_verify, message, sig): """ Verify the ECDSA signature on the message """ plaintext = message.encode("utf8") - ## YOUR CODE HERE + ## ADD CODE HERE return res @@ -225,7 +227,8 @@ def dh_get_key(): def dh_encrypt(pub, message, aliceSig = None): - """ Assume you know the public key of someone else (Bob), + """ + Assume you know the public key of someone else (Bob), and wish to Encrypt a message for them. - Generate a fresh DH key for this message. - Derive a fresh shared key. @@ -233,15 +236,17 @@ def dh_encrypt(pub, message, aliceSig = None): - Optionally: sign the message with Alice's key. """ - ## YOUR CODE HERE + ## ADD CODE HERE pass def dh_decrypt(priv, ciphertext, aliceVer = None): - """ Decrypt a received message encrypted using your public key, + """ + Decrypt a received message encrypted using your public key, of which the private key is provided. Optionally verify - the message came from Alice using her verification key.""" + the message came from Alice using her verification key. + """ - ## YOUR CODE HERE + ## ADD CODE HERE pass ## NOTE: populate those (or more) tests diff --git a/Lab01Basics/Lab01Tests.py b/Lab01Basics/Lab01Tests.py index 928dbbb..03897dc 100644 --- a/Lab01Basics/Lab01Tests.py +++ b/Lab01Basics/Lab01Tests.py @@ -113,7 +113,6 @@ def test_gcm_fails(): def test_on_curve(): """ Test the procedues that tests whether a point is on a curve. - """ ## Example on how to define a curve diff --git a/Lab01Basics/README.md b/Lab01Basics/README.md index 51752f5..0bb015c 100644 --- a/Lab01Basics/README.md +++ b/Lab01Basics/README.md @@ -102,7 +102,7 @@ $ py.test -v Lab01Tests.py -m task2 - The petlib.cipher package provides two handy functions `quick_gcm_enc` and `quick_gcm_dec` that will help you define the encryption and decryption functions. -- Use the encoded `plaintext` rather than the input directly (you can encypt bytes not unicode strings, hence the need for encoding and decoding with UTF8 first). +- Use the encoded `plaintext` rather than the input directly (you can encrypt bytes not unicode strings, hence the need for encoding and decoding with UTF8 first). - The documentation for petlib.cipher is [available here](http://petlib.readthedocs.org/en/latest/index.html#module-petlib-cipher). @@ -186,7 +186,7 @@ $ py.test -v Lab01Tests.py -m task5 - This task requires you to implement a simple hybrid encryption scheme, guided by the scheme presented in the slides. In a nutshell you may assume that Alice and Bob are aware of each other's public keys, and use those to eventually derive a shared key. This shared key is eventually used to key an AES-GCM cipher to protect the integrity and confidentiality of a message. -- You may assume that the public key passed to `dh_encrypt` is the public encryption key of the recipient, and the `aliceeSig` parameter is the signature key of Alice the sender. Conversely, the `priv` paramemter of `dh_encrypt` is the receipient's (Bob) secret decryption key and `aliceVer` a public verification key for a signature scheme. +- You may assume that the public key passed to `dh_encrypt` is the public encryption key of the recipient, and the `aliceeSig` parameter is the signature key of Alice the sender. Conversely, the `priv` parameter of `dh_encrypt` is the recipient's (Bob) secret decryption key and `aliceVer` a public verification key for a signature scheme. - As part of this task you MUST implement a number of tests to ensure that your code is correct. Stubs for 3 such tests are provided, namely `test_encrypt`, `test_decrypt` (which are self explanatory), and `test_fails` which is meant to check for conditions under which the decryption must fail. At least these should be implemented in the code file, but feel free to implement more. diff --git a/Lab02Mix/Lab02Code.py b/Lab02Mix/Lab02Code.py index ec74343..1b66d50 100644 --- a/Lab02Mix/Lab02Code.py +++ b/Lab02Mix/Lab02Code.py @@ -22,7 +22,8 @@ from binascii import hexlify def aes_ctr_enc_dec(key, iv, input): - """ A helper function that implements AES Counter (CTR) Mode encryption and decryption. + """ + A helper function that implements AES Counter (CTR) Mode encryption and decryption. Expects a key (16 byte), and IV (16 bytes) and an input plaintext / ciphertext. If it is not obvious convince yourself that CTR encryption and decryption are in @@ -54,13 +55,13 @@ def aes_ctr_enc_dec(key, iv, input): from petlib.cipher import Cipher def mix_server_one_hop(private_key, message_list): - """ Implements the decoding for a simple one-hop mix. - - Each message is decoded in turn: - - A shared key is derived from the message public key and the mix private_key. - - the hmac is checked against all encrypted parts of the message - - the address and message are decrypted, decoded and returned + """ + Implements the decoding for a simple one-hop mix. + Each message is decoded in turn: + - A shared key is derived from the message public key and the mix private_key. + - the hmac is checked against all encrypted parts of the message + - the address and message are decrypted, decoded and returned """ G = EcGroup() @@ -69,14 +70,14 @@ def mix_server_one_hop(private_key, message_list): # Process all messages for msg in message_list: - ## Check elements and lengths + # Check elements and lengths if not G.check_point(msg.ec_public_key) or \ not len(msg.hmac) == 20 or \ not len(msg.address) == 258 or \ not len(msg.message) == 1002: raise Exception("Malformed input message") - ## First get a shared key + # First get a shared key shared_element = private_key * msg.ec_public_key key_material = sha512(shared_element.export()).digest() @@ -85,7 +86,7 @@ def mix_server_one_hop(private_key, message_list): address_key = key_material[16:32] message_key = key_material[32:48] - ## Check the HMAC + # Check the HMAC h = Hmac(b"sha512", hmac_key) h.update(msg.address) h.update(msg.message) @@ -94,7 +95,7 @@ def mix_server_one_hop(private_key, message_list): if not secure_compare(msg.hmac, expected_mac[:20]): raise Exception("HMAC check failure") - ## Decrypt the address and the message + # Decrypt the address and the message iv = b"\x00"*16 address_plaintext = aes_ctr_enc_dec(address_key, iv, msg.address) @@ -128,7 +129,7 @@ def mix_client_one_hop(public_key, address, message): address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) - ## Generate a fresh public key + # Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() @@ -153,7 +154,8 @@ def mix_client_one_hop(public_key, address, message): def mix_server_n_hop(private_key, message_list, final=False): - """ Decodes a NHopMixMessage message and outputs either messages destined + """ + Decodes a NHopMixMessage message and outputs either messages destined to the next mix or a list of tuples (address, message) (if final=True) to be sent to their final recipients. @@ -171,7 +173,7 @@ def mix_server_n_hop(private_key, message_list, final=False): # Process all messages for msg in message_list: - ## Check elements and lengths + # Check elements and lengths if not G.check_point(msg.ec_public_key) or \ not isinstance(msg.hmacs, list) or \ not len(msg.hmacs[0]) == 20 or \ @@ -179,7 +181,7 @@ def mix_server_n_hop(private_key, message_list, final=False): not len(msg.message) == 1002: raise Exception("Malformed input message") - ## First get a shared key + # First get a shared key shared_element = private_key * msg.ec_public_key key_material = sha512(shared_element.export()).digest() @@ -192,7 +194,7 @@ def mix_server_n_hop(private_key, message_list, final=False): blinding_factor = Bn.from_binary(key_material[48:]) new_ec_public_key = blinding_factor * msg.ec_public_key - ## Check the HMAC + # Check the HMAC h = Hmac(b"sha512", hmac_key) for other_mac in msg.hmacs[1:]: @@ -206,7 +208,7 @@ def mix_server_n_hop(private_key, message_list, final=False): if not secure_compare(msg.hmacs[0], expected_mac[:20]): raise Exception("HMAC check failure") - ## Decrypt the hmacs, address and the message + # Decrypt the hmacs, address and the message aes = Cipher("AES-128-CTR") # Decrypt hmacs @@ -245,7 +247,6 @@ def mix_client_n_hop(public_keys, address, message): The maximum size of the final address and the message are 256 bytes and 1000 bytes respectively. Returns an 'NHopMixMessage' with four parts: a public key, a list of hmacs (20 bytes each), an address ciphertext (256 + 2 bytes) and a message ciphertext (1002 bytes). - """ G = EcGroup() # assert G.check_point(public_key) @@ -257,7 +258,7 @@ def mix_client_n_hop(public_keys, address, message): address_plaintext = pack("!H256s", len(address), address) message_plaintext = pack("!H1000s", len(message), message) - ## Generate a fresh public key + # Generate a fresh public key private_key = G.order().random() client_public_key = private_key * G.generator() @@ -282,14 +283,14 @@ def generate_trace(number_of_users, threshold_size, number_of_rounds, targets_fr all_users = range(number_of_users) trace = [] - ## Generate traces in which Alice (user 0) is not sending + # Generate traces in which Alice (user 0) is not sending for _ in range(number_of_rounds // 2): senders = sorted(random.sample( others, threshold_size)) receivers = sorted(random.sample( all_users, threshold_size)) trace += [(senders, receivers)] - ## Generate traces in which Alice (user 0) is sending + # Generate traces in which Alice (user 0) is sending for _ in range(number_of_rounds // 2): senders = sorted([0] + random.sample( others, threshold_size-1)) # Alice sends to a friend diff --git a/Lab02Mix/README.md b/Lab02Mix/README.md index 469a450..b2738d9 100644 --- a/Lab02Mix/README.md +++ b/Lab02Mix/README.md @@ -94,7 +94,7 @@ where the `client_public_key` is an EC point, the expected Hmac is an Hmac of th py.test -v Lab02Tests.py -m task3 -- The key differences between the 1-hop mix and the n-hop mix message encoding relates to: (1) the use of a blinding factor to provide bit-wise unlikability of the public key associated with the message; (2) the inclusion of a sequence (list) of hmacs as the second part of the mix message; (3) the decryption of the hmacs (in addition to the address and message) at each step of mixing. +- The key differences between the 1-hop mix and the n-hop mix message encoding relates to: (1) the use of a blinding factor to provide bit-wise unlinkability of the public key associated with the message; (2) the inclusion of a sequence (list) of hmacs as the second part of the mix message; (3) the decryption of the hmacs (in addition to the address and message) at each step of mixing. - The output of the function `mix_client_n_hop` should be an `NHopMixMessage`. The first element is a public key, the second is a list of hmacs, the third is the encrypted address and the final one is the encrypted message. You can build such a structure using: diff --git a/Lab03Compute/Lab03Code.py b/Lab03Compute/Lab03Code.py index 47c90d9..07482fe 100644 --- a/Lab03Compute/Lab03Code.py +++ b/Lab03Compute/Lab03Code.py @@ -25,22 +25,23 @@ def setup(): g = G.hash_to_point(b"g") h = G.hash_to_point(b"h") o = G.order() + return (G, g, h, o) def keyGen(params): - """ Generate a private / public key pair """ - (G, g, h, o) = params + """ Generate a private / public key pair """ + (G, g, h, o) = params - # ADD CODE HERE + ## ADD CODE HERE - return (priv, pub) + return (priv, pub) def encrypt(params, pub, m): """ Encrypt a message under the public key """ if not -100 < m < 100: raise Exception("Message value to low or high.") - # ADD CODE HERE + ## ADD CODE HERE return c @@ -75,7 +76,7 @@ def decrypt(params, priv, ciphertext): assert isCiphertext(params, ciphertext) a , b = ciphertext - # ADD CODE HERE + ## ADD CODE HERE return logh(params, hm) @@ -85,8 +86,9 @@ def decrypt(params, priv, ciphertext): # def add(params, pub, c1, c2): - """ Given two ciphertexts compute the ciphertext of the - sum of their plaintexts. + """ + Given two ciphertexts compute the ciphertext of the + sum of their plaintexts. """ assert isCiphertext(params, c1) assert isCiphertext(params, c2) @@ -96,8 +98,10 @@ def add(params, pub, c1, c2): return c3 def mul(params, pub, c1, alpha): - """ Given a ciphertext compute the ciphertext of the - product of the plaintext time alpha """ + """ + Given a ciphertext compute the ciphertext of the + product of the plaintext times alpha + """ assert isCiphertext(params, c1) # ADD CODE HERE @@ -113,16 +117,18 @@ def groupKey(params, pubKeys=[]): """ Generate a group public key from a list of public keys """ (G, g, h, o) = params - # ADD CODE HERE + ## ADD CODE HERE return pub def partialDecrypt(params, priv, ciphertext, final=False): - """ Given a ciphertext and a private key, perform partial decryption. - If final is True, then return the plaintext. """ + """ + Given a ciphertext and a private key, perform partial decryption. + If final is True, then return the plaintext. + """ assert isCiphertext(params, ciphertext) - # ADD CODE HERE + ## ADD CODE HERE if final: return logh(params, b1) @@ -142,7 +148,7 @@ def corruptPubKey(params, priv, OtherPubKeys=[]): corrupt authority. """ (G, g, h, o) = params - # ADD CODE HERE + ## ADD CODE HERE return pub @@ -152,27 +158,33 @@ def corruptPubKey(params, priv, OtherPubKeys=[]): # def encode_vote(params, pub, vote): - """ Given a vote 0 or 1 encode the vote as two - ciphertexts representing the count of votes for - zero and the votes for one.""" + """ + Given a vote 0 or 1 encode the vote as two + ciphertexts representing the count of votes for + zero and the votes for one. + """ assert vote in [0, 1] - # ADD CODE HERE + ## ADD CODE HERE return (v0, v1) def process_votes(params, pub, encrypted_votes): - """ Given a list of encrypted votes tally them - to sum votes for zeros and votes for ones. """ + """ + Given a list of encrypted votes tally them + to sum votes for zeros and votes for ones. + """ assert isinstance(encrypted_votes, list) - # ADD CODE HERE + ## ADD CODE HERE return tv0, tv1 def simulate_poll(votes): - """ Simulates the full process of encrypting votes, - tallying them, and then decrypting the total. """ + """ + Simulates the full process of encrypting votes, + tallying them, and then decrypting the total. + """ # Generate parameters for the crypto-system params = setup() @@ -215,9 +227,9 @@ def simulate_poll(votes): # 3) H2 provides the adversary A, with Ca, Cb, Cc and C. # # What is the advantage of the adversary in guessing b given your implementation of -# Homomorphic addition? What are the security implications of this? +# homomorphic addition? What are the security implications of this? -""" Your Answer here """ +""" TODO: Your answer HERE """ ########################################################### # TASK Q2 -- Answer questions regarding your implementation @@ -228,4 +240,4 @@ def simulate_poll(votes): # that it yields an arbitrary result. Can those malicious actions # be detected given your implementation? -""" Your Answer here """ +""" TODO: Your answer HERE """ diff --git a/Lab03Compute/README.md b/Lab03Compute/README.md index 2ed4826..013c9d9 100644 --- a/Lab03Compute/README.md +++ b/Lab03Compute/README.md @@ -98,7 +98,7 @@ The full documentation of pytest is [available here](http://pytest.org/latest/). - The `GroupKey` operation aggregates a list of public keys from a number of authorities, without using any private keys, to generate a group public key. Encryption under this group key requires all authorities to help with decryption. -- The `partialDecrypt` function takes a ciphertext encypted under a group public key, and returns a partially decrypted ciphertext. +- The `partialDecrypt` function takes a ciphertext encrypted under a group public key, and returns a partially decrypted ciphertext. - The `final` flag signifies that an authority is the last in a decryption chain, and should return a plaintext rather than a partially decrypted ciphertext. diff --git a/Lab04Zkp/Lab04Code.py b/Lab04Zkp/Lab04Code.py index fe2266c..ebfaf42 100644 --- a/Lab04Zkp/Lab04Code.py +++ b/Lab04Zkp/Lab04Code.py @@ -22,14 +22,16 @@ def setup(): g = G.hash_to_point(b"g") hs = [G.hash_to_point(("h%s" % i).encode("utf8")) for i in range(4)] o = G.order() + return (G, g, hs, o) def keyGen(params): - """ Generate a private / public key pair. """ - (G, g, hs, o) = params - priv = o.random() - pub = priv * g - return (priv, pub) + """ Generate a private / public key pair. """ + (G, g, hs, o) = params + priv = o.random() + pub = priv * g + + return (priv, pub) def to_challenge(elements): """ Generates a Bn challenge by hashing a number of EC points """ @@ -42,22 +44,24 @@ def to_challenge(elements): # secret. def proveKey(params, priv, pub): - """ Uses the Schnorr non-interactive protocols produce a proof - of knowledge of the secret priv such that pub = priv * g. + """ + Uses the Schnorr non-interactive protocols produce a proof + of knowledge of the secret priv such that pub = priv * g. - Outputs: a proof (c, r) - c (a challenge) - r (the response) + Outputs: a proof (c, r) + c (a challenge) + r (the response) """ (G, g, hs, o) = params - ## YOUR CODE HERE: + ## ADD CODE HERE: return (c, r) def verifyKey(params, pub, proof): - """ Schnorr non-interactive proof verification of knowledge of a a secret. - Returns a boolean indicating whether the verification was successful. + """ + Schnorr non-interactive proof verification of knowledge of a a secret. + Returns a boolean indicating whether the verification was successful. """ (G, g, hs, o) = params c, r = proof @@ -69,41 +73,47 @@ def verifyKey(params, pub, proof): # representation. def commit(params, secrets): - """ Produces a commitment C = r * g + Sum xi * hi, - where secrets is a list of xi of length 4. - Returns the commitment (C) and the opening (r). + """ + Produces a commitment C = r * g + Sum xi * hi, + where secrets is a list of xi of length 4. + Returns the commitment (C) and the opening (r). """ assert len(secrets) == 4 (G, g, (h0, h1, h2, h3), o) = params x0, x1, x2, x3 = secrets r = o.random() C = x0 * h0 + x1 * h1 + x2 * h2 + x3 * h3 + r * g + return (C, r) def proveCommitment(params, C, r, secrets): - """ Prove knowledge of the secrets within a commitment, - as well as the opening of the commitment. + """ + Prove knowledge of the secrets within a commitment, + as well as the opening of the commitment. - Args: C (the commitment), r (the opening of the - commitment), and secrets (a list of secrets). - Returns: a challenge (c) and a list of responses. + Args: C (the commitment), r (the opening of the + commitment), and secrets (a list of secrets). + Returns: a challenge (c) and a list of responses. """ (G, g, (h0, h1, h2, h3), o) = params x0, x1, x2, x3 = secrets - ## YOUR CODE HERE: + ## ADD CODE HERE: return (c, responses) def verifyCommitments(params, C, proof): - """ Verify a proof of knowledge of the commitment. - Return a boolean denoting whether the verification succeeded. """ + """ + Verify a proof of knowledge of the commitment. + Return a boolean denoting whether the verification succeeded. + """ (G, g, (h0, h1, h2, h3), o) = params c, responses = proof (r0, r1, r2, r3, rr) = responses Cw_prime = c * C + r0 * h0 + r1 * h1 + r2 * h2 + r3 * h3 + rr * g c_prime = to_challenge([g, h0, h1, h2, h3, Cw_prime]) + return c_prime == c ##################################################### @@ -121,8 +131,10 @@ def gen2Keys(params): return (x, K, L) def proveDLEquality(params, x, K, L): - """ Generate a ZK proof that two public keys K, L have the same secret private key x, - as well as knowledge of this private key. """ + """ + Generate a ZK proof that two public keys K, L have the same secret private key x, + as well as knowledge of this private key. + """ (G, g, (h0, h1, h2, h3), o) = params w = o.random() Kw = w * g @@ -138,7 +150,7 @@ def verifyDLEquality(params, K, L, proof): (G, g, (h0, h1, h2, h3), o) = params c, r = proof - ## YOUR CODE HERE: + ## ADD CODE HERE: return # YOUR RETURN HERE @@ -147,23 +159,25 @@ def verifyDLEquality(params, K, L, proof): # a plaintext. def encrypt(params, pub, m): - """ Encrypt a message m under a public key pub. - Returns both the randomness and the ciphertext. + """ + Encrypt a message m under a public key pub. + Returns both the randomness and the ciphertext. """ (G, g, (h0, h1, h2, h3), o) = params k = o.random() return k, (k * g, k * pub + m * h0) def proveEnc(params, pub, Ciphertext, k, m): - """ Prove in ZK that the ciphertext is well formed - and knowledge of the message encrypted as well. + """ + Prove in ZK that the ciphertext is well formed + and knowledge of the message encrypted as well. - Return the proof: challenge and the responses. + Return the proof: challenge and the responses. """ (G, g, (h0, h1, h2, h3), o) = params a, b = Ciphertext - ## YOUR CODE HERE: + ## ADD CODE HERE: return (c, (rk, rm)) @@ -173,7 +187,7 @@ def verifyEnc(params, pub, Ciphertext, proof): a, b = Ciphertext (c, (rk, rm)) = proof - ## YOUR CODE HERE: + ## ADD CODE HERE: return ## YOUR RETURN HERE @@ -183,8 +197,9 @@ def verifyEnc(params, pub, Ciphertext, proof): # def relation(params, x1): - """ Returns a commitment C to x0 and x1, such that x0 = 10 x1 + 20, - as well as x0, x1 and the commitment opening r. + """ + Returns a commitment C to x0 and x1, such that x0 = 10 x1 + 20, + as well as x0, x1 and the commitment opening r. """ (G, g, (h0, h1, h2, h3), o) = params r = o.random() @@ -198,7 +213,7 @@ def prove_x0eq10x1plus20(params, C, x0, x1, r): """ Prove C is a commitment to x0 and x1 and that x0 = 10 x1 + 20. """ (G, g, (h0, h1, h2, h3), o) = params - ## YOUR CODE HERE: + ## ADD CODE HERE: return ## YOUR RETURN HERE @@ -206,7 +221,7 @@ def verify_x0eq10x1plus20(params, C, proof): """ Verify that proof of knowledge of C and x0 = 10 x1 + 20. """ (G, g, (h0, h1, h2, h3), o) = params - ## YOUR CODE HERE: + ## ADD CODE HERE: return ## YOUR RETURN HERE @@ -253,7 +268,7 @@ def test_bin_incorrect(): # that deviates from the Schnorr identification protocol? Justify # your answer by describing what a dishonest verifier may do. -""" TODO: Your answer here. """ +""" TODO: Your answer HERE """ ##################################################### # TASK Q2 - Answer the following question: @@ -266,7 +281,7 @@ def test_bin_incorrect(): # # Hint: Look at "test_prove_something" too. -""" TODO: Your answer here. """ +""" TODO: Your answer HERE """ def prove_something(params, KX, KY, y): (G, g, _, o) = params diff --git a/Lab04Zkp/README.md b/Lab04Zkp/README.md index 9ce7cf0..87ea0a1 100644 --- a/Lab04Zkp/README.md +++ b/Lab04Zkp/README.md @@ -76,7 +76,7 @@ The full documentation of pytest is [available here](http://pytest.org/latest/). - Study the function `commit` to understand the structure of the commitment. Ensure you understand the role of the opening value `r`. -- Study the function `verifyCommitement` to ensure your proof can be verified correctly. +- Study the function `verifyCommitments` to ensure your proof can be verified correctly. - The `proveCommitment` function is passed the commitment and the secrets (including the opening). It should returns a proof consisting of the challenge and responses (multiple ones). diff --git a/Lab05Credential/Lab05Code.py b/Lab05Credential/Lab05Code.py index 6c5a699..e980d20 100644 --- a/Lab05Credential/Lab05Code.py +++ b/Lab05Credential/Lab05Code.py @@ -54,7 +54,6 @@ def credential_KeyGenUser(params): return (priv, pub) ## This is our old friend "to_challenge" from Lab04 on Zero Knowledge - def to_challenge(elements): """ Generates a Bn challenge by hashing a number of EC points """ Cstring = b",".join([hexlify(x.export()) for x in elements]) @@ -74,23 +73,25 @@ def to_challenge(elements): # pages 8-9 of https://eprint.iacr.org/2013/516.pdf def credential_EncryptUserSecret(params, pub, priv): - """ Encrypt a user defined random secret v under the public key of the user. - Prove knowledge of the secret v, the private key "priv" and correctness of - the encryption """ + """ + Encrypt a user defined random secret v under the public key of the user. + Prove knowledge of the secret v, the private key "priv" and correctness of + the encryption. + """ G, g, h, o = params v = o.random() - ## Encrypt v using Benaloh with randomness k + # Encrypt v using Benaloh with randomness k k = o.random() ciphertext = k * g, k * pub + v * g a, b = ciphertext - ## Prove knowledge of the encrypted v and priv in ZK + # Prove knowledge of the encrypted v and priv in ZK # NIZK{(v, k, priv): a = k * g and # b = k * pub + v * g and # pub = priv * g} - ## TODO + ## ADD CODE HERE # Return the fresh v, the encryption of v and the proof. proof = (c, rk, rv, rpriv) @@ -121,16 +122,18 @@ def credential_VerifyUserSecret(params, pub, ciphertext, proof): # that the MAC is correctly formed. The user # decrypts and verifies the MAC. -## IMPRTANT NOTE: Study the section "Issuance" p.8 +## IMPORTANT NOTE: Study the section "Issuance" p.8 # of https://eprint.iacr.org/2013/516.pdf def credential_Issuing(params, pub, ciphertext, issuer_params): - """ A function used by the credential issuer to provide a MAC - on a secret (encrypted) attribute v """ + """ + A function used by the credential issuer to provide a MAC + on a secret (encrypted) attribute v. + """ G, g, h, o = params - ## The public and private parameters of the issuer + # The public and private parameters of the issuer (Cx0, iparams), (sk, x0_bar) = issuer_params X1 = iparams x0, x1 = sk @@ -142,12 +145,12 @@ def credential_Issuing(params, pub, ciphertext, issuer_params): # 2) Create a X1b as X1b == b * X1 == (b * x1) * h # and x1b = (b * x1) mod o - # TODO 1 & 2 + # ADD CODE HERE FOR 1 & 2 # 3) The encrypted MAC is u, and an encrypted u_prime defined as # E( (b*x0) * g + (x1 * b * v) * g ) + E(0; r_prime) - # TODO 3 + # ADD CODE HERE FOR 3 ciphertext = new_a, new_b @@ -161,27 +164,29 @@ def credential_Issuing(params, pub, ciphertext, issuer_params): # new_b = r_prime * pub + x1b * b + x0 * u # Cx0 = x0 * g + x0_bar * h } - ## TODO proof + ## ADD CODE HERE proof proof = (c, rs, X1b) # Where rs are multiple responses return u, ciphertext, proof def credential_Verify_Issuing(params, issuer_pub_params, pub, u, Enc_v, Enc_u_prime, proof): - """ User verifies that the proof associated with the issuance - of the credential is valid. """ + """ + User verifies that the proof associated with the issuance + of the credential is valid. + """ G, g, h, o = params - ## The public parameters of the issuer. + # The public parameters of the issuer. (Cx0, iparams) = issuer_pub_params X1 = iparams - ## The ciphertext of the encrypted attribute v and the encrypted u_prime + # The ciphertext of the encrypted attribute v and the encrypted u_prime a, b = Enc_v new_a, new_b = Enc_u_prime - ## The proof of correctness + # The proof of correctness (c, rs, X1b) = proof c_prime = to_challenge([g, h, pub, a, b, X1, X1b, new_a, new_b, Cx0, @@ -213,12 +218,14 @@ def credential_Decrypt(params, priv, u, Enc_u_prime): # p.9 of https://eprint.iacr.org/2013/516.pdf def credential_show(params, issuer_pub_params, u, u_prime, v): - """ The user blinds the credential (u, u_prime) and then - proves its correct possession.""" + """ + The user blinds the credential (u, u_prime) and then + proves its correct possession. + """ G, g, h, o = params - ## The public parameters of the credential issuer + # The public parameters of the credential issuer (Cx0, iparams) = issuer_pub_params X1 = iparams @@ -226,12 +233,12 @@ def credential_show(params, issuer_pub_params, u, u_prime, v): # using (alpha * u, alpha * u_prime) for a # random alpha. - # TODO 1 + # ADD CODE HERE 1 # 2) Implement the "Show" protocol (p.9) for a single attribute v. # Cv is a commitment to v and Cup is C_{u'} in the paper. - # TODO 2 + # ADD CODE HERE 2 tag = (u, Cv, Cup) @@ -241,7 +248,7 @@ def credential_show(params, issuer_pub_params, u, u_prime, v): # Cv = v *u + z1 * h and # V = r * (-g) + z1 * X1 } - ## TODO proof + ## ADD CODE HERE proof proof = (c, rr, rz1, rv) return tag, proof @@ -251,7 +258,7 @@ def credential_show_verify(params, issuer_params, tag, proof): G, g, h, o = params - ## Public and private issuer parameters + # Public and private issuer parameters (Cx0, iparams), (sk, x0_bar) = issuer_params x0, x1 = sk X1 = iparams @@ -260,7 +267,7 @@ def credential_show_verify(params, issuer_params, tag, proof): (c, rr, rz1, rv) = proof (u, Cv, Cup) = tag - ## TODO + ## ADD CODE HERE return c == c_prime @@ -271,40 +278,44 @@ def credential_show_verify(params, issuer_params, tag, proof): # be unlikable between services. def credential_show_pseudonym(params, issuer_pub_params, u, u_prime, v, service_name): - """ From a credential (u, u_prime) generate a pseudonym H(service_name)^v - and prove you hold a valid credential with attribute v """ + """ + From a credential (u, u_prime) generate a pseudonym H(service_name)^v + and prove you hold a valid credential with attribute v. + """ G, g, h, o = params - ## Public issuer parameters + # Public issuer parameters (Cx0, iparams) = issuer_pub_params X1 = iparams - ## A stable pseudonym associated with the service + # A stable pseudonym associated with the service N = G.hash_to_point(service_name) pseudonym = v * N - ## TODO (use code from above and modify as necessary!) + ## ADD CODE HERE (use code from above and modify as necessary!) return pseudonym, tag, proof def credential_show_verify_pseudonym(params, issuer_params, pseudonym, tag, proof, service_name): - """ Verify a pseudonym H(service_name)^v is generated by the holder of the - a valid credential with attribute v """ + """ + Verify a pseudonym H(service_name)^v is generated by the holder of the + a valid credential with attribute v. + """ G, g, h, o = params - ## The public and private issuer parameters + # The public and private issuer parameters (Cx0, iparams), (sk, x0_bar) = issuer_params x0, x1 = sk X1 = iparams - ## The EC point corresponding to the service + # The EC point corresponding to the service N = G.hash_to_point(service_name) - ## Verify the correct Show protocol and the correctness of the pseudonym + # Verify the correct Show protocol and the correctness of the pseudonym - # TODO (use code from above and modify as necessary!) + # ADD CODE HERE (use code from above and modify as necessary!) return c == c_prime @@ -317,4 +328,4 @@ def credential_show_verify_pseudonym(params, issuer_params, pseudonym, tag, proo # What would the credential represent, and what statements # would need to be shown to a verifier. -""" Your answer here. """ +""" TODO: Your answer HERE """ diff --git a/Lab05Credential/README.md b/Lab05Credential/README.md index a627739..18bc9ec 100644 --- a/Lab05Credential/README.md +++ b/Lab05Credential/README.md @@ -69,7 +69,7 @@ The user may verify the issuing was indeed correct. Finally, the user may use th - In this task the user derives a fresh random attribute v, and encrypts it using a homomorphic encryption scheme under their own public key. The ciphertext will be used by the issuer to issue a credential on this attribute blindly, i.e. without ever learning the attribute. -- The encryption is performed for you, but you have to provide a Zero-knowledge proof of knowledge on a number of statements: You need to prove knowledge of the encryption randomess k, the secret v and the private key for he used public key. +- The encryption is performed for you, but you have to provide a Zero-knowledge proof of knowledge on a number of statements: You need to prove knowledge of the encryption randomness k, the secret v and the private key for he used public key. - Note the verification of the proof above is provided: study it and ensure your proof may be verified correctly. The comment in task 1 also describes the statements to be proved.