package com.kryptnostic.storage.v1.models.request; import java.io.IOException; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ExecutionException; import org.apache.commons.codec.binary.Base64; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.kryptnostic.kodex.v1.crypto.ciphers.CryptoService; import com.kryptnostic.kodex.v1.crypto.ciphers.Cypher; import com.kryptnostic.kodex.v1.crypto.ciphers.PasswordCryptoService; import com.kryptnostic.kodex.v1.crypto.keys.CryptoServiceLoader; import com.kryptnostic.kodex.v1.exceptions.types.SecurityConfigurationException; import com.kryptnostic.kodex.v1.indexing.metadata.Metadata; import com.kryptnostic.kodex.v1.serialization.crypto.Encryptable; import com.kryptnostic.kodex.v1.serialization.jackson.KodexObjectMapperFactory; import com.kryptnostic.storage.v1.models.IndexedMetadata; import com.kryptnostic.utils.SecurityConfigurationTestUtils; import com.kryptnostic.utils.TestKeyLoader; @SuppressWarnings( "javadoc" ) public class AesMetadataRequestTests extends SecurityConfigurationTestUtils { private static final int INDEX_LENGTH = 256; @Test /** * Does implicit deserialization produce an Encryptable in a PLAIN state in the PRESENCE of a private key? * @throws IOException */ public void testImplicitDeserialization() throws SecurityConfigurationException, IOException, ClassNotFoundException { byte[] key = new byte[ INDEX_LENGTH >>>3 ]; new Random().nextBytes( key ); String documentId = "TEST"; Metadata metadatum = new Metadata( documentId, "test", Arrays.asList( 1, 2, 3 ) ); Encryptable<Metadata> data = new Encryptable<Metadata>( metadatum ); // explicit encryption to generate some json data = data.encrypt( loader ); String expected = serialize( new MetadataRequest( Arrays.asList( new IndexedMetadata( key, data, documentId ) ) ) ); System.out.println( expected ); MetadataRequest deserialized = deserialize( expected, MetadataRequest.class ); IndexedMetadata meta = deserialized.getMetadata().iterator().next(); // Ensure the key matches Assert.assertArrayEquals( key, meta.getKey() ); // Ensure we decrypted the metadata successfully Assert.assertFalse( meta.getData().isEncrypted() ); Assert.assertNull( meta.getData().getEncryptedClassName() ); Assert.assertEquals( metadatum.getClass().getName(), meta.getData().getClassName() ); Assert.assertEquals( metadatum, meta.getData().getData() ); } @Test /** * Does implicit deserialization produces an Encryptable in an ENCRYPTED state in the ABSENCE of a private key? * @throws IOException */ @Ignore // TODO: make decryption in cryptoService using wrong key fail fast by storing a hash of the blockCiphertext length // for comparison by cryptoService public void testImplicitDeserializationKeyless() throws SecurityConfigurationException, IOException, ClassNotFoundException { byte[] key = new byte[ INDEX_LENGTH >>>3 ]; new Random().nextBytes( key ); String documentId = "TEST"; Metadata metadatum = new Metadata( documentId, "test", Arrays.asList( 1, 2, 3 ) ); Encryptable<Metadata> data = new Encryptable<Metadata>( metadatum ); // explicit encryption to generate some json data = data.encrypt( loader ); String expected = serialize( new MetadataRequest( Arrays.asList( new IndexedMetadata( key, data, documentId ) ) ) ); MetadataRequest deserialized = deserialize( expected, MetadataRequest.class ); IndexedMetadata meta = deserialized.getMetadata().iterator().next(); // ensure nothing was decrypted Assert.assertArrayEquals( key, meta.getKey() ); Assert.assertNull( meta.getData().getClassName() ); Assert.assertNull( meta.getData().getData() ); // and ensure nothing was screwed up in the ciphertext as a result of deserialization Assert.assertArrayEquals( data.getEncryptedData(), meta.getData().getEncryptedData() ); Assert.assertArrayEquals( data.getEncryptedClassName().getContents(), meta.getData().getEncryptedClassName() .getContents() ); Assert.assertTrue( data.getEncryptedClassName().getEncryptedLength().isPresent() ); Assert.assertArrayEquals( data.getEncryptedClassName().getEncryptedLength().get(), meta.getData().getEncryptedClassName() .getEncryptedLength().get() ); } @Test /** * Does serialization of Encryptable work if you create an objectMapper with an FHE SecurityConfiguration? * @throws JsonGenerationException * @throws JsonMappingException * @throws IOException */ @Ignore public void testSerializationWithImplicitEncryption() throws JsonGenerationException, JsonMappingException, IOException { byte[] key = new byte[ INDEX_LENGTH >>>3 ]; new Random().nextBytes( key ); Metadata metadatum = new Metadata( "TEST", "test", Arrays.asList( 1, 2, 3 ) ); Encryptable<Metadata> data = new Encryptable<Metadata>( metadatum ); // implicit encryption via objectmapper // Create our request with our (PLAIN) Encryptable. It will get encrypted upon serialization MetadataRequest req = new MetadataRequest( Arrays.asList( new IndexedMetadata( key, data, "test" ) ) ); String actual = serialize( req ); String expectedSubstring = "{\"metadata\":[{\"key\":" + wrapQuotes( Base64.encodeBase64String( key ) ); // weak substring assertion that does not test ciphertext validity // ciphertext validity is covered in serializationDeserializationTest Assert.assertThat( actual, CoreMatchers.containsString( expectedSubstring ) ); } @Test /** * Does serialization of an Encryptable work if you opt to encrypt it explicitly, rather than registering * an FHE SecurityConfiguration? * @throws JsonGenerationException * @throws JsonMappingException * @throws IOException */ public void testSerializationWithExplicitEncryption() throws ClassNotFoundException, SecurityConfigurationException, IOException, ExecutionException { byte[] key = new byte[ INDEX_LENGTH >>>3 ]; new Random().nextBytes( key ); Metadata metadatum = new Metadata( "TEST", "test", Arrays.asList( 1, 2, 3 ) ); Encryptable<Metadata> data = new Encryptable<Metadata>( metadatum ); // explicit encryption CryptoService crypto = new PasswordCryptoService( Cypher.AES_CTR_128, "crypto-test".toCharArray() ); CryptoServiceLoader loader = new TestKeyLoader(); loader.put( PasswordCryptoService.class.getCanonicalName(), crypto ); data = data.encrypt( loader ); // create the metadataRequest with our (ENCRYPTED) Encryptable String docId = "test"; MetadataRequest req = new MetadataRequest( Arrays.asList( new IndexedMetadata( key, data, docId ) ) ); ObjectMapper unloadedMapper = KodexObjectMapperFactory.getObjectMapper(); String out = serialize( req, unloadedMapper ); MetadataRequest outReq = deserialize( out, MetadataRequest.class, unloadedMapper ); Assert.assertEquals( serialize( req, unloadedMapper ), serialize( outReq, unloadedMapper ) ); } }