diff --git a/iam-login-service/src/main/java/it/infn/mw/iam/util/mfa/IamTotpMfaEncryptionAndDecryptionUtil.java b/iam-login-service/src/main/java/it/infn/mw/iam/util/mfa/IamTotpMfaEncryptionAndDecryptionUtil.java index 2c9b00730..c6b1fb127 100644 --- a/iam-login-service/src/main/java/it/infn/mw/iam/util/mfa/IamTotpMfaEncryptionAndDecryptionUtil.java +++ b/iam-login-service/src/main/java/it/infn/mw/iam/util/mfa/IamTotpMfaEncryptionAndDecryptionUtil.java @@ -37,15 +37,15 @@ public class IamTotpMfaEncryptionAndDecryptionUtil { .getInstance(); private IamTotpMfaEncryptionAndDecryptionUtil() { - // Prevent instantiation } + /** * This process requires a password for encrypting the plaintext. Ensure to use * the same password for decryption as well. * - * @param input plaintext to encrypt. - * @param password Provided by the admin through the environment - * variable. + * @param input plaintext to encrypt. + * @param password Provided by the admin through the environment + * variable. * * @return String If encryption is successful, the cipherText would be returned. * @@ -85,8 +85,8 @@ public static String encryptSecretOrRecoveryCode(String input, String password) * during encryption. * * @param cipherText Encrypted data which help us to extract the plaintext. - * @param password Provided by the admin through the environment - * variable. + * @param password Provided by the admin through the environment + * variable. * * @return String Returns plainText which we obtained from the cipherText. * diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/account/multi_factor_authentication/authenticator_app/AuthenticatorAppSettingsControllerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/account/multi_factor_authentication/authenticator_app/AuthenticatorAppSettingsControllerTests.java index fcd1b8ee7..df483aa18 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/api/account/multi_factor_authentication/authenticator_app/AuthenticatorAppSettingsControllerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/api/account/multi_factor_authentication/authenticator_app/AuthenticatorAppSettingsControllerTests.java @@ -43,6 +43,7 @@ import org.springframework.web.context.WebApplicationContext; import it.infn.mw.iam.api.account.multi_factor_authentication.IamTotpMfaService; +import it.infn.mw.iam.config.mfa.IamTotpMfaProperties; import it.infn.mw.iam.persistence.model.IamAccount; import it.infn.mw.iam.persistence.model.IamTotpMfa; import it.infn.mw.iam.persistence.repository.IamAccountRepository; @@ -52,6 +53,7 @@ import it.infn.mw.iam.test.util.WithMockMfaUser; import it.infn.mw.iam.test.util.WithMockPreAuthenticatedUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; @RunWith(SpringRunner.class) @IamMockMvcIntegrationTest @@ -68,6 +70,9 @@ public class AuthenticatorAppSettingsControllerTests extends MultiFactorTestSupp @MockBean private IamTotpMfaService totpMfaService; + @MockBean + private IamTotpMfaProperties iamTotpMfaProperties; + @BeforeClass public static void init() { TestUtils.initRestAssured(); @@ -77,6 +82,7 @@ public static void init() { public void setup() { when(accountRepository.findByUsername(TEST_USERNAME)).thenReturn(Optional.of(TEST_ACCOUNT)); when(accountRepository.findByUsername(TOTP_USERNAME)).thenReturn(Optional.of(TOTP_MFA_ACCOUNT)); + when(iamTotpMfaProperties.getPasswordToEncryptOrDecrypt()).thenReturn("define_me_please"); mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).alwaysDo(log()).build(); @@ -89,7 +95,9 @@ public void testAddSecret() throws Exception { IamTotpMfa totpMfa = cloneTotpMfa(TOTP_MFA); totpMfa.setActive(false); totpMfa.setAccount(null); - totpMfa.setSecret("secret"); + totpMfa.setSecret( + IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode("secret", + iamTotpMfaProperties.getPasswordToEncryptOrDecrypt())); when(totpMfaService.addTotpMfaSecret(account)).thenReturn(totpMfa); mvc.perform(put(ADD_SECRET_URL)).andExpect(status().isOk()); @@ -119,7 +127,9 @@ public void testEnableAuthenticatorApp() throws Exception { IamTotpMfa totpMfa = cloneTotpMfa(TOTP_MFA); totpMfa.setActive(true); totpMfa.setAccount(account); - totpMfa.setSecret("secret"); + totpMfa.setSecret( + IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode("secret", + iamTotpMfaProperties.getPasswordToEncryptOrDecrypt())); String totp = "123456"; when(totpMfaService.verifyTotp(account, totp)).thenReturn(true); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaCommons.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaCommons.java new file mode 100644 index 000000000..c9b56c766 --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaCommons.java @@ -0,0 +1,6 @@ +package it.infn.mw.iam.test.multi_factor_authentication; + +public class IamTotpMfaCommons { + public static final String DEFAULT_KEY = "define_me_please"; + public static final String TOTP_MFA_SECRET = "secret"; +} \ No newline at end of file diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaEncryptionAndDecryptionUtilTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaEncryptionAndDecryptionUtilTests.java new file mode 100644 index 000000000..d32f6f10c --- /dev/null +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaEncryptionAndDecryptionUtilTests.java @@ -0,0 +1,71 @@ +package it.infn.mw.iam.test.multi_factor_authentication; + +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; +import it.infn.mw.iam.util.mfa.IamTotpMfaInvalidArgumentError; + +@RunWith(MockitoJUnitRunner.class) +public class IamTotpMfaEncryptionAndDecryptionUtilTests extends IamTotpMfaCommons { + + @Test + public void testEncryptSecretOrRecoveryCode() throws IamTotpMfaInvalidArgumentError { + // Encrypt the plainText + String cipherText = IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode(TOTP_MFA_SECRET, DEFAULT_KEY); + + // Decrypt the cipherText + String plainText = IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode(cipherText, DEFAULT_KEY); + + assertEquals(TOTP_MFA_SECRET, plainText); + } + + @Test + public void testEncryptSecretOrRecoveryCodeWithDifferentKey() throws IamTotpMfaInvalidArgumentError { + // Encrypt the plainText + String cipherText = IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode(TOTP_MFA_SECRET, DEFAULT_KEY); + + IamTotpMfaInvalidArgumentError thrownException = assertThrows(IamTotpMfaInvalidArgumentError.class, () -> { + // Decrypt the cipherText with a different key + IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode(cipherText, "NOT_THE_SAME_KEY"); + }); + + assertTrue(thrownException.getMessage().startsWith("Please use the same password")); + + // Decrypt the cipherText with a the key used for encryption. + String plainText = IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode(cipherText, DEFAULT_KEY); + + assertEquals(TOTP_MFA_SECRET, plainText); + } + + @Test + public void testEncryptSecretOrRecoveryCodeWithTamperedCipher() throws IamTotpMfaInvalidArgumentError { + // Encrypt the plainText + String cipherText = IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode(TOTP_MFA_SECRET, DEFAULT_KEY); + + String modifyCipher = cipherText.substring(3); + String tamperedCipher = "iam" + modifyCipher; + + if (!tamperedCipher.substring(0, 3).equals(cipherText.substring(0, 3))) { + + IamTotpMfaInvalidArgumentError thrownException = assertThrows(IamTotpMfaInvalidArgumentError.class, () -> { + // Decrypt the cipherText with a different key + IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode(tamperedCipher, DEFAULT_KEY); + }); + + // Always throws an error because we have tampered with cipherText. + assertTrue(thrownException.getMessage().startsWith("Please use the same password")); + } else { + + // Decrypt the cipherText with a the key used for encryption. + String plainText = IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode(cipherText, DEFAULT_KEY); + + assertEquals(TOTP_MFA_SECRET, plainText); + } + } +} diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTestSupport.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTestSupport.java index 060c3c444..29eb936e8 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTestSupport.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTestSupport.java @@ -23,10 +23,12 @@ import it.infn.mw.iam.persistence.model.IamAuthority; import it.infn.mw.iam.persistence.model.IamTotpMfa; import it.infn.mw.iam.persistence.model.IamTotpRecoveryCode; +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; public class IamTotpMfaServiceTestSupport { public static final String PASSWORD = "password"; + public static final String DEFAULT_KEY = "define_me_please"; public static final String TOTP_MFA_ACCOUNT_UUID = "b3e7dd7f-a1ac-eda0-371d-b902a6c5cee2"; public static final String TOTP_MFA_ACCOUNT_USERNAME = "totp"; @@ -73,7 +75,7 @@ public IamTotpMfaServiceTestSupport() { TOTP_MFA = new IamTotpMfa(); TOTP_MFA.setAccount(TOTP_MFA_ACCOUNT); - TOTP_MFA.setSecret(TOTP_MFA_SECRET); + TOTP_MFA.setSecret(getEncryptedCode(TOTP_MFA_SECRET, DEFAULT_KEY)); TOTP_MFA.setActive(true); TOTP_RECOVERY_CODE_1 = new IamTotpRecoveryCode(TOTP_MFA); @@ -83,12 +85,12 @@ public IamTotpMfaServiceTestSupport() { TOTP_RECOVERY_CODE_5 = new IamTotpRecoveryCode(TOTP_MFA); TOTP_RECOVERY_CODE_6 = new IamTotpRecoveryCode(TOTP_MFA); - TOTP_RECOVERY_CODE_1.setCode(TOTP_RECOVERY_CODE_STRING_1); - TOTP_RECOVERY_CODE_2.setCode(TOTP_RECOVERY_CODE_STRING_2); - TOTP_RECOVERY_CODE_3.setCode(TOTP_RECOVERY_CODE_STRING_3); - TOTP_RECOVERY_CODE_4.setCode(TOTP_RECOVERY_CODE_STRING_4); - TOTP_RECOVERY_CODE_5.setCode(TOTP_RECOVERY_CODE_STRING_5); - TOTP_RECOVERY_CODE_6.setCode(TOTP_RECOVERY_CODE_STRING_6); + TOTP_RECOVERY_CODE_1.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_1, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_2.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_2, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_3.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_3, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_4.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_4, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_5.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_5, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_6.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_6, DEFAULT_KEY)); TOTP_MFA .setRecoveryCodes(new HashSet<>(Arrays.asList(TOTP_RECOVERY_CODE_1, TOTP_RECOVERY_CODE_2, @@ -128,4 +130,8 @@ public IamTotpMfa cloneTotpMfa(IamTotpMfa totpMfa) { return newTotpMfa; } + + public String getEncryptedCode(String plaintext, String key) { + return IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode(plaintext, key); + } } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTests.java index 2ce503607..3f473621a 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/IamTotpMfaServiceTests.java @@ -50,6 +50,7 @@ import it.infn.mw.iam.api.account.multi_factor_authentication.IamTotpMfaService; import it.infn.mw.iam.audit.events.account.multi_factor_authentication.AuthenticatorAppDisabledEvent; import it.infn.mw.iam.audit.events.account.multi_factor_authentication.AuthenticatorAppEnabledEvent; +import it.infn.mw.iam.config.mfa.IamTotpMfaProperties; import it.infn.mw.iam.core.user.IamAccountService; import it.infn.mw.iam.core.user.exception.MfaSecretAlreadyBoundException; import it.infn.mw.iam.core.user.exception.MfaSecretNotFoundException; @@ -58,6 +59,7 @@ import it.infn.mw.iam.persistence.model.IamTotpMfa; import it.infn.mw.iam.persistence.model.IamTotpRecoveryCode; import it.infn.mw.iam.persistence.repository.IamTotpMfaRepository; +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; @RunWith(MockitoJUnitRunner.class) public class IamTotpMfaServiceTests extends IamTotpMfaServiceTestSupport { @@ -82,11 +84,16 @@ public class IamTotpMfaServiceTests extends IamTotpMfaServiceTestSupport { @Mock private ApplicationEventPublisher eventPublisher; + @Mock + private IamTotpMfaProperties iamTotpMfaProperties; + @Captor private ArgumentCaptor eventCaptor; @Before public void setup() { + when(iamTotpMfaProperties.getPasswordToEncryptOrDecrypt()).thenReturn("define_me_please"); + when(secretGenerator.generate()).thenReturn("test_secret"); when(repository.findByAccount(TOTP_MFA_ACCOUNT)).thenReturn(Optional.of(TOTP_MFA)); when(iamAccountService.saveAccount(TOTP_MFA_ACCOUNT)).thenAnswer(i -> i.getArguments()[0]); @@ -98,7 +105,7 @@ public void setup() { when(recoveryCodeGenerator.generateCodes(anyInt())).thenReturn(testArray); service = new DefaultIamTotpMfaService(iamAccountService, repository, secretGenerator, - recoveryCodeGenerator, codeVerifier, eventPublisher); + recoveryCodeGenerator, codeVerifier, eventPublisher, iamTotpMfaProperties); } @After @@ -166,10 +173,12 @@ public void testAddsMfaRecoveryCode_whenNoMfaSecretAssignedFails() { } @Test - public void testEnablesTotpMfa() { + public void testEnablesTotpMfa() throws Exception { IamAccount account = cloneAccount(TOTP_MFA_ACCOUNT); IamTotpMfa totpMfa = cloneTotpMfa(TOTP_MFA); - totpMfa.setSecret("secret"); + totpMfa.setSecret( + IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode( + "secret", iamTotpMfaProperties.getPasswordToEncryptOrDecrypt())); totpMfa.setActive(false); totpMfa.setAccount(account); diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/MultiFactorTestSupport.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/MultiFactorTestSupport.java index 8550a82d5..307ca0d35 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/MultiFactorTestSupport.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/MultiFactorTestSupport.java @@ -22,8 +22,9 @@ import it.infn.mw.iam.persistence.model.IamAccount; import it.infn.mw.iam.persistence.model.IamTotpMfa; import it.infn.mw.iam.persistence.model.IamTotpRecoveryCode; +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; -public class MultiFactorTestSupport { +public class MultiFactorTestSupport extends IamTotpMfaCommons{ public static final String TEST_USERNAME = "test-user"; public static final String TEST_UUID = "a23deabf-88a7-47af-84b5-1d535a1b267c"; public static final String TEST_EMAIL = "test@example.org"; @@ -34,7 +35,6 @@ public class MultiFactorTestSupport { public static final String TOTP_EMAIL = "test-mfa@example.org"; public static final String TOTP_GIVEN_NAME = "Test"; public static final String TOTP_FAMILY_NAME = "Mfa"; - public static final String TOTP_MFA_SECRET = "secret"; public static final String TOTP_RECOVERY_CODE_STRING_1 = "code-1"; public static final String TOTP_RECOVERY_CODE_STRING_2 = "code-2"; public static final String TOTP_RECOVERY_CODE_STRING_3 = "code-3"; @@ -88,7 +88,9 @@ public MultiFactorTestSupport() { TOTP_MFA = new IamTotpMfa(); TOTP_MFA.setAccount(TOTP_MFA_ACCOUNT); - TOTP_MFA.setSecret(TOTP_MFA_SECRET); + TOTP_MFA.setSecret( + IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode( + TOTP_MFA_SECRET, DEFAULT_KEY)); TOTP_MFA.setActive(true); TOTP_MFA.touch(); @@ -105,18 +107,18 @@ public MultiFactorTestSupport() { TOTP_RECOVERY_CODE_11 = new IamTotpRecoveryCode(TOTP_MFA); TOTP_RECOVERY_CODE_12 = new IamTotpRecoveryCode(TOTP_MFA); - TOTP_RECOVERY_CODE_1.setCode(TOTP_RECOVERY_CODE_STRING_1); - TOTP_RECOVERY_CODE_2.setCode(TOTP_RECOVERY_CODE_STRING_2); - TOTP_RECOVERY_CODE_3.setCode(TOTP_RECOVERY_CODE_STRING_3); - TOTP_RECOVERY_CODE_4.setCode(TOTP_RECOVERY_CODE_STRING_4); - TOTP_RECOVERY_CODE_5.setCode(TOTP_RECOVERY_CODE_STRING_5); - TOTP_RECOVERY_CODE_6.setCode(TOTP_RECOVERY_CODE_STRING_6); - TOTP_RECOVERY_CODE_7.setCode(TOTP_RECOVERY_CODE_STRING_7); - TOTP_RECOVERY_CODE_8.setCode(TOTP_RECOVERY_CODE_STRING_8); - TOTP_RECOVERY_CODE_9.setCode(TOTP_RECOVERY_CODE_STRING_9); - TOTP_RECOVERY_CODE_10.setCode(TOTP_RECOVERY_CODE_STRING_10); - TOTP_RECOVERY_CODE_11.setCode(TOTP_RECOVERY_CODE_STRING_11); - TOTP_RECOVERY_CODE_12.setCode(TOTP_RECOVERY_CODE_STRING_12); + TOTP_RECOVERY_CODE_1.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_1, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_2.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_2, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_3.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_3, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_4.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_4, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_5.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_5, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_6.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_6, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_7.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_7, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_8.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_8, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_9.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_9, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_10.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_10, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_11.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_11, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_12.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_12, DEFAULT_KEY)); RECOVERY_CODE_SET_FIRST = new HashSet<>( Arrays.asList(TOTP_RECOVERY_CODE_1, TOTP_RECOVERY_CODE_2, TOTP_RECOVERY_CODE_3, @@ -148,22 +150,24 @@ protected void resetTotpAccount() { TOTP_MFA_ACCOUNT.touch(); TOTP_MFA.setAccount(TOTP_MFA_ACCOUNT); - TOTP_MFA.setSecret(TOTP_MFA_SECRET); + TOTP_MFA.setSecret( + IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode( + TOTP_MFA_SECRET, DEFAULT_KEY)); TOTP_MFA.setActive(true); TOTP_MFA.touch(); - TOTP_RECOVERY_CODE_1.setCode(TOTP_RECOVERY_CODE_STRING_1); - TOTP_RECOVERY_CODE_2.setCode(TOTP_RECOVERY_CODE_STRING_2); - TOTP_RECOVERY_CODE_3.setCode(TOTP_RECOVERY_CODE_STRING_3); - TOTP_RECOVERY_CODE_4.setCode(TOTP_RECOVERY_CODE_STRING_4); - TOTP_RECOVERY_CODE_5.setCode(TOTP_RECOVERY_CODE_STRING_5); - TOTP_RECOVERY_CODE_6.setCode(TOTP_RECOVERY_CODE_STRING_6); - TOTP_RECOVERY_CODE_7.setCode(TOTP_RECOVERY_CODE_STRING_7); - TOTP_RECOVERY_CODE_8.setCode(TOTP_RECOVERY_CODE_STRING_8); - TOTP_RECOVERY_CODE_9.setCode(TOTP_RECOVERY_CODE_STRING_9); - TOTP_RECOVERY_CODE_10.setCode(TOTP_RECOVERY_CODE_STRING_10); - TOTP_RECOVERY_CODE_11.setCode(TOTP_RECOVERY_CODE_STRING_11); - TOTP_RECOVERY_CODE_12.setCode(TOTP_RECOVERY_CODE_STRING_12); + TOTP_RECOVERY_CODE_1.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_1, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_2.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_2, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_3.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_3, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_4.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_4, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_5.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_5, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_6.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_6, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_7.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_7, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_8.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_8, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_9.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_9, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_10.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_10, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_11.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_11, DEFAULT_KEY)); + TOTP_RECOVERY_CODE_12.setCode(getEncryptedCode(TOTP_RECOVERY_CODE_STRING_12, DEFAULT_KEY)); TOTP_MFA.setRecoveryCodes(RECOVERY_CODE_SET_FIRST); } @@ -199,4 +203,8 @@ protected IamTotpMfa cloneTotpMfa(IamTotpMfa totpMfa) { return newTotpMfa; } + + public String getEncryptedCode(String plaintext, String key) { + return IamTotpMfaEncryptionAndDecryptionUtil.encryptSecretOrRecoveryCode(plaintext, key); + } } diff --git a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/authenticator_app/RecoveryCodeManagementControllerTests.java b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/authenticator_app/RecoveryCodeManagementControllerTests.java index 7d03ee299..4b8601b2d 100644 --- a/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/authenticator_app/RecoveryCodeManagementControllerTests.java +++ b/iam-login-service/src/test/java/it/infn/mw/iam/test/multi_factor_authentication/authenticator_app/RecoveryCodeManagementControllerTests.java @@ -55,6 +55,7 @@ import dev.samstevens.totp.recovery.RecoveryCodeGenerator; import it.infn.mw.iam.api.account.AccountUtils; import it.infn.mw.iam.api.account.multi_factor_authentication.IamTotpRecoveryCodeResetService; +import it.infn.mw.iam.config.mfa.IamTotpMfaProperties; import it.infn.mw.iam.persistence.model.IamTotpRecoveryCode; import it.infn.mw.iam.persistence.repository.IamTotpMfaRepository; import it.infn.mw.iam.test.multi_factor_authentication.MultiFactorTestSupport; @@ -62,6 +63,7 @@ import it.infn.mw.iam.test.util.WithMockMfaUser; import it.infn.mw.iam.test.util.WithMockPreAuthenticatedUser; import it.infn.mw.iam.test.util.annotation.IamMockMvcIntegrationTest; +import it.infn.mw.iam.util.mfa.IamTotpMfaEncryptionAndDecryptionUtil; @RunWith(SpringRunner.class) @IamMockMvcIntegrationTest @@ -87,6 +89,9 @@ public class RecoveryCodeManagementControllerTests extends MultiFactorTestSuppor @MockBean private RecoveryCodeGenerator generator; + @MockBean + private IamTotpMfaProperties iamTotpMfaProperties; + @Captor private ArgumentCaptor authEventCaptor; @@ -98,6 +103,7 @@ public void setup() { when(accountUtils.getAuthenticatedUserAccount()).thenReturn(Optional.of(TOTP_MFA_ACCOUNT)); when(totpMfaRepository.findByAccount(TOTP_MFA_ACCOUNT)).thenReturn(Optional.of(TOTP_MFA)); when(service.resetRecoveryCodes(TOTP_MFA_ACCOUNT)).thenAnswer(i -> i.getArguments()[0]); + when(iamTotpMfaProperties.getPasswordToEncryptOrDecrypt()).thenReturn(DEFAULT_KEY); mvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).alwaysDo(log()).build(); @@ -185,7 +191,8 @@ public void testGetRecoveryCodes() throws Exception { String[] originalCodes = new String[RECOVERY_CODE_SET_FIRST.size()]; List recoveryCodes = new ArrayList<>(RECOVERY_CODE_SET_FIRST); for (int i = 0; i < recoveryCodes.size(); i++) { - originalCodes[i] = recoveryCodes.get(i).getCode(); + originalCodes[i] = IamTotpMfaEncryptionAndDecryptionUtil.decryptSecretOrRecoveryCode( + recoveryCodes.get(i).getCode(), DEFAULT_KEY); // This is here because the string.split() method adds backslashed quotes around the separated // strings. So this is a hacky method to remove them to allow for the comparison to succeed.