Esempio n. 1
0
    def generate_key_parameters(key_size: int) -> KeyParameters:
        """
        Generates new random key parameters of size key_size.

        :param key_size: the key size in bits
        :return: the key parameters containing p, q and g.
        """

        # Create safe primes
        while True:
            q = number.getPrime(key_size - 1)
            p = 2 * q + 1
            if number.isPrime(p):
                break

        # Create generator of subgroup of order q.
        #
        # Since subgroups can just have the orders 1, 2, q, 2q=p-1 testing is easy.
        # https://crypto.stackexchange.com/questions/7983/elgamal-generation-of-g-value
        # https://crypto.stackexchange.com/questions/1451/elgamal-multiplicative-cyclic-group-and-key-generation
        while True:
            g = number.getRandomRange(3, p - 2)
            if (pow(g, q, p) == 1 and pow(g, 2, p) != 1):
                break

        return KeyParameters(p, q, g)
Esempio n. 2
0
    def _encrypt_key_element(key_element: int, public_key: PublicKey) -> (int, int):
        key_params = public_key.key_parameters

        if key_element >= key_params.p:
            raise ThresholdCryptoError('key element is larger than key parameter p')

        k = number.getRandomRange(1, key_params.q - 1)
        g_k = pow(key_params.g, k, key_params.p) # aka v
        g_ak = pow(public_key.g_a, k, key_params.p)
        c = (key_element * g_ak) % key_params.p

        return g_k, c
Esempio n. 3
0
    def test_key_encryption_decryption_without_enough_shares(self):
        r = number.getRandomRange(2, self.kp.q)
        testkey_element = pow(self.kp.g, r, self.kp.p)
        g_k, c = ThresholdCrypto._encrypt_key_element(testkey_element, self.pk)
        em = EncryptedMessage(g_k, c, '')
        reconstruct_shares = [self.shares[i]
                              for i in [0, 4]]  # choose 2 of 5 key shares
        partial_decryptions = [
            ThresholdCrypto.compute_partial_decryption(em, share)
            for share in reconstruct_shares
        ]
        rec_testkey_element = ThresholdCrypto._combine_shares(
            partial_decryptions, em, self.tp, self.kp)

        self.assertNotEqual(testkey_element, rec_testkey_element)
Esempio n. 4
0
    def create_public_key_and_shares_centralized(key_params: KeyParameters, threshold_params: ThresholdParameters) -> (PublicKey, [KeyShare]):
        """
        Creates a public key and n shares by choosing a random secret key and using it for computations.

        :param key_params: key parameters to use
        :param threshold_params: parameters t and n for the threshold scheme
        :return: (the public key, n key shares)
        """
        a = number.getRandomRange(2, key_params.q - 2)
        g_a = pow(key_params.g, a, key_params.p)
        public_key = PublicKey(g_a, key_params)

        # Perform Shamir's secret sharing in Z_q
        polynom = number.PolynomMod.create_random_polynom(a, threshold_params.t - 1, key_params.q)
        supporting_points = range(1, threshold_params.n + 1)
        shares = [KeyShare(x, polynom.evaluate(x), key_params) for x in supporting_points]

        return public_key, shares
Esempio n. 5
0
    def encrypt_message(public_key: PublicKey,
                        message: str) -> EncryptedMessage:
        """
        Encrypt a message using a public key. A hybrid encryption approach is used to include advantages of symmetric
        encryption (fast, independent of message-length, integrity-preserving by using AE-scheme).
        Internally a combination of Salsa20 and Poly1305 from the cryptographic library NaCl is used.
        
        :param message: the message to be encrypted
        :param public_key: the public key
        :return: an encrypted message
        """
        encoded_message = bytes(message, 'utf-8')
        key_params = public_key.parameters

        # Create random subgroup element and use its hash as symmetric key to prevent
        # attacks described in "Why Textbook ElGamal and RSA Encryption Are Insecure"
        # by Boneh et. al.
        r = number.getRandomRange(2, key_params.q)
        key_subgroup_element = pow(key_params.g, r, key_params.p)
        key_subgroup_element_byte_length = (key_subgroup_element.bit_length() +
                                            7) // 8
        element_bytes = key_subgroup_element.to_bytes(
            key_subgroup_element_byte_length, byteorder='big')

        try:
            symmetric_key = nacl.hash.blake2b(
                element_bytes,
                digest_size=nacl.secret.SecretBox.KEY_SIZE,
                encoder=nacl.encoding.RawEncoder)
            # Use derived symmetric key to encrypt the message
            box = nacl.secret.SecretBox(symmetric_key)
            encrypted = box.encrypt(encoded_message).hex()
        except nacl.exceptions.CryptoError as e:
            print('Encryption failed: ' + str(e))
            raise ThresholdCryptoError('Message encryption failed.')

        # Use threshold scheme to encrypt the subgroup element used as hash input to derive the symmetric key
        g_k, c = ThresholdCrypto._encrypt_key_element(key_subgroup_element,
                                                      public_key)

        return EncryptedMessage(g_k, c, encrypted)