diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/clientcrypto/test/service/ClientCryptoManagerServiceTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/clientcrypto/test/service/ClientCryptoManagerServiceTest.java new file mode 100644 index 00000000..94b0b422 --- /dev/null +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/clientcrypto/test/service/ClientCryptoManagerServiceTest.java @@ -0,0 +1,563 @@ +package io.mosip.kernel.clientcrypto.test.service; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.interfaces.RSAPublicKey; +import java.util.Comparator; + +import javax.crypto.SecretKey; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import io.mosip.kernel.clientcrypto.constant.ClientCryptoErrorConstants; +import io.mosip.kernel.clientcrypto.constant.ClientCryptoManagerConstant; +import io.mosip.kernel.clientcrypto.constant.ClientType; +import io.mosip.kernel.clientcrypto.exception.ClientCryptoException; +import io.mosip.kernel.clientcrypto.service.impl.AndroidClientCryptoServiceImpl; +import io.mosip.kernel.clientcrypto.service.impl.ClientCryptoFacade; +import io.mosip.kernel.clientcrypto.service.spi.ClientCryptoService; +import io.mosip.kernel.core.crypto.spi.CryptoCoreSpec; +import tss.Tpm; +import tss.TpmDeviceBase; +import tss.TpmDeviceTbs; +import tss.tpm.CreatePrimaryResponse; +import tss.tpm.TPMT_PUBLIC; +import tss.tpm.TPMA_OBJECT; +import tss.tpm.TPMS_NULL_ASYM_SCHEME; +import tss.tpm.TPMS_NULL_SIG_SCHEME; +import tss.tpm.TPMS_SIGNATURE_RSASSA; +import tss.tpm.TPMT_TK_HASHCHECK; +import tss.tpm.TPMS_RSA_PARMS; +import tss.tpm.TPMT_SYM_DEF_OBJECT; +import tss.tpm.TPMS_SIG_SCHEME_RSASSA; +import tss.tpm.TPMS_ENC_SCHEME_OAEP; +import tss.tpm.TPMS_SENSITIVE_CREATE; +import tss.tpm.TPMS_PCR_SELECTION; +import tss.tpm.TPM2B_PUBLIC_KEY_RSA; +import tss.tpm.TPM_HANDLE; +import tss.tpm.TPM_RH; +import tss.tpm.TPM_ALG_ID; + +@RunWith(MockitoJUnitRunner.class) +public class ClientCryptoManagerServiceTest { + + @Mock + private CryptoCoreSpec cryptoCore; + + @Mock + private ClientCryptoService clientCryptoService; + + @Mock + private org.springframework.context.ApplicationContext applicationContext; + + private KeyPair keyPair; + private byte[] sampleData; + private ClientCryptoFacade clientCryptoFacade; + private static final int symmetricKeyLength = 32; + private static final int ivLength = 12; + private static final int aadLength = 16; + + @Before + public void setUp() throws Exception { + keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); + sampleData = "client-data".getBytes(StandardCharsets.UTF_8); + + clientCryptoFacade = new ClientCryptoFacade(); + injectField(clientCryptoFacade, "cryptoCore", cryptoCore); + injectField(clientCryptoFacade, "applicationContext", applicationContext); + injectField(clientCryptoFacade, "useResidentServiceModuleKey", false); + injectField(clientCryptoFacade, "residentServiceAppId", "RESIDENT"); + injectField(clientCryptoFacade, "ivLength", 12); + injectField(clientCryptoFacade, "aadLength", 16); + injectField(clientCryptoFacade, "symmetricKeyLength", 32); + + setStaticField(ClientCryptoFacade.class, "clientCryptoService", clientCryptoService); + setStaticField(ClientCryptoFacade.class, "secureRandom", new java.security.SecureRandom()); + setStaticField(Class.forName("io.mosip.kernel.clientcrypto.service.impl.LocalClientCryptoServiceImpl"), + "cryptoCore", cryptoCore); + + lenient().when(cryptoCore.symmetricEncrypt(any(SecretKey.class), any(byte[].class), any(byte[].class), any(byte[].class))) + .thenReturn("cipher".getBytes(StandardCharsets.UTF_8)); + lenient().when(cryptoCore.symmetricDecrypt(any(SecretKey.class), any(byte[].class), any(byte[].class), any(byte[].class))) + .thenReturn(sampleData); + lenient().when(cryptoCore.asymmetricEncrypt(any(PublicKey.class), any(byte[].class))) + .thenAnswer(inv -> ((byte[]) inv.getArgument(1))); + lenient().when(cryptoCore.asymmetricDecrypt(any(PrivateKey.class), any(byte[].class))) + .thenAnswer(inv -> ((byte[]) inv.getArgument(1))); + } + + @After + public void tearDown() throws Exception { + setStaticField(ClientCryptoFacade.class, "clientCryptoService", null); + setStaticField(ClientCryptoFacade.class, "secureRandom", null); + setStaticField(Class.forName("io.mosip.kernel.clientcrypto.service.impl.LocalClientCryptoServiceImpl"), + "cryptoCore", null); + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + resetTpmStatics(tpmClass); + cleanKeysDirectory(); + } + + @Test + public void testAndroidValidateSignatureSuccess() throws Exception { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(keyPair.getPrivate()); + signature.update(sampleData); + byte[] signed = signature.sign(); + + boolean verified = AndroidClientCryptoServiceImpl.validateSignature(keyPair.getPublic().getEncoded(), + signed, sampleData); + + assertTrue(verified); + } + + @Test(expected = ClientCryptoException.class) + public void testAndroidValidateSignatureThrowsException() throws Exception { + AndroidClientCryptoServiceImpl.validateSignature("invalid".getBytes(StandardCharsets.UTF_8), + "sig".getBytes(StandardCharsets.UTF_8), sampleData); + } + + @Test + public void testAndroidAsymmetricEncryptProducesCiphertext() throws Exception { + byte[] cipher = AndroidClientCryptoServiceImpl.asymmetricEncrypt(keyPair.getPublic().getEncoded(), sampleData); + assertNotNull(cipher); + assertTrue(cipher.length > 0); + } + + @Test + public void testClientCryptoFacadeEncryptWithAndroidClient() { + byte[] envelope = clientCryptoFacade.encrypt(ClientType.ANDROID, + keyPair.getPublic().getEncoded(), sampleData); + + assertNotNull(envelope); + assertTrue(envelope.length > 0); + verify(cryptoCore).symmetricEncrypt(any(SecretKey.class), eq(sampleData), + any(byte[].class), any(byte[].class)); + } + + @Test + public void testClientCryptoFacadeDecryptUsesClientSecurity() throws Exception { + byte[] secret = new byte[32]; + byte[] iv = new byte[12]; + byte[] aad = new byte[16]; + byte[] cipher = "cipher".getBytes(StandardCharsets.UTF_8); + byte[] payload = new byte[secret.length + iv.length + aad.length + cipher.length]; + + System.arraycopy(new byte[secret.length], 0, payload, 0, secret.length); + System.arraycopy(iv, 0, payload, secret.length, iv.length); + System.arraycopy(aad, 0, payload, secret.length + iv.length, aad.length); + System.arraycopy(cipher, 0, payload, secret.length + iv.length + aad.length, cipher.length); + + when(clientCryptoService.asymmetricDecrypt(any(byte[].class))).thenReturn(secret); + + byte[] result = clientCryptoFacade.decrypt(payload); + + assertArrayEquals(sampleData, result); + verify(clientCryptoService).asymmetricDecrypt(any(byte[].class)); + verify(cryptoCore).symmetricDecrypt(any(SecretKey.class), any(byte[].class), any(byte[].class), any(byte[].class)); + } + + @Test + public void testClientCryptoFacadeGenerateRandomBytes() { + byte[] random = ClientCryptoFacade.generateRandomBytes(24); + assertNotNull(random); + assertEquals(24, random.length); + } + + @Test + public void testLocalClientCryptoServiceSignAndValidate() throws Exception { + ClientCryptoService localService = createLocalClientCryptoService(); + byte[] signature = localService.signData(sampleData); + assertNotNull(signature); + assertTrue(localService.validateSignature(signature, sampleData)); + } + + @Test + public void testLocalClientCryptoServiceAsymmetricEncryptUsesCryptoCore() throws Exception { + ClientCryptoService localService = createLocalClientCryptoService(); + byte[] result = localService.asymmetricEncrypt(sampleData); + assertArrayEquals(sampleData, result); + verify(cryptoCore).asymmetricEncrypt(any(PublicKey.class), eq(sampleData)); + } + + @Test + public void testLocalClientCryptoServiceGenerateRandomBytes() throws Exception { + Class localClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.LocalClientCryptoServiceImpl"); + Method method = localClass.getDeclaredMethod("generateRandomBytes", int.class); + method.setAccessible(true); + byte[] random = (byte[]) method.invoke(null, 8); + assertEquals(8, random.length); + } + + @Test + public void testTpmClientCryptoServiceOperations() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + + TPMT_PUBLIC signingPublic = Mockito.mock(TPMT_PUBLIC.class); + TPMT_PUBLIC encryptionPublic = Mockito.mock(TPMT_PUBLIC.class); + lenient().when(signingPublic.toTpm()).thenReturn("signPub".getBytes(StandardCharsets.UTF_8)); + lenient().when(encryptionPublic.toTpm()).thenReturn("encPub".getBytes(StandardCharsets.UTF_8)); + + CreatePrimaryResponse signingResponse = new CreatePrimaryResponse(); + signingResponse.handle = new TPM_HANDLE(0x81000001); + signingResponse.outPublic = signingPublic; + + CreatePrimaryResponse encryptionResponse = new CreatePrimaryResponse(); + encryptionResponse.handle = new TPM_HANDLE(0x81000002); + encryptionResponse.outPublic = encryptionPublic; + + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", signingResponse); + setStaticField(tpmClass, "encPrimaryResponse", encryptionResponse); + + Constructor ctor = tpmClass.getDeclaredConstructor(); + ctor.setAccessible(true); + ClientCryptoService tpmService = (ClientCryptoService) ctor.newInstance(); + + TPMS_SIGNATURE_RSASSA rsassa = new TPMS_SIGNATURE_RSASSA(TPM_ALG_ID.SHA256, + "sig".getBytes(StandardCharsets.UTF_8)); + when(tpmMock.Sign(any(TPM_HANDLE.class), any(byte[].class), any(TPMS_NULL_SIG_SCHEME.class), + any(TPMT_TK_HASHCHECK.class))).thenReturn(rsassa); + when(tpmMock.RSA_Decrypt(any(TPM_HANDLE.class), any(byte[].class), any(TPMS_NULL_ASYM_SCHEME.class), any(byte[].class))) + .thenReturn("plain".getBytes(StandardCharsets.UTF_8)); + when(tpmMock.GetRandom(4)).thenReturn(new byte[] { 1, 2, 3, 4 }); + + assertArrayEquals("sig".getBytes(StandardCharsets.UTF_8), tpmService.signData(sampleData)); + assertArrayEquals("plain".getBytes(StandardCharsets.UTF_8), tpmService.asymmetricDecrypt("cipher".getBytes())); + + Method randomMethod = tpmClass.getDeclaredMethod("generateRandomBytes", int.class); + randomMethod.setAccessible(true); + byte[] random = (byte[]) randomMethod.invoke(null, 4); + assertArrayEquals(new byte[] { 1, 2, 3, 4 }, random); + + doThrow(new IOException("boom")).when(tpmMock).close(); + tpmService.closeSecurityInstance(); + + setStaticField(tpmClass, "tpm", null); + setStaticField(tpmClass, "signingPrimaryResponse", null); + setStaticField(tpmClass, "encPrimaryResponse", null); + } + + @Test + public void testTpmGetSigningPublicPartInitializesCache() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", null); + setStaticField(tpmClass, "encPrimaryResponse", null); + + CreatePrimaryResponse signingResponse = buildSigningPrimaryResponse(); + when(tpmMock.CreatePrimary(any(TPM_HANDLE.class), any(TPMS_SENSITIVE_CREATE.class), + any(TPMT_PUBLIC.class), any(byte[].class), any(TPMS_PCR_SELECTION[].class))) + .thenReturn(signingResponse); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + byte[] publicPart = tpmService.getSigningPublicPart(); + + assertArrayEquals(signingResponse.outPublic.toTpm(), publicPart); + verify(tpmMock).CreatePrimary(any(TPM_HANDLE.class), any(TPMS_SENSITIVE_CREATE.class), + any(TPMT_PUBLIC.class), any(byte[].class), any(TPMS_PCR_SELECTION[].class)); + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmGetEncryptionPublicPartInitializesCache() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", null); + setStaticField(tpmClass, "encPrimaryResponse", null); + + CreatePrimaryResponse encryptionResponse = buildEncryptionPrimaryResponse(); + when(tpmMock.CreatePrimary(any(TPM_HANDLE.class), any(TPMS_SENSITIVE_CREATE.class), + any(TPMT_PUBLIC.class), isNull(), isNull())) + .thenReturn(encryptionResponse); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + byte[] publicPart = tpmService.getEncryptionPublicPart(); + + assertArrayEquals(encryptionResponse.outPublic.toTpm(), publicPart); + verify(tpmMock).CreatePrimary(any(TPM_HANDLE.class), any(TPMS_SENSITIVE_CREATE.class), + any(TPMT_PUBLIC.class), isNull(), isNull()); + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmSignDataThrowsWhenTpmNull() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + setStaticField(tpmClass, "tpm", null); + + try { + tpmService.signData(sampleData); + fail("Expected ClientCryptoException when TPM instance is null"); + } catch (ClientCryptoException expected) { + // expected + } + + try { + tpmService.asymmetricDecrypt(sampleData); + fail("Expected ClientCryptoException when TPM instance is null"); + } catch (ClientCryptoException expected) { + // expected + } + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmCloseSecurityInstanceWithNullTpmDoesNothing() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + setStaticField(tpmClass, "tpm", null); + + tpmService.closeSecurityInstance(); + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmSignDataThrowsWhenSignedDataNull() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + when(tpmMock.Sign(any(TPM_HANDLE.class), any(byte[].class), any(TPMS_NULL_SIG_SCHEME.class), + any(TPMT_TK_HASHCHECK.class))).thenReturn(null); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + + try { + tpmService.signData(sampleData); + fail("Expected ClientCryptoException"); + } catch (ClientCryptoException ex) { + assertEquals(ClientCryptoErrorConstants.CRYPTO_FAILED.getErrorCode(), ex.getErrorCode()); + } + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmCloseSecurityInstanceInvokesClose() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + tpmService.closeSecurityInstance(); + + verify(tpmMock).close(); + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmGetSecretKeyViaReflection() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Method method = tpmClass.getDeclaredMethod("getSecretKey"); + method.setAccessible(true); + Object result = method.invoke(null); + assertNotNull(result); + } + + @Test + public void testTpmIsKernelModeTRMTrueBranch() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + when(tpmMock._getDevice()).thenReturn(mock(TpmDeviceTbs.class)); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + Method method = tpmClass.getDeclaredMethod("isKernelModeTRM"); + method.setAccessible(true); + assertTrue((Boolean) method.invoke(tpmService)); + + resetTpmStatics(tpmClass); + } + + @Test + public void testTpmIsKernelModeTRMFalseBranch() throws Exception { + Class tpmClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.TPMClientCryptoServiceImpl"); + Tpm tpmMock = mock(Tpm.class); + setStaticField(tpmClass, "tpm", tpmMock); + setStaticField(tpmClass, "signingPrimaryResponse", buildSigningPrimaryResponse()); + setStaticField(tpmClass, "encPrimaryResponse", buildEncryptionPrimaryResponse()); + + when(tpmMock._getDevice()).thenReturn((TpmDeviceBase) null); + + ClientCryptoService tpmService = instantiateTpmService(tpmClass); + Method method = tpmClass.getDeclaredMethod("isKernelModeTRM"); + method.setAccessible(true); + assertFalse((Boolean) method.invoke(tpmService)); + + resetTpmStatics(tpmClass); + } + + @Test + public void testClientCryptoFacadeValidateSignatureDefaultPath() throws Exception { + Signature signature = Signature.getInstance("SHA256withRSA"); + signature.initSign(keyPair.getPrivate()); + signature.update(sampleData); + byte[] signed = signature.sign(); + + boolean verified = clientCryptoFacade.validateSignature(keyPair.getPublic().getEncoded(), signed, sampleData); + assertTrue(verified); + } + + @Test + public void testClientCryptoFacadeDecryptFallbackPath() throws Exception { + byte[] secret = new byte[symmetricKeyLength]; + byte[] iv = new byte[ivLength]; + byte[] aad = new byte[aadLength]; + byte[] cipher = "cipher".getBytes(StandardCharsets.UTF_8); + when(clientCryptoService.asymmetricDecrypt(any(byte[].class))).thenReturn(secret); + when(cryptoCore.symmetricDecrypt(any(SecretKey.class), any(byte[].class), any(byte[].class), any(byte[].class))) + .thenThrow(new RuntimeException("primary")) + .thenReturn(sampleData); + + byte[] payload = new byte[secret.length + iv.length + aad.length + cipher.length]; + System.arraycopy(secret, 0, payload, 0, secret.length); + System.arraycopy(iv, 0, payload, secret.length, iv.length); + System.arraycopy(aad, 0, payload, secret.length + iv.length, aad.length); + System.arraycopy(cipher, 0, payload, secret.length + iv.length + aad.length, cipher.length); + + byte[] result = clientCryptoFacade.decrypt(payload); + + assertArrayEquals(sampleData, result); + verify(cryptoCore, times(2)).symmetricDecrypt(any(SecretKey.class), any(byte[].class), any(byte[].class), any(byte[].class)); + } + + private ClientCryptoService createLocalClientCryptoService() throws Exception { + cleanKeysDirectory(); + Class localClass = Class.forName("io.mosip.kernel.clientcrypto.service.impl.LocalClientCryptoServiceImpl"); + Constructor ctor = localClass.getDeclaredConstructor(CryptoCoreSpec.class, + org.springframework.context.ApplicationContext.class, Boolean.class, String.class); + ctor.setAccessible(true); + return (ClientCryptoService) ctor.newInstance(cryptoCore, applicationContext, false, "RESIDENT"); + } + + private ClientCryptoService instantiateTpmService(Class tpmClass) throws Exception { + Constructor ctor = tpmClass.getDeclaredConstructor(); + ctor.setAccessible(true); + return (ClientCryptoService) ctor.newInstance(); + } + + private void injectField(Object target, String name, Object value) throws Exception { + Field field = target.getClass().getDeclaredField(name); + field.setAccessible(true); + field.set(target, value); + } + + private void setStaticField(Class clazz, String fieldName, Object value) throws Exception { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(null, value); + } + + private void cleanKeysDirectory() throws IOException { + Path keysDir = Paths.get(ClientCryptoManagerConstant.KEY_PATH, ClientCryptoManagerConstant.KEYS_DIR); + if (Files.exists(keysDir)) { + Files.walk(keysDir) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(java.io.File::delete); + } + } + + private CreatePrimaryResponse buildSigningPrimaryResponse() { + CreatePrimaryResponse response = new CreatePrimaryResponse(); + response.handle = new TPM_HANDLE(0x81010001); + response.outPublic = buildSigningPublicArea(); + return response; + } + + private CreatePrimaryResponse buildEncryptionPrimaryResponse() { + CreatePrimaryResponse response = new CreatePrimaryResponse(); + response.handle = new TPM_HANDLE(0x81010002); + response.outPublic = buildEncryptionPublicArea(); + return response; + } + + private TPMT_PUBLIC buildSigningPublicArea() { + return new TPMT_PUBLIC(TPM_ALG_ID.SHA1, + new TPMA_OBJECT(TPMA_OBJECT.fixedTPM, TPMA_OBJECT.fixedParent, TPMA_OBJECT.sign, + TPMA_OBJECT.sensitiveDataOrigin, TPMA_OBJECT.userWithAuth), + new byte[0], + new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.NULL, 0, TPM_ALG_ID.NULL), + new TPMS_SIG_SCHEME_RSASSA(TPM_ALG_ID.SHA256), 2048, 65537), + new TPM2B_PUBLIC_KEY_RSA(rsaModulusBytes())); + } + + private TPMT_PUBLIC buildEncryptionPublicArea() { + return new TPMT_PUBLIC(TPM_ALG_ID.SHA256, + new TPMA_OBJECT(TPMA_OBJECT.fixedTPM, TPMA_OBJECT.fixedParent, + TPMA_OBJECT.decrypt, TPMA_OBJECT.sensitiveDataOrigin, TPMA_OBJECT.userWithAuth), + new byte[0], + new TPMS_RSA_PARMS(new TPMT_SYM_DEF_OBJECT(TPM_ALG_ID.NULL, 0, TPM_ALG_ID.NULL), + new TPMS_ENC_SCHEME_OAEP(TPM_ALG_ID.SHA256), 2048, 65537), + new TPM2B_PUBLIC_KEY_RSA(rsaModulusBytes())); + } + + private byte[] rsaModulusBytes() { + byte[] modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray(); + byte[] normalized = new byte[256]; + if (modulus.length >= 256) { + System.arraycopy(modulus, modulus.length - 256, normalized, 0, 256); + } else { + System.arraycopy(modulus, 0, normalized, 256 - modulus.length, modulus.length); + } + return normalized; + } + + private void resetTpmStatics(Class tpmClass) throws Exception { + setStaticField(tpmClass, "tpm", null); + setStaticField(tpmClass, "signingPrimaryResponse", null); + setStaticField(tpmClass, "encPrimaryResponse", null); + } +} diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/crypto/jce/test/CryptoCoreTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/crypto/jce/test/CryptoCoreTest.java index 575c282a..0551e01f 100644 --- a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/crypto/jce/test/CryptoCoreTest.java +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/crypto/jce/test/CryptoCoreTest.java @@ -10,11 +10,15 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import io.mosip.kernel.keymanagerservice.test.KeymanagerTestBootApplication; +import io.mosip.kernel.keymanagerservice.util.KeymanagerUtil; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,7 +30,7 @@ import io.mosip.kernel.core.crypto.exception.SignatureException; import io.mosip.kernel.core.crypto.spi.CryptoCoreSpec; -@SpringBootTest +@SpringBootTest(classes = { KeymanagerTestBootApplication.class }) @RunWith(SpringRunner.class) public class CryptoCoreTest { @@ -35,6 +39,9 @@ public class CryptoCoreTest { @Autowired private CryptoCoreSpec cryptoCore; + @Autowired + private KeymanagerUtil keymanagerUtil; + private KeyPair rsaPair; private byte[] data; @@ -43,6 +50,28 @@ public class CryptoCoreTest { private final SecureRandom random = new SecureRandom(); + private String certificate = "-----BEGIN CERTIFICATE-----\n" + + "MIIDbDCCAlSgAwIBAgIUTW8ScXGEgz/C0o7xnAsBmd3P8hswDQYJKoZIhvcNAQEL\n" + + "BQAwbzELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMRIwEAYDVQQHDAlCZW5nYWx1\n" + + "cnUxDjAMBgNVBAoMBU1vc2lwMRMwEQYDVQQLDApLZXltYW5hZ2VyMRowGAYDVQQD\n" + + "DBFQTVMtcm9vdC10ZXN0Y2FzZTAgFw0yNTEwMTMxMzQzMzZaGA8yMTI1MTAxMzEz\n" + + "NDMzNlowbzELMAkGA1UEBhMCSU4xCzAJBgNVBAgMAktBMRIwEAYDVQQHDAlCZW5n\n" + + "YWx1cnUxDjAMBgNVBAoMBU1vc2lwMRMwEQYDVQQLDApLZXltYW5hZ2VyMRowGAYD\n" + + "VQQDDBFQTVMtcm9vdC10ZXN0Y2FzZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\n" + + "AQoCggEBANZqa/+RIVKaoIiQ11pFXOCL1NgOd6F1a98KIWU3ZZ8Kh/CjPN5V5QN/\n" + + "pqLX5/4+Zw4tJJqsruQmCz76LCLFREuoWTByNtnKZDni1quNRkcz7uiKeOLFHzk4\n" + + "QODDF4BfefaQElOLSMdHueoKgWBor+/E9aK8+vvk3kPOtC67RmhWCJ5TAI19kCaY\n" + + "lBrneAx+JmQxJ8sAHszErHxjdlEIUNSoU4GbIrgw4C8dtdG6yVb3arM9+kCsa0hg\n" + + "JGYCW8igi8P0yyUoeGpi86ZiYjiIVGZS7dmZM/vGun+JjaHtTlBCvCsMxVstrhMZ\n" + + "AgVZouiaXgmbvubSXDuBBOL6pDRWFocCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA\n" + + "irKsATgEedB8IoD4WeGW7KRuPxT6iow4yQUf9kODEYzsNKRdvowUD97MnORaF1ns\n" + + "EtA+vTfutktHHMhnBNfuFyZFsZCqq3skbRGst9RjxokznljE/OZc0q+24Hm9dRfZ\n" + + "SMBYWPEnFQzpvPmOexLwRRwt6EGrZPWUh22NGYLbJR22CP5wTgsUKwA6MHcAVVTS\n" + + "5+WcxMD0OMoRX5LIlFLUSyyZb6POs/lsta7+fr2FU84FNLrooz0Q+8/QzTpW/XND\n" + + "N3yr7o9LBHFXwVB+Fb6ow4/r9hPuBFg58FM+wQt5AJ5cz/LeOKsVpDJ8Bvuodrxa\n" + + "vb31TtM0csPVLODrpnNZyA==\n" + + "-----END CERTIFICATE-----"; + @Before public void init() throws java.security.NoSuchAlgorithmException { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); @@ -224,4 +253,29 @@ public void testAsymmetricPublicInvalidKeyDecrypt() throws NoSuchAlgorithmExcept byte[] encryptedData = cryptoCore.asymmetricEncrypt(rsaPair.getPublic(), data); assertThat(cryptoCore.asymmetricDecrypt(invalidKeyPair.getPrivate(), rsaPair.getPublic(), encryptedData), isA(byte[].class)); } + + @Test + public void signTest() { + X509Certificate x509Certificate = (X509Certificate) keymanagerUtil.convertToCertificate(certificate); + String result = cryptoCore.sign(data, rsaPair.getPrivate(), x509Certificate); + Assert.assertNotNull(result); + } + + @Test + public void verifySignatureTest() { + X509Certificate x509Certificate = (X509Certificate) keymanagerUtil.convertToCertificate(certificate); + String signature = cryptoCore.sign(data, rsaPair.getPrivate(), x509Certificate); + boolean result = cryptoCore.verifySignature(signature); + Assert.assertFalse(result); + } + + @Test(expected = SignatureException.class) + public void verifySignatureException() { + cryptoCore.verifySignature(""); + } + + @Test(expected = SignatureException.class) + public void verifySignatureInvalidSign() { + cryptoCore.verifySignature("Invalid Signature"); + } } diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/Argon2HashServiceTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/Argon2HashServiceTest.java new file mode 100644 index 00000000..44caf87d --- /dev/null +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/service/Argon2HashServiceTest.java @@ -0,0 +1,311 @@ +package io.mosip.kernel.cryptomanager.test.service; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import io.mosip.kernel.core.http.RequestWrapper; +import io.mosip.kernel.core.http.ResponseWrapper; +import io.mosip.kernel.cryptomanager.controller.CryptomanagerController; +import io.mosip.kernel.cryptomanager.dto.Argon2GenerateHashRequestDto; +import io.mosip.kernel.cryptomanager.dto.Argon2GenerateHashResponseDto; +import io.mosip.kernel.cryptomanager.exception.CryptoManagerSerivceException; +import io.mosip.kernel.cryptomanager.service.CryptomanagerService; +import io.mosip.kernel.cryptomanager.service.impl.CryptomanagerServiceImpl; +import io.mosip.kernel.cryptomanager.util.CryptomanagerUtils; + +@RunWith(MockitoJUnitRunner.class) +public class Argon2HashServiceTest { + + @Mock + private CryptomanagerService cryptomanagerService; + + @InjectMocks + private CryptomanagerController cryptomanagerController; + + private Argon2GenerateHashRequestDto validRequest; + private Argon2GenerateHashRequestDto requestWithSalt; + private Argon2GenerateHashResponseDto validResponse; + private String testInputData = "dGVzdCBkYXRh"; + private String testSalt = "dGVzdFNhbHQ"; + + @Before + public void setUp() { + validRequest = new Argon2GenerateHashRequestDto(); + validRequest.setInputData(testInputData); + + requestWithSalt = new Argon2GenerateHashRequestDto(); + requestWithSalt.setInputData(testInputData); + requestWithSalt.setSalt(testSalt); + + validResponse = new Argon2GenerateHashResponseDto(); + validResponse.setHashValue("mockHashValue"); + validResponse.setSalt("mockSalt"); + } + + @Test + public void testGenerateArgon2Hash_Controller_Success() { + RequestWrapper requestWrapper = new RequestWrapper<>(); + requestWrapper.setRequest(validRequest); + + when(cryptomanagerService.generateArgon2Hash(any(Argon2GenerateHashRequestDto.class))) + .thenReturn(validResponse); + + ResponseWrapper response = + cryptomanagerController.generateArgon2Hash(requestWrapper); + + assertNotNull(response); + assertNotNull(response.getResponse()); + assertEquals("mockHashValue", response.getResponse().getHashValue()); + assertEquals("mockSalt", response.getResponse().getSalt()); + verify(cryptomanagerService).generateArgon2Hash(validRequest); + } + + @Test + public void testGenerateArgon2Hash_Controller_WithSalt() { + RequestWrapper requestWrapper = new RequestWrapper<>(); + requestWrapper.setRequest(requestWithSalt); + + Argon2GenerateHashResponseDto responseWithSalt = new Argon2GenerateHashResponseDto(); + responseWithSalt.setHashValue("hashWithSalt"); + responseWithSalt.setSalt(testSalt); + + when(cryptomanagerService.generateArgon2Hash(any(Argon2GenerateHashRequestDto.class))) + .thenReturn(responseWithSalt); + + ResponseWrapper response = + cryptomanagerController.generateArgon2Hash(requestWrapper); + + assertNotNull(response); + assertEquals("hashWithSalt", response.getResponse().getHashValue()); + assertEquals(testSalt, response.getResponse().getSalt()); + verify(cryptomanagerService).generateArgon2Hash(requestWithSalt); + } + + @Test(expected = CryptoManagerSerivceException.class) + public void testGenerateArgon2Hash_Controller_ServiceException() { + RequestWrapper requestWrapper = new RequestWrapper<>(); + requestWrapper.setRequest(validRequest); + + when(cryptomanagerService.generateArgon2Hash(any(Argon2GenerateHashRequestDto.class))) + .thenThrow(new CryptoManagerSerivceException("KER-CRY-001", "Invalid request")); + + cryptomanagerController.generateArgon2Hash(requestWrapper); + } + + @Test + public void testGenerateArgon2Hash_Controller_NullRequest() { + RequestWrapper requestWrapper = new RequestWrapper<>(); + requestWrapper.setRequest(null); + + when(cryptomanagerService.generateArgon2Hash(null)) + .thenThrow(new CryptoManagerSerivceException("KER-CRY-001", "Request cannot be null")); + + try { + cryptomanagerController.generateArgon2Hash(requestWrapper); + fail("Expected exception was not thrown"); + } catch (CryptoManagerSerivceException e) { + assertEquals("KER-CRY-001", e.getErrorCode()); + } + } + + @Test + public void testGenerateArgon2Hash_Service_Success() { + when(cryptomanagerService.generateArgon2Hash(validRequest)) + .thenReturn(validResponse); + + Argon2GenerateHashResponseDto response = cryptomanagerService.generateArgon2Hash(validRequest); + + assertNotNull(response); + assertEquals("mockHashValue", response.getHashValue()); + assertEquals("mockSalt", response.getSalt()); + verify(cryptomanagerService).generateArgon2Hash(validRequest); + } + + @Test + public void testGenerateArgon2Hash_Service_WithProvidedSalt() { + Argon2GenerateHashResponseDto responseWithSalt = new Argon2GenerateHashResponseDto(); + responseWithSalt.setHashValue("hashWithProvidedSalt"); + responseWithSalt.setSalt(testSalt); + + when(cryptomanagerService.generateArgon2Hash(requestWithSalt)) + .thenReturn(responseWithSalt); + + Argon2GenerateHashResponseDto response = cryptomanagerService.generateArgon2Hash(requestWithSalt); + + assertNotNull(response); + assertEquals("hashWithProvidedSalt", response.getHashValue()); + assertEquals(testSalt, response.getSalt()); + verify(cryptomanagerService).generateArgon2Hash(requestWithSalt); + } + + @Test(expected = CryptoManagerSerivceException.class) + public void testGenerateArgon2Hash_Service_InvalidInput() { + Argon2GenerateHashRequestDto invalidRequest = new Argon2GenerateHashRequestDto(); + invalidRequest.setInputData(""); + + when(cryptomanagerService.generateArgon2Hash(invalidRequest)) + .thenThrow(new CryptoManagerSerivceException("KER-CRY-001", "Invalid input data")); + + cryptomanagerService.generateArgon2Hash(invalidRequest); + } + + @Test(expected = CryptoManagerSerivceException.class) + public void testGenerateArgon2Hash_Service_NullInput() { + Argon2GenerateHashRequestDto nullRequest = new Argon2GenerateHashRequestDto(); + nullRequest.setInputData(null); + + when(cryptomanagerService.generateArgon2Hash(nullRequest)) + .thenThrow(new CryptoManagerSerivceException("KER-CRY-001", "Input data cannot be null")); + + cryptomanagerService.generateArgon2Hash(nullRequest); + } + + @Test + public void testGenerateArgon2Hash_Service_EmptyInput() { + Argon2GenerateHashRequestDto emptyRequest = new Argon2GenerateHashRequestDto(); + emptyRequest.setInputData(""); + + when(cryptomanagerService.generateArgon2Hash(emptyRequest)) + .thenThrow(new CryptoManagerSerivceException("KER-CRY-001", "Input data cannot be empty")); + + try { + cryptomanagerService.generateArgon2Hash(emptyRequest); + fail("Expected exception was not thrown"); + } catch (CryptoManagerSerivceException e) { + assertEquals("KER-CRY-001", e.getErrorCode()); + } + } + + // DTO Tests - 100% Coverage + @Test + public void testArgon2RequestDto_SettersGetters() { + Argon2GenerateHashRequestDto request = new Argon2GenerateHashRequestDto(); + request.setInputData("testInput"); + request.setSalt("testSalt"); + + assertEquals("testInput", request.getInputData()); + assertEquals("testSalt", request.getSalt()); + assertNotNull(request.toString()); + } + + @Test + public void testArgon2ResponseDto_SettersGetters() { + Argon2GenerateHashResponseDto response = new Argon2GenerateHashResponseDto(); + response.setHashValue("testHash"); + response.setSalt("testSalt"); + + assertEquals("testHash", response.getHashValue()); + assertEquals("testSalt", response.getSalt()); + } + + @Test + public void testArgon2RequestDto_AllArgsConstructor() { + Argon2GenerateHashRequestDto request = new Argon2GenerateHashRequestDto(testInputData, testSalt); + assertEquals(testInputData, request.getInputData()); + assertEquals(testSalt, request.getSalt()); + } + + @Test + public void testArgon2ResponseDto_AllArgsConstructor() { + Argon2GenerateHashResponseDto response = new Argon2GenerateHashResponseDto("hashValue", "saltValue"); + assertEquals("hashValue", response.getHashValue()); + assertEquals("saltValue", response.getSalt()); + } + + @Test + public void testArgon2RequestDto_NoArgsConstructor() { + Argon2GenerateHashRequestDto request = new Argon2GenerateHashRequestDto(); + assertNull(request.getInputData()); + assertNull(request.getSalt()); + } + + @Test + public void testArgon2ResponseDto_NoArgsConstructor() { + Argon2GenerateHashResponseDto response = new Argon2GenerateHashResponseDto(); + assertNull(response.getHashValue()); + assertNull(response.getSalt()); + } + + @Test + public void testArgon2RequestDto_Equals() { + Argon2GenerateHashRequestDto request1 = new Argon2GenerateHashRequestDto(testInputData, testSalt); + Argon2GenerateHashRequestDto request2 = new Argon2GenerateHashRequestDto(testInputData, testSalt); + + assertEquals(request1, request2); + assertEquals(request1.hashCode(), request2.hashCode()); + } + + @Test + public void testArgon2ResponseDto_Equals() { + Argon2GenerateHashResponseDto response1 = new Argon2GenerateHashResponseDto("hash", "salt"); + Argon2GenerateHashResponseDto response2 = new Argon2GenerateHashResponseDto("hash", "salt"); + + assertEquals(response1, response2); + assertEquals(response1.hashCode(), response2.hashCode()); + } + + // Private Method Tests using Reflection - Service Implementation Coverage + @Test + public void testGetLongBytes_PrivateMethod() throws Exception { + CryptomanagerServiceImpl realService = new CryptomanagerServiceImpl(); + long testValue = 12345L; + + Method getLongBytesMethod = CryptomanagerServiceImpl.class.getDeclaredMethod("getLongBytes", long.class); + getLongBytesMethod.setAccessible(true); + + byte[] result = (byte[]) getLongBytesMethod.invoke(realService, testValue); + + assertNotNull(result); + assertEquals(8, result.length); + } + + @Test + public void testGetSaltBytes_PrivateMethod_WithValidKey() throws Exception { + CryptomanagerServiceImpl realService = new CryptomanagerServiceImpl(); + SecretKey testKey = KeyGenerator.getInstance("AES").generateKey(); + byte[] testBytes = "testData".getBytes(); + + Method getSaltBytesMethod = CryptomanagerServiceImpl.class.getDeclaredMethod("getSaltBytes", byte[].class, SecretKey.class); + getSaltBytesMethod.setAccessible(true); + + byte[] result = (byte[]) getSaltBytesMethod.invoke(realService, testBytes, testKey); + + assertNotNull(result); + assertTrue(result.length > 0); + } + + + + // Real Service Implementation Test - Validation Coverage + @Test(expected = CryptoManagerSerivceException.class) + public void testGenerateArgon2Hash_RealService_ValidationError() throws Exception { + CryptomanagerServiceImpl realService = new CryptomanagerServiceImpl(); + CryptomanagerUtils mockUtil = mock(CryptomanagerUtils.class); + + Field utilField = CryptomanagerServiceImpl.class.getDeclaredField("cryptomanagerUtil"); + utilField.setAccessible(true); + utilField.set(realService, mockUtil); + + Argon2GenerateHashRequestDto invalidRequest = new Argon2GenerateHashRequestDto(); + invalidRequest.setInputData(""); + + doThrow(new CryptoManagerSerivceException("KER-CRY-001", "Invalid input")) + .when(mockUtil).validateInputData(""); + + realService.generateArgon2Hash(invalidRequest); + } +} \ No newline at end of file diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/util/KeymanagerSymmetricKeyConverterTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/util/KeymanagerSymmetricKeyConverterTest.java new file mode 100644 index 00000000..0f7cf928 --- /dev/null +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/cryptomanager/test/util/KeymanagerSymmetricKeyConverterTest.java @@ -0,0 +1,98 @@ +package io.mosip.kernel.cryptomanager.test.util; + +import static org.junit.Assert.*; + +import java.time.LocalDateTime; + +import org.junit.Before; +import org.junit.Test; + +import io.mosip.kernel.cryptomanager.dto.CryptomanagerRequestDto; +import io.mosip.kernel.cryptomanager.dto.KeymanagerSymmetricKeyRequestDto; +import io.mosip.kernel.cryptomanager.util.KeymanagerSymmetricKeyConverter; + +public class KeymanagerSymmetricKeyConverterTest { + + private KeymanagerSymmetricKeyConverter converter; + private CryptomanagerRequestDto source; + private KeymanagerSymmetricKeyRequestDto destination; + + @Before + public void setUp() { + converter = new KeymanagerSymmetricKeyConverter(); + + source = new CryptomanagerRequestDto(); + source.setApplicationId("TEST_APP"); + source.setReferenceId("REF_001"); + source.setTimeStamp(LocalDateTime.of(2023, 11, 18, 10, 30, 0)); + source.setData("encryptedSymmetricKeyData"); + + destination = new KeymanagerSymmetricKeyRequestDto(); + } + + @Test + public void testConvert_Success() { + converter.convert(source, destination); + + assertEquals("TEST_APP", destination.getApplicationId()); + assertEquals("REF_001", destination.getReferenceId()); + assertEquals(LocalDateTime.of(2023, 11, 18, 10, 30, 0), destination.getTimeStamp()); + assertEquals("encryptedSymmetricKeyData", destination.getEncryptedSymmetricKey()); + } + + @Test + public void testConvert_WithNullValues() { + CryptomanagerRequestDto nullSource = new CryptomanagerRequestDto(); + nullSource.setApplicationId(null); + nullSource.setReferenceId(null); + nullSource.setTimeStamp(null); + nullSource.setData(null); + + converter.convert(nullSource, destination); + + assertNull(destination.getApplicationId()); + assertNull(destination.getReferenceId()); + assertNull(destination.getTimeStamp()); + assertNull(destination.getEncryptedSymmetricKey()); + } + + @Test + public void testConvert_WithEmptyStrings() { + CryptomanagerRequestDto emptySource = new CryptomanagerRequestDto(); + emptySource.setApplicationId(""); + emptySource.setReferenceId(""); + emptySource.setData(""); + emptySource.setTimeStamp(LocalDateTime.now()); + + converter.convert(emptySource, destination); + + assertEquals("", destination.getApplicationId()); + assertEquals("", destination.getReferenceId()); + assertEquals("", destination.getEncryptedSymmetricKey()); + assertNotNull(destination.getTimeStamp()); + } + + @Test + public void testConvert_PreservesExistingDestinationValues() { + destination.setApplicationId("OLD_APP"); + destination.setReferenceId("OLD_REF"); + + converter.convert(source, destination); + + assertEquals("TEST_APP", destination.getApplicationId()); + assertEquals("REF_001", destination.getReferenceId()); + } + + @Test + public void testConvert_WithSpecialCharacters() { + source.setApplicationId("APP@123"); + source.setReferenceId("REF#456"); + source.setData("data$with%special&chars"); + + converter.convert(source, destination); + + assertEquals("APP@123", destination.getApplicationId()); + assertEquals("REF#456", destination.getReferenceId()); + assertEquals("data$with%special&chars", destination.getEncryptedSymmetricKey()); + } +} \ No newline at end of file diff --git a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/keygenerator/bouncycastle/test/KeyGeneratorTest.java b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/keygenerator/bouncycastle/test/KeyGeneratorTest.java index d342553c..3e22ba04 100644 --- a/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/keygenerator/bouncycastle/test/KeyGeneratorTest.java +++ b/kernel/kernel-keymanager-service/src/test/java/io/mosip/kernel/keygenerator/bouncycastle/test/KeyGeneratorTest.java @@ -1,12 +1,20 @@ package io.mosip.kernel.keygenerator.bouncycastle.test; import static org.hamcrest.CoreMatchers.isA; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.SecureRandom; import javax.crypto.SecretKey; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -22,31 +30,81 @@ @RunWith(SpringRunner.class) public class KeyGeneratorTest { - @Autowired - KeyGenerator keyGenerator; + @Autowired + KeyGenerator keyGenerator; - @MockBean - private ECKeyStore keyStore; + @MockBean + private ECKeyStore keyStore; - @Test - public void testGetSymmetricKey() { - assertThat(keyGenerator.getSymmetricKey(), isA(SecretKey.class)); - } + @Before + public void init() { + ReflectionTestUtils.setField(keyGenerator, "secureRandom", null); + } + + @Test + public void testGetSymmetricKey() { + assertThat(keyGenerator.getSymmetricKey(), isA(SecretKey.class)); + } + + @Test + public void testGetAsymmetricKey() { + assertThat(keyGenerator.getAsymmetricKey(), isA(KeyPair.class)); + } + + @Test + public void getSymmetricKeyTest() { + SecretKey key = keyGenerator.getSymmetricKey(); + assertThat(key, isA(SecretKey.class)); + assertNotNull(key.getEncoded()); + assertTrue(key.getEncoded().length > 0); + } + + @Test + public void getAsymmetricKeyTest() { + KeyPair keyPair = keyGenerator.getAsymmetricKey(); + assertThat(keyPair, isA(KeyPair.class)); + assertNotNull(keyPair.getPublic()); + assertNotNull(keyPair.getPrivate()); + } - @Test - public void testGetAsymmetricKey() { - assertThat(keyGenerator.getAsymmetricKey(), isA(KeyPair.class)); - } + @Test + public void testBuildPrivateKey() { + KeyPair keyPair = keyGenerator.getEd25519KeyPair(); + byte[] privateKeyData = keyPair.getPrivate().getEncoded(); + PrivateKey rebuiltKey = keyGenerator.buildPrivateKey(privateKeyData); + assertNotNull(rebuiltKey); + } @Test - public void testGetSecureRandom() { - ReflectionTestUtils.setField(keyGenerator, "rngProviderName", "PKCS11"); + public void testSecureRandomCaching() { + ReflectionTestUtils.setField(keyGenerator, "secureRandom", new SecureRandom()); + SecureRandom result = (SecureRandom) ReflectionTestUtils.invokeMethod(keyGenerator, "getSecureRandom"); + assertNotNull(result); + } + + @Test + public void testSecureRandomRngDisabled() { + ReflectionTestUtils.setField(keyGenerator, "rngProviderEnabled", false); + SecureRandom result = (SecureRandom) ReflectionTestUtils.invokeMethod(keyGenerator, "getSecureRandom"); + assertNotNull(result); + } + + @Test + public void testSecureRandomRngEnabled() { + when(keyStore.getKeystoreProviderName()).thenReturn("SUN"); + ReflectionTestUtils.setField(keyGenerator, "rngProviderEnabled", true); + ReflectionTestUtils.setField(keyGenerator, "rngProviderName", "SHA1PRNG"); + SecureRandom result = (SecureRandom) ReflectionTestUtils.invokeMethod(keyGenerator, "getSecureRandom"); + assertNotNull(result); + } + + @Test + public void testSecureRandomFallback() { + when(keyStore.getKeystoreProviderName()).thenReturn("SUN"); ReflectionTestUtils.setField(keyGenerator, "rngProviderEnabled", true); - try { - assertThat(keyGenerator.getSymmetricKey(), isA(SecretKey.class)); - } catch (Exception e) { - ReflectionTestUtils.setField(keyGenerator, "rngProviderEnabled", false); - assertThat(keyGenerator.getSymmetricKey(), isA(SecretKey.class)); - } + ReflectionTestUtils.setField(keyGenerator, "rngProviderName", "INVALID_PROVIDER"); + SecureRandom result = (SecureRandom) ReflectionTestUtils.invokeMethod(keyGenerator, "getSecureRandom"); + assertNotNull(result); } + }