Beispiel #1
0
    def test_asymmetric(self):
        rand = p.RandomDevice()
        context = p.CryptoContext()

        for cipher in p.AsymmetricCipher.__members__:
            if cipher == "UNSPECIFIED":
                continue

            for symmCipher in p.SymmetricCipher.__members__:
                if symmCipher == "UNSPECIFIED":
                    continue

                plaintext = p.Plaintext(get_random_data(), get_random_data())
                key = p.Key(p.AsymmetricCipher.__members__[cipher],
                            p.SymmetricCipher.__members__[symmCipher], rand)

                if cipher[:4] == "ECDH":
                    myKey = p.Key(p.AsymmetricCipher.__members__[cipher],
                                  p.SymmetricCipher.__members__[symmCipher],
                                  rand)
                    peerKey = p.Key(p.AsymmetricCipher.__members__[cipher],
                                    p.SymmetricCipher.__members__[symmCipher],
                                    rand)
                    key = p.Key(p.SymmetricCipher.__members__[symmCipher],
                                myKey, peerKey)

                encrypted = context.encrypt(key, plaintext, rand)
                self.assertTrue(
                    context.sign(key, plaintext, p.DigestAlgorithm.SHA_256,
                                 encrypted))
                serialized = context.serialize(p.DigestAlgorithm.SHA_256,
                                               encrypted)
                self.assertNotEqual(serialized, plaintext.data)

                deserialized = context.deserialize(serialized)

                # Assert the configs coming out are equal
                self.assertEqual(key.get_config().mode, deserialized[1].mode)
                self.assertEqual(key.get_config().symm_cipher,
                                 deserialized[1].symm_cipher)
                self.assertEqual(key.get_config().asymm_cipher,
                                 deserialized[1].asymm_cipher)
                # These won't be equal because the original key didn't have the digest algorithm set
                self.assertNotEqual(key.get_config().digest_algorithm,
                                    deserialized[1].digest_algorithm)

                # Do the decrypt
                result = context.decrypt(key, deserialized[0])
                self.assertTrue(result[1])
                self.assertEqual(result[0].data, plaintext.data)
                # Verify
                verified = context.verify(key, result[0], deserialized[0])
                self.assertTrue(verified)
Beispiel #2
0
    def encrypt_in_domain(self, plain_text: bytes, use_domain_name: str) -> bytes:
        assert isinstance(plain_text, bytes)
        assert isinstance(use_domain_name, str)

        self.__verify_bootstrapped_and_registered()
        self.__update_config_by_elasped_time(MAX_ELASPED_TIME)
        use_domain_for_encryption = self.__get_valid_use_domain_for_encryption(use_domain_name)
        encryption_key_id_for_encryption = self.__get_encryption_key_id(use_domain_for_encryption)

        symmetric_cipher = self.__get_symmetric_cipher(use_domain_for_encryption.symmetric_key_encryption_alg)
        signing_key = self.__get_signing_key(use_domain_for_encryption)
        digest = self.__get_digest_alg(use_domain_for_encryption.digest_algorithm)

        key = p.Key(symmetric_cipher, self.__get_key(encryption_key_id_for_encryption))

        # FIXME persister preferred keyid can cause delay if persister read from disk
        aad = CiphertextAAD(encryption_key_id_for_encryption, self.persister.load(PERSISTER_PREFERRED_KEYID))
        json_bytes = bytes(json.dumps(aad.__dict__), encoding='utf8')

        pm_plain_text = p.Plaintext(plain_text, json_bytes)
        random_device = p.RandomDevice()

        cipher_text = self.__crypto_context.encrypt(key, pm_plain_text, random_device)

        if not self.__crypto_context.sign(signing_key, pm_plain_text, digest, cipher_text):
            self.logger.warning('Signing failed. Verification not required when decrypting.')

        return self.__crypto_context.serialize(digest, cipher_text).encode(encoding='UTF-8')
