def test_close(self): client_encryption = ClientEncryption(KMS_PROVIDERS, 'admin.datakeys', client_context.client, OPTS) client_encryption.close() # Close can be called multiple times. client_encryption.close() algo = Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic msg = 'Cannot use closed ClientEncryption' with self.assertRaisesRegex(InvalidOperation, msg): client_encryption.create_data_key('local') with self.assertRaisesRegex(InvalidOperation, msg): client_encryption.encrypt('val', algo, key_alt_name='name') with self.assertRaisesRegex(InvalidOperation, msg): client_encryption.decrypt(Binary(b'', 6))
def test_validation(self): client_encryption = ClientEncryption(KMS_PROVIDERS, 'admin.datakeys', client_context.client, OPTS) self.addCleanup(client_encryption.close) msg = 'value to decrypt must be a bson.binary.Binary with subtype 6' with self.assertRaisesRegex(TypeError, msg): client_encryption.decrypt('str') with self.assertRaisesRegex(TypeError, msg): client_encryption.decrypt(Binary(b'123')) msg = 'key_id must be a bson.binary.Binary with subtype 4' algo = Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic with self.assertRaisesRegex(TypeError, msg): client_encryption.encrypt('str', algo, key_id=uuid.uuid4()) with self.assertRaisesRegex(TypeError, msg): client_encryption.encrypt('str', algo, key_id=Binary(b'123'))
def test_codec_options(self): with self.assertRaisesRegex(TypeError, 'codec_options must be'): ClientEncryption(KMS_PROVIDERS, 'keyvault.datakeys', client_context.client, None) opts = CodecOptions(uuid_representation=JAVA_LEGACY) client_encryption_legacy = ClientEncryption(KMS_PROVIDERS, 'keyvault.datakeys', client_context.client, opts) self.addCleanup(client_encryption_legacy.close) # Create the encrypted field's data key. key_id = client_encryption_legacy.create_data_key('local') # Encrypt a UUID with JAVA_LEGACY codec options. value = uuid.uuid4() encrypted_legacy = client_encryption_legacy.encrypt( value, Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=key_id) decrypted_value_legacy = client_encryption_legacy.decrypt( encrypted_legacy) self.assertEqual(decrypted_value_legacy, value) # Encrypt the same UUID with STANDARD codec options. client_encryption = ClientEncryption(KMS_PROVIDERS, 'keyvault.datakeys', client_context.client, OPTS) self.addCleanup(client_encryption.close) encrypted_standard = client_encryption.encrypt( value, Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=key_id) decrypted_standard = client_encryption.decrypt(encrypted_standard) self.assertEqual(decrypted_standard, value) # Test that codec_options is applied during encryption. self.assertNotEqual(encrypted_standard, encrypted_legacy) # Test that codec_options is applied during decryption. self.assertEqual(client_encryption_legacy.decrypt(encrypted_standard), value) self.assertNotEqual(client_encryption.decrypt(encrypted_legacy), value)
def test_encrypt_decrypt(self): client_encryption = ClientEncryption(KMS_PROVIDERS, 'keyvault.datakeys', client_context.client, OPTS) self.addCleanup(client_encryption.close) # Use standard UUID representation. key_vault = client_context.client.keyvault.get_collection( 'datakeys', codec_options=OPTS) self.addCleanup(key_vault.drop) # Create the encrypted field's data key. key_id = client_encryption.create_data_key('local', key_alt_names=['name']) self.assertBinaryUUID(key_id) self.assertTrue(key_vault.find_one({'_id': key_id})) # Create an unused data key to make sure filtering works. unused_key_id = client_encryption.create_data_key( 'local', key_alt_names=['unused']) self.assertBinaryUUID(unused_key_id) self.assertTrue(key_vault.find_one({'_id': unused_key_id})) doc = {'_id': 0, 'ssn': '000'} encrypted_ssn = client_encryption.encrypt( doc['ssn'], Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_id=key_id) # Ensure encryption via key_alt_name for the same key produces the # same output. encrypted_ssn2 = client_encryption.encrypt( doc['ssn'], Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic, key_alt_name='name') self.assertEqual(encrypted_ssn, encrypted_ssn2) # Test decryption. decrypted_ssn = client_encryption.decrypt(encrypted_ssn) self.assertEqual(decrypted_ssn, doc['ssn'])
def _test_corpus(self, opts): # Drop and create the collection 'db.coll' with jsonSchema. coll = create_with_schema( self.client.db.coll, self.fix_up_schema(json_data('corpus', 'corpus-schema.json'))) self.addCleanup(coll.drop) vault = create_key_vault(self.client.keyvault.datakeys, json_data('corpus', 'corpus-key-local.json'), json_data('corpus', 'corpus-key-aws.json')) self.addCleanup(vault.drop) client_encrypted = rs_or_single_client(auto_encryption_opts=opts, uuidRepresentation='standard') self.addCleanup(client_encrypted.close) client_encryption = ClientEncryption(self.kms_providers(), 'keyvault.datakeys', client_context.client, OPTS) self.addCleanup(client_encryption.close) corpus = self.fix_up_curpus(json_data('corpus', 'corpus.json')) corpus_copied = SON() for key, value in corpus.items(): corpus_copied[key] = copy.deepcopy(value) if key in ('_id', 'altname_aws', 'altname_local'): continue if value['method'] == 'auto': continue if value['method'] == 'explicit': identifier = value['identifier'] self.assertIn(identifier, ('id', 'altname')) kms = value['kms'] self.assertIn(kms, ('local', 'aws')) if identifier == 'id': if kms == 'local': kwargs = dict(key_id=LOCAL_KEY_ID) else: kwargs = dict(key_id=AWS_KEY_ID) else: kwargs = dict(key_alt_name=kms) self.assertIn(value['algo'], ('det', 'rand')) if value['algo'] == 'det': algo = ( Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic) else: algo = Algorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random try: encrypted_val = client_encryption.encrypt( value['value'], algo, **kwargs) if not value['allowed']: self.fail('encrypt should have failed: %r: %r' % (key, value)) corpus_copied[key]['value'] = encrypted_val except Exception: if value['allowed']: tb = traceback.format_exc() self.fail('encrypt failed: %r: %r, traceback: %s' % (key, value, tb)) client_encrypted.db.coll.insert_one(corpus_copied) corpus_decrypted = client_encrypted.db.coll.find_one() self.assertEqual(corpus_decrypted, corpus) corpus_encrypted_expected = self.fix_up_curpus_encrypted( json_data('corpus', 'corpus-encrypted.json'), corpus) corpus_encrypted_actual = coll.find_one() for key, value in corpus_encrypted_actual.items(): if key in ('_id', 'altname_aws', 'altname_local'): continue if value['algo'] == 'det': self.assertEqual(value['value'], corpus_encrypted_expected[key]['value'], key) elif value['algo'] == 'rand' and value['allowed']: self.assertNotEqual(value['value'], corpus_encrypted_expected[key]['value'], key) if value['allowed']: decrypt_actual = client_encryption.decrypt(value['value']) decrypt_expected = client_encryption.decrypt( corpus_encrypted_expected[key]['value']) self.assertEqual(decrypt_actual, decrypt_expected, key) else: self.assertEqual(value['value'], corpus[key]['value'], key)