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)
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')
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
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)
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]))
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
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)
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)