Beispiel #3
0
    def __gen_new_asymmetric_keypair(self, persister: Persister) -> PublicKey:
        assert isinstance(persister, Persister)
        rand = p.RandomDevice()

        symm_cipher = DEFAULT_SYMM_CIPHER
        asymm_cipher = self.__get_asymmetric_cipher(self.crypto_config.client_key_type, self.crypto_config.client_key_bitlength)

        key = p.Key(asymm_cipher, symm_cipher, rand)

        pub_pem = key.get_pub_pem()
        priv_pem = key.get_priv_pem()

        created_time = int(round(time.time()))
        if created_time > sys.maxsize:
            self.logger.error("Failed to detect a valid time for local asymmetric key creation time")
            raise UnrecoverableClockSkewDetectedError("Failed to detect a valid time for local asymmetric key creation time")

        pub_key = PublicKey(id="",
                            key=pub_pem,
                            creation_time=created_time,
                            key_type=self.crypto_config.client_key_type,
                            encoding="pem")

        persister.save(PERSISTER_PRIV_KEY, priv_pem)
        persister.save(PERSISTER_PUB_KEY, pub_pem)
        persister.save(PERSISTER_ASYM_TYPE, self.crypto_config.client_key_type)
        persister.save(PERSISTER_ASYM_CREATED_DATE_EPOCH, created_time)
        persister.save(PERSISTER_ASYM_BITLEN, self.crypto_config.client_key_bitlength)

        return pub_key
Beispiel #4
0
    def __get_or_download_public_key(self, key_id: str) -> p.Key:
        assert isinstance(key_id, str)
        #FIXME: should we really use default symm cipher all the time?
        if (self.persister.exists(key_id)):
            return p.Key(DEFAULT_SYMM_CIPHER, self.persister.load(key_id), False)

        key_service_api = KeyServiceApi(self.__get_client())

        try:
            pub_key = key_service_api.get_public_key(key_id)
        except ApiException as e:
            self.logger.warning("Exception in getting public key: {}".format(e))
            raise ServerError(e)

        self.persister.save(key_id, pub_key.key)

        return p.Key(DEFAULT_SYMM_CIPHER, pub_key.key, False)
Beispiel #5
0
    def test_sign_only(self):
        rand = p.RandomDevice()
        context = p.CryptoContext()
        key = p.Key(p.RSA_2048, p.SymmetricCipher.CHACHA20_POLY1305, rand)

        plaintext = p.Plaintext(get_random_data(), get_random_data())
        blob = context.get_plaintext_blob(plaintext)
        self.assertTrue(
            context.sign(key, plaintext, p.DigestAlgorithm.SHA_256, blob))

        serialized = context.serialize(p.DigestAlgorithm.SHA_256, blob)
        self.assertNotEqual(serialized, plaintext.data)

        deserialized = context.deserialize(serialized)

        result = context.extract_plaintext_blob(deserialized[0])

        self.assertTrue(context.verify(key, result, deserialized[0]))
Beispiel #6
0
    def decrypt(self, cipher_text: bytes) -> bytes:
        assert isinstance(cipher_text, bytes)
        self.__verify_bootstrapped_and_registered()
        self.__update_config_by_elasped_time(MAX_ELASPED_TIME)

        cipher_text_blob, cfg = self.__crypto_context.deserialize(cipher_text)
        aad = self.__crypto_context.extract_unverified_aad(cipher_text).aad
        aad = self.__parse_cipher_text_AAD(aad)
        if not self.__is_key_id_decryption_viable(aad.cryptoKeyID):
            self.logger.warning("Ciphertext is no longer viable for decryption")
            raise NoValidUseDomainsForDecryptionError("Ciphertext is no longer viable for decryption")

        key = self.__get_key(aad.cryptoKeyID)
        pmKey = p.Key(p.SymmetricCipher(cfg.symm_cipher), key)
        plain_text, need_verification = self.__crypto_context.decrypt(pmKey, cipher_text_blob)
        if need_verification and not self.__verify_message(aad, cfg, cipher_text_blob, plain_text):
            self.logger.warning("Tampering of data detected, verification failed.")
            raise CoreCryptoError("Tampering of data detected, verification failed.")

        result = plain_text.data

        assert isinstance(result, bytes)
        return result
