Esempio n. 1
0
    def create_issue_request(server_pk, attributes):
        """Gets all known attributes (subscription) of a user and creates an issuance request.
        You are allowed to add extra attributes to the issuance.

        You should design the issue_request as you see fit.
        """
        attributes = [Bn.from_binary(hashlib.sha256(attr.encode()).digest()) for attr in attributes]
        gen_g1 = server_pk[0]
        t = G1.order().random()

        #Gen C
        C = gen_g1 ** t
        for e in zip(server_pk[1:], attributes):
            C = C * e[0] ** e[1]
        
        #Gen commitment
        comm_values = [G1.order().random() for _ in range(len(attributes) + 1)]
        comm = gen_g1 ** comm_values[0]
        for e in zip(server_pk[1:], comm_values[1:]):
            comm  = comm * e[0] ** e[1]
        
        #Gen challenge
        challenge = hashlib.sha256(jsonpickle.encode(C).encode())
        challenge.update(jsonpickle.encode(comm).encode())
        challenge.update(jsonpickle.encode(server_pk).encode())
        challenge = Bn.from_binary(challenge.digest())

        #Generate response
        response = [e[0].mod_sub(challenge * e[1],G1.order()) for e in zip(comm_values, [t] + attributes)]


        return IssuanceRequest(C, comm, challenge, response),t
Esempio n. 2
0
    def generate_ca(valid_attributes):
        """Initializes the credential system. Runs exactly once in the
        beginning. Decides on schemes public parameters and choses a secret key
        for the server.

        Args:
            valid_attributes (string): a list of all valid attributes. Users cannot
            get a credential with a attribute which is not included here.

            Note: You can use JSON to encode valid_attributes in the string.

        Returns:
            (tuple): tuple containing:
                byte[] : server's pubic information
                byte[] : server's secret key
            You are free to design this as you see fit, but all commuincations
            needs to be encoded as byte arrays.
        """

        # Code to generate the secret key
        private_key_array = []
        public_key_array = []
        ys_in_G1 = []
        g = G2.generator()
        for i in range(L + 1):
            c = G1.order().random()
            private_key_array.append(c)
            public_key_array.append(g**c)
            ys_in_G1.append(G1.generator()**c)
        x = private_key_array[0]
        X = public_key_array[0]
        X_G1 = G1.generator()**x
        y_s = private_key_array[1:]
        Y_s = public_key_array[1:]
        ys_in_G1 = ys_in_G1[1:]

        # Some sanity checks on the key
        for i in range(len(y_s)):
            if not (G1.generator().pair(Y_s[i]) == ys_in_G1[i].pair(
                    G2.generator())):
                raise Exception("Key generation failed !")

        public_information = {
            "Ys": Y_s,
            "X_G1": X_G1,
            "X": X,
            "Ys_G1": ys_in_G1,
            "validAttributes": valid_attributes.split(",")
        }
        pk = jsonpickle.encode(public_information).encode()
        private_key = {
            "Ys": Y_s,
            "X_G1": X_G1,
            "X": X,
            "Ys_G1": ys_in_G1,
            "validAttributes": valid_attributes.split(","),
            "x": x,
            "ys": y_s
        }
        return pk, jsonpickle.encode(private_key).encode()
Esempio n. 3
0
    def generate_ca(valid_attributes):
        """Initializes the credential system. Runs exactly once in the
        beginning. Decides on schemes public parameters and choses a secret key
        for the server.

        Args:
            valid_attributes (string): a list of all valid attributes. Users cannot
            get a credential with a attribute which is not included here.

            Note: You can use JSON to encode valid_attributes in the string.

        Returns:
            (tuple): tuple containing:
                byte[] : server's pubic information
                byte[] : server's secret key
            You are free to design this as you see fit, but all commuincations
            needs to be encoded as byte arrays.
        """
        attr_list = valid_attributes.split(',')
        nb_attributes = len(attr_list)

        gen_g1 = G1.generator()
        gen_g2 = G2.generator()
        exp = [G1.order().random() for _ in range(nb_attributes + 1)]

        pk = [gen_g1] + [gen_g1**i for i in exp[1:]
                         ] + [gen_g2] + [gen_g2**i for i in exp]
        sk = gen_g1**exp[0]

        sk = [sk, pk, attr_list]
        pk = [pk, attr_list]

        return (jsonpickle.encode(pk).encode(), jsonpickle.encode(sk).encode())
Esempio n. 4
0
    def sign(self, message, revealed_attr):
        """Signs the message.

        Args:
            message (byte []): message
            revealed_attr (string []): a list of revealed attributes

        Return:
            Signature: signature
        """
        #public_key separation
        nb_attr_public_key = (len(self.server_pk) - 3) // 2
        gen_g1_pk = self.server_pk[0]
        public_key1 = self.server_pk[1:nb_attr_public_key + 1]
        gen_g2_pk = self.server_pk[nb_attr_public_key + 1]
        x_g2_pk = self.server_pk[nb_attr_public_key + 2]
        public_key2 = self.server_pk[nb_attr_public_key + 3:]

        #Gen signature
        r = G1.order().random()
        t = G1.order().random()
        signature = (self.credential[0] ** r, (self.credential[1] * self.credential[0]**t)**r)

        #attributes work
        revealed_attributes_idx = [self.attributes.index(attr) for attr in self.attributes if attr in revealed_attr]
        revealed_attributes_bn = [Bn.from_binary(hashlib.sha256(attr.encode()).digest()) for attr in revealed_attr]
        hidden_attributes_idx = [self.attributes.index(attr) for attr in self.attributes if attr not in revealed_attr]
        hidden_attributes_bn = [Bn.from_binary(hashlib.sha256(attr.encode()).digest()) for attr in self.attributes if attr not in revealed_attr]


        #Gen C (left-hand side)
        C = signature[1].pair(gen_g2_pk) / signature[0].pair(x_g2_pk)
        for i in range(len(revealed_attr)):
            C = C * signature[0].pair(public_key2[revealed_attributes_idx[i]]) ** (-revealed_attributes_bn[i] % G1.order())
        

        #Gen commitment (to prove right-hand side)
        comm_values = [G1.order().random() for _ in range(len(hidden_attributes_idx) + 1)]
        comm = signature[0].pair(gen_g2_pk) ** comm_values[0]
        for e in zip(hidden_attributes_idx, comm_values[1:]):
            comm = comm * signature[0].pair(public_key2[e[0]])**e[1]


        #Gen Challenge
        challenge = hashlib.sha256(jsonpickle.encode(C).encode())
        challenge.update(jsonpickle.encode(comm).encode())
        challenge.update(jsonpickle.encode(self.server_pk).encode())
        challenge.update(message)
        challenge = Bn.from_binary(challenge.digest())

        #Gen Responses
        response = [e[0].mod_sub(challenge * e[1],G1.order()) for e in zip(comm_values, [t] + hidden_attributes_bn)]


        return Signature(signature, comm, challenge, response, revealed_attributes_idx)
Esempio n. 5
0
    def verify(self, issuer_public_info, public_attrs, message):
        """Verifies a signature.

        Args:
            issuer_public_info (): output of issuer's 'get_serialized_public_key' method
            public_attrs (dict): public attributes
            message (byte []): list of messages

        returns:
            valid (boolean): is signature valid
        """
        #public_key separation
        nb_attr_public_key = (len(issuer_public_info) - 3) // 2
        gen_g1_pk = issuer_public_info[0]
        public_key1 = issuer_public_info[1:nb_attr_public_key + 1]
        gen_g2_pk = issuer_public_info[nb_attr_public_key + 1]
        x_g2_pk = issuer_public_info[nb_attr_public_key + 2]
        public_key2 = issuer_public_info[nb_attr_public_key + 3:]

        #attributes work
        nb_attr = len(self.response) - 1 + len(public_attrs)
        public_attributes_idx = self.attributes_idx
        public_attributes_bn = [Bn.from_binary(hashlib.sha256(attr.encode()).digest()) for attr in public_attrs]
        hidden_attributes_idx = [i for i in range(nb_attr) if i not in public_attributes_idx]


        #Gen C (left-hand side)
        C = self.signature[1].pair(gen_g2_pk) / self.signature[0].pair(x_g2_pk)
        for i in range(len(public_attrs)):
            C = C * self.signature[0].pair(public_key2[public_attributes_idx[i]]) ** (-public_attributes_bn[i] % G1.order())

        #Gen Challenge
        challenge = hashlib.sha256(jsonpickle.encode(C).encode())
        challenge.update(jsonpickle.encode(self.commitment).encode())
        challenge.update(jsonpickle.encode(issuer_public_info).encode())
        challenge.update(message)
        challenge = Bn.from_binary(challenge.digest())

        #check challenge
        challenge_valid = challenge == self.challenge

        #Compute zkp
        candidate = C ** challenge * self.signature[0].pair(gen_g2_pk) ** self.response[0]
        for e in zip(hidden_attributes_idx, self.response[1:]):
            candidate = candidate * self.signature[0].pair(public_key2[e[0]]) ** e[1]

        proof_valid = candidate == self.commitment


        return challenge_valid and proof_valid 