Beispiel #7
0
 def __bootsrapped_private_preferred_key_and_cipher(self):
     self.__loaded_private_preferred_cipher = self.__get_asymmetric_cipher(self.persister.load(PERSISTER_ASYM_TYPE), self.persister.load(PERSISTER_ASYM_BITLEN))
     self.__loaded_private_preferred_key = p.Key(DEFAULT_SYMM_CIPHER, self.persister.load(PERSISTER_PRIV_KEY), True)
Beispiel #8
0
    def __decrypt_and_save(self, all_keys: list):
        ''' decryptes the encrypted symmetric keys and save them to persister
        '''
        assert isinstance(all_keys, list)
        # loaded private preferred key should be loaded at boostrap
        if self.__loaded_private_preferred_key == None:
            self.logger.warning("SDK was not registered, please register before using other SDK operations")
            raise PeacemakrError("SDK was not registered, please register before using other SDK operations")

        for key in all_keys:
            if key == None:
                continue

            raw_cipher_text_str = key.packaged_ciphertext
            if raw_cipher_text_str == None:
                self.logger.info("Failed to get raw ciphertext str from EncryptedSymmetricKey")
                continue

            # deserialized[0] is a pycapsule object that decrypt takes in,
            # deseralized[1] is the config (sym cipher, asym cipher, digest algo, and mode)
            deserialized = self.__crypto_context.deserialize(raw_cipher_text_str)

            extracted_aad = self.__crypto_context.extract_unverified_aad(raw_cipher_text_str)
            # check if extracted aad is null
            if extracted_aad == "" or not extracted_aad:
                self.logger.warning("Failed to extrat aad from the ciphertext")
                raise CoreCryptoError("Failed to extract aad from the ciphertext")

            aad = json.loads(extracted_aad.aad)

            verification_key = self.__get_or_download_public_key(aad["senderKeyID"])

            if self.__is_ec(self.__loaded_private_preferred_cipher):
                # verfication is the peer key and client key is "my_key"
                if isinstance(verification_key, str):
                    verification_key = p.Key(DEFAULT_SYMM_CIPHER, verification_key, False)

                shared_symm_key = p.Key(DEFAULT_SYMM_CIPHER, self.__loaded_private_preferred_key, verification_key)
                result = self.__crypto_context.decrypt(shared_symm_key, deserialized[0])
            elif self.__is_rsa(self.__loaded_private_preferred_cipher):
                result = self.__crypto_context.decrypt(self.__loaded_private_preferred_key, deserialized[0])
            else:
                self.logger.warning("This version of python SDK only support ec or rsa. Invalid cipher.")
                raise InvalidCipherError("This version of python SDK only support ec or rsa. Invalid cipher.")

            # data is the plaintext, result = [plainText, NeedVerify:bool]; plainText = {data, aad}
            need_verfication = result[1]
            # check verfication
            if need_verfication:
                verified = self.__crypto_context.verify(verification_key, result[0], deserialized[0])
                if verified == False:
                    raise CoreCryptoError('Verification step failed')

            keys_in_str = result[0].data

            key_len = key.key_length
            keys_in_bytes = base64.decodebytes(keys_in_str)
            for i in range(len(key.key_ids)):
                # loop thru each key id and save their respective keys
                key_in_bytes = keys_in_bytes[i*key_len:(i+1)*key_len]
                self.__sym_key_cache[key.key_ids[i]] = key_in_bytes
                self.persister.save(key.key_ids[i], key_in_bytes)