Esempio n. 6
0
def test_tampered_credential():
    '''
    Test if a user can have a response to a request if the credential is wrong
    '''

    attributes = "a,b,c,d,e"
    chosen_attr = "d,e"
    revealed_attr = "e"
    username = "******"
    message = "46.5 6.6".encode()

    pk, sk = Server.generate_ca(attributes)

    #Registration process
    issue_request, private_state = Client().prepare_registration(pk, username, chosen_attr)
    response = Server().register(sk, issue_request, username, chosen_attr)
    credential = Client().proceed_registration_response(pk, response, private_state)

    #Tampering the credential
    credential = jsonpickle.decode(credential)
    credential.credential = (credential.credential[0] ** 2, credential.credential[1])
    credential = jsonpickle.encode(credential).encode()

    #Request a service
    signature = Client().sign_request(pk, credential, message, revealed_attr)
    
    assert(not Server().check_request_signature(pk, message, revealed_attr, signature))
Esempio n. 7
0
    def register(self, server_sk, issuance_request, username, attributes):
        """ Registers a new account on the server.

        Args:
            server_sk (byte []): the server's secret key (serialized)
            issuance_request (bytes[]): The issuance request (serialized)
            username (string): username
            attributes (string): attributes

            Note: You can use JSON to encode attributes in the string.

        Return:
            response (bytes[]): the client should be able to build a credential
            with this response.
        """
        sk = jsonpickle.decode(server_sk)
        attributes = attributes.split(',')

        #Check if attributes chosen are valid
        for attr in attributes:
            if attr not in sk[2]:
                raise RuntimeError("Revealed attributes are not valid")

        response = Issuer.issue(sk, jsonpickle.decode(issuance_request),
                                username, attributes)
        return jsonpickle.encode(response).encode()
Esempio n. 8
0
    def serialize(self):
        """Serialize the object to a byte array.

        Returns: 
            byte[]: a byte array 
        """
        data = jsonpickle.encode(self)
        return data.encode()
Esempio n. 9
0
    def register(self, server_sk, issuance_request, username, attributes):
        """ Registers a new account on the server.

        Args:
            server_sk (byte []): the server's secret key (serialized)
            issuance_request (bytes[]): The issuance request (serialized)
            username (string): username
            attributes (string): attributes

            Note: You can use JSON to encode attributes in the string.

        Return:
            response (bytes[]): the client should be able to build a credential
            with this response.
        """

        #Reconstructing the secret key
        sk = jsonpickle.decode(server_sk)
        #Decoding the request
        decoded_request = jsonpickle.decode(issuance_request)
        C = decoded_request["C"]

        #Verifying the value for C
        s_sk = decoded_request["s_sk"]
        s_t = decoded_request["s_t"]
        R = decoded_request["R"]
        c = int.from_bytes(
            sha256((username + attributes).encode()).digest(),
            "big") % G1.order()  # hashing

        if (sk["Ys_G1"][0]**s_sk) * (G1.generator()**s_t) * C**c != R:
            raise Exception("Invalid register !")

        #Generating a random u
        u = G1.order().random()
        g_pow_u = G1.generator()**u
        X = sk[
            "X_G1"]  #Note : I decided to have X in the secret key, rather than recompute it everytime

        #Building the Ys
        Ys = sk["Ys_G1"]

        sigma_2 = X * C
        #The subscription attributes
        for att in attributes.split(","):
            if att not in sk["validAttributes"]:
                raise ValueError("Non-recognized attribute")
            else:
                index_of_att = sk["validAttributes"].index(att)
                y_i = Ys[index_of_att + 1]  #+1 because of the hidden attribute
                sigma_2 = sigma_2 * y_i
        json_res = {"sigma1": g_pow_u, "sigma2": sigma_2**u}
        return jsonpickle.encode(json_res).encode()
Esempio n. 10
0
    def prepare_registration(self, server_pk, username, attributes):
        """Prepare a request to register a new account on the server.

        Args:
            server_pk (byte[]): a server's public key (serialized)
            username (string): username
            attributes (string): user's attributes

            Note: You can use JSON to encode attributes in the string.

        Return:
            tuple:
                byte[]: an issuance request
                (private_state): You can use state to store and transfer information
                from prepare_registration to proceed_registration_response.
                You need to design the state yourself.
        """

        t = G1.order().random()
        pk = jsonpickle.decode(server_pk)
        Ys_in_G1 = pk["Ys_G1"]

        random_sk = G1.order().random()  # Some secret key as asked
        C = (G1.generator()**t) * (Ys_in_G1[0]**random_sk)
        for att in attributes.split(","):
            if att not in pk["validAttributes"]:
                raise ValueError(
                    "Attribute not in list of accepted attribute by the server"
                )

        issuance_request = {}
        issuance_request["C"] = C
        r_sk = G1.order().random()
        r_t = G1.order().random()
        c = int.from_bytes(
            sha256((username + attributes).encode()).digest(),
            "big") % G1.order()  # hashing

        s_sk = ((r_sk - random_sk * c) + G1.order()) % G1.order()
        s_t = ((r_t - t * c) + G1.order()) % G1.order()

        R = (Ys_in_G1[0]**r_sk) * (G1.generator()**r_t)
        if (Ys_in_G1[0]**s_sk) * (G1.generator()**s_t) * C**c != R:
            raise Exception("Invalid signature !")

        issuance_request["s_t"] = s_t
        issuance_request["s_sk"] = s_sk
        issuance_request["R"] = R

        return jsonpickle.encode(issuance_request).encode(), (
            random_sk, t
        )  #t is the inner state, we only need to remember that (since the key as secret attribute is completely useless)
Esempio n. 11
0
    def issue(sk, request, username, attributes):
        """Issues a credential for a new user. 

        This function should receive a issuance request from the user
        (AnonCredential.create_issue_request), and a list of known attributes of the
        user (e.g. the server received bank notes for subscriptions x, y, and z).

        You should design the issue_request as you see fit.
        """
        #extract public and secret key
        secret_key = sk[0]
        public_key = sk[1]

        #Derive challenge
        challenge = hashlib.sha256(jsonpickle.encode(request.C).encode())
        challenge.update(jsonpickle.encode(request.commitment).encode())
        challenge.update(jsonpickle.encode(public_key).encode())
        challenge = Bn.from_binary(challenge.digest())

        #Compare the derived challenge to the received challenge
        challenge_valid = challenge == request.challenge

        #Compute the zkp
        candidate = request.C ** challenge
        for e in zip(public_key, request.response):
            candidate = candidate * e[0] ** e[1]
        

        proof_valid = request.commitment == candidate

        #If the proof and the derived challenge is valid, sig the credential
        if proof_valid and challenge_valid:
            u = G1.order().random()
            sig = (public_key[0] ** u,(secret_key * request.C) ** u)
            return sig
        else :
            raise ValueError
Esempio n. 12
0
    def proceed_registration_response(self, server_pk, server_response,
                                      private_state):
        """Process the response from the server.

        Args:
            server_pk (byte[]): a server's public key (serialized)
            server_response (byte[]): the response from the server (serialized)
            private_state (private_state): state from the prepare_registration
            request corresponding to this response

        Return:
            credential (byte []): create an attribute-based credential for the user
        """
        credential = AnonCredential.receive_issue_response(
            jsonpickle.decode(server_pk)[0],
            jsonpickle.decode(server_response), private_state)
        return jsonpickle.encode(credential).encode()
Esempio n. 13
0
    def proceed_registration_response(self, server_pk, server_response,
                                      private_state):
        """Process the response from the server.

        Args:
            server_pk (byte[]): a server's public key (serialized)
            server_response (byte[]): the response from the server (serialized)
            private_state (private_state): state from the prepare_registration
            request corresponding to this response

        Return:
            credential (byte []): create an attribute-based credential for the user
        """

        sigma = jsonpickle.decode(server_response)
        (random_sk, t) = private_state
        sigma_bis = (sigma["sigma1"], sigma["sigma2"] / (sigma["sigma1"]**t))

        #Verifying the received signature
        pk = jsonpickle.decode(server_pk)
        Y_s = pk["Ys"]
        acc = G2.neutral_element()
        for (i, y_i) in zip(range(len(Y_s)), Y_s):
            if i == 0:
                acc = acc * (y_i**random_sk)
            else:
                acc = acc * y_i
        #Checking that the signature is valid
        if not (sigma_bis[0].pair(acc * pk["X"]) == sigma_bis[1].pair(
                G2.generator())):
            raise Exception("Invalid signature !")

        return jsonpickle.encode({
            "sigma": (sigma_bis[0], sigma_bis[1]),
            "random_sk": random_sk
        }).encode()
Esempio n. 14
0
 def serialize(self):
     data = jsonpickle.encode(self)
     return data.encode()
Esempio n. 15
0
    def sign_request(self, server_pk, credential, message, revealed_info):
        """Signs the request with the clients credential.

        Arg:
            server_pk (byte[]): a server's public key (serialized)
            credential (byte[]): client's credential (serialized)
            message (byte[]): message to sign
            revealed_info (string): attributes which need to be authorized

            Note: You can use JSON to encode revealed_info.

        Returns:
            byte []: message's signature (serialized)
        """
        # Generating r,t
        r = G1.order().random()
        t = G1.order().random()

        #Decoding the public key
        pk = jsonpickle.decode(server_pk)
        valid_attributes = pk["validAttributes"]

        decoded_credential = jsonpickle.decode(credential)
        sigma = decoded_credential["sigma"]
        random_sk = decoded_credential["random_sk"]

        sigma_bis = (sigma[0]**r, ((sigma[0]**t) * sigma[1])**r)

        values_to_prove = [
        ]  # Storing the values used in the proof of knowledge

        com = (sigma_bis[0]).pair(G2.generator())**t
        values_to_prove.append((sigma_bis[0]).pair(G2.generator()))
        # The secret part of the multiplication
        com = com * sigma_bis[0].pair(pk["Ys"][0]**random_sk)
        values_to_prove.append(sigma_bis[0].pair(pk["Ys"][0]))
        # Attributes
        for att in valid_attributes:
            if att not in revealed_info.split(","):
                #raise ValueError("Attribute not in the possible ones !")
                com = com * sigma_bis[0].pair(
                    pk["Ys"][valid_attributes.index(att) + 1])
                values_to_prove.append(sigma_bis[0].pair(
                    pk["Ys"][valid_attributes.index(att) + 1]))
        c = int.from_bytes(sha256(message).digest(),
                           "big") % G1.order()  # hashing

        #Adding the signature
        r_is = []
        R = GT.generator()**GT.order()  # The neutral element of the group
        for value in values_to_prove:
            r_i = G1.order().random()
            r_is.append(r_i)
            R = R * (value**r_i)  # creating the R part of the protocol

        s_is = []
        for (i, r_i) in zip(range(len(r_is)), r_is):
            tmp = r_i - c % G1.order()
            if i == 0:
                tmp = (r_i - c * t) % G1.order()
            if i == 1:
                tmp = (r_i - c * random_sk) % G1.order()

            if tmp < 0:
                tmp += G1.order()
            s_is.append(tmp)

        proof = {"c": c, "R": R, "s_is": s_is, "sigma": sigma_bis}
        return jsonpickle.encode(proof).encode()