diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 933873bc5e..9af728589c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -50,6 +50,7 @@ import info.nightscout.androidaps.queue.commands.* import info.nightscout.androidaps.setupwizard.SWEventListener import info.nightscout.androidaps.setupwizard.SWScreen import info.nightscout.androidaps.setupwizard.elements.* +import info.nightscout.androidaps.utils.CryptoUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelperImplementation @@ -261,6 +262,7 @@ open class AppModule { @ContributesAndroidInjector fun graphDataInjector(): GraphData + @ContributesAndroidInjector fun cryptoUtilInjector(): CryptoUtil @ContributesAndroidInjector fun importExportPrefsInjector(): ImportExportPrefs @ContributesAndroidInjector fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat @ContributesAndroidInjector fun classicPrefsFormatInjector(): ClassicPrefsFormat diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt index cec2ae9b5f..463b5a31cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt @@ -18,6 +18,7 @@ import javax.inject.Singleton @Singleton class EncryptedPrefsFormat @Inject constructor( private var resourceHelper: ResourceHelper, + private var cryptoUtil: CryptoUtil, private var storage: Storage ) : PrefsFormat { @@ -58,14 +59,14 @@ class EncryptedPrefsFormat @Inject constructor( var encodedContent = "" if (encrypted) { - val salt = CryptoUtil.mineSalt() + val salt = cryptoUtil.mineSalt() val rawContent = content.toString() - val contentAttempt = CryptoUtil.encrypt(masterPassword!!, salt, rawContent) + val contentAttempt = cryptoUtil.encrypt(masterPassword!!, salt, rawContent) if (contentAttempt != null) { encodedContent = contentAttempt security.put("algorithm", "v1") security.put("salt", salt.toHex()) - security.put("content_hash", CryptoUtil.sha256(rawContent)) + security.put("content_hash", cryptoUtil.sha256(rawContent)) } else { // fallback when encryption does not work encrypted = false @@ -80,7 +81,7 @@ class EncryptedPrefsFormat @Inject constructor( container.put("content", if (encrypted) encodedContent else content) var fileContents = container.toString(2) - val fileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + val fileHash = cryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) fileContents = fileContents.replace(Regex("(\\\"file_hash\\\"\\s*\\:\\s*\\\")(--to-be-calculated--)(\\\")"), "$1" + fileHash + "$3") @@ -102,7 +103,7 @@ class EncryptedPrefsFormat @Inject constructor( val jsonBody = storage.getFileContents(file) val fileContents = jsonBody.replace(Regex("(?is)(\\\"file_hash\\\"\\s*\\:\\s*\\\")([^\"]*)(\\\")"), "$1--to-be-calculated--$3") - val calculatedFileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + val calculatedFileHash = cryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) val container = JSONObject(jsonBody) if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { @@ -144,11 +145,11 @@ class EncryptedPrefsFormat @Inject constructor( if (security.has("salt") && security.has("content_hash")) { val salt = security.getString("salt").hexStringToByteArray() - val decrypted = CryptoUtil.decrypt(masterPassword!!, salt, container.getString("content")) + val decrypted = cryptoUtil.decrypt(masterPassword!!, salt, container.getString("content")) if (decrypted != null) { try { - val contentHash = CryptoUtil.sha256(decrypted) + val contentHash = cryptoUtil.sha256(decrypted) if (contentHash == security.getString("content_hash")) { contentJsonObj = JSONObject(decrypted) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt index 745f4449ee..01cc925942 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.utils +import info.nightscout.androidaps.logging.AAPSLogger import org.spongycastle.util.encoders.Base64 import java.nio.ByteBuffer import java.security.MessageDigest @@ -12,6 +13,8 @@ import javax.crypto.SecretKeyFactory import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.SecretKeySpec +import javax.inject.Inject +import javax.inject.Singleton private val HEX_CHARS = "0123456789abcdef" private val HEX_CHARS_ARRAY = "0123456789abcdef".toCharArray() @@ -45,15 +48,21 @@ fun ByteArray.toHex() : String{ return result.toString() } -object CryptoUtil { +@Singleton +class CryptoUtil @Inject constructor( + val aapsLogger: AAPSLogger +) { - private const val IV_LENGTH_BYTE = 12 - private const val TAG_LENGTH_BIT = 128 - private const val AES_KEY_SIZE_BIT = 256 - private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device - private const val SALT_SIZE_BYTE = 32 + companion object { + private const val IV_LENGTH_BYTE = 12 + private const val TAG_LENGTH_BIT = 128 + private const val AES_KEY_SIZE_BIT = 256 + private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device + private const val SALT_SIZE_BYTE = 32 + } private val secureRandom: SecureRandom = SecureRandom() + var lastException: Exception? = null fun sha256(source: String): String { val digest = MessageDigest.getInstance("SHA-256") @@ -85,6 +94,7 @@ object CryptoUtil { val iv: ByteArray? val encrypted: ByteArray? return try { + lastException = null iv = ByteArray(IV_LENGTH_BYTE) secureRandom.nextBytes(iv) val cipherEnc: Cipher = Cipher.getInstance("AES/GCM/NoPadding") @@ -96,7 +106,9 @@ object CryptoUtil { byteBuffer.put(encrypted) String(Base64.encode(byteBuffer.array())) } catch (e: Exception) { - null + lastException = e + aapsLogger.error("Encryption failed due to technical exception: ${e}") + null } } @@ -104,6 +116,7 @@ object CryptoUtil { val iv: ByteArray? val encrypted: ByteArray? return try { + lastException = null val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData)) val ivLength = byteBuffer.get().toInt() iv = ByteArray(ivLength) @@ -115,6 +128,8 @@ object CryptoUtil { val dec = cipherDec.doFinal(encrypted) String(dec) } catch (e: Exception) { + lastException = e + aapsLogger.error("Decryption failed due to technical exception: ${e}") null } } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt index 1907ac01b5..2df55c9f2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt @@ -19,7 +19,10 @@ import javax.inject.Singleton val AUTOFILL_HINT_NEW_PASSWORD = "newPassword" @Singleton -class PasswordCheck @Inject constructor(val sp: SP) { +class PasswordCheck @Inject constructor( + val sp: SP, + val cryptoUtil: CryptoUtil +) { @SuppressLint("InflateParams") fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)?, cancel: (()->Unit)? = null, fail: (()->Unit)? = null) { @@ -45,7 +48,7 @@ class PasswordCheck @Inject constructor(val sp: SP) { .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key)) .setPositiveButton(context.getString(R.string.ok)) { _, _ -> val enteredPassword = userInput.text.toString() - if (CryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword) + if (cryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword) else { ToastUtils.errorToast(context, context.getString(R.string.wrongpassword)) fail?.invoke() @@ -80,7 +83,7 @@ class PasswordCheck @Inject constructor(val sp: SP) { .setPositiveButton(context.getString(R.string.ok)) { _, _ -> val enteredPassword = userInput.text.toString() if (enteredPassword.isNotEmpty()) { - sp.putString(preference, CryptoUtil.hashPassword(enteredPassword)) + sp.putString(preference, cryptoUtil.hashPassword(enteredPassword)) ToastUtils.okToast(context, context.getString(R.string.password_set)) ok?.invoke(enteredPassword) } else { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt index 16c2f4b0ef..4537783ce4 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt @@ -5,10 +5,11 @@ import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.plugins.general.maintenance.formats.* import info.nightscout.androidaps.testing.mockers.AAPSMocker import info.nightscout.androidaps.testing.utils.SingleStringStorage +import info.nightscout.androidaps.utils.CryptoUtil +import info.nightscout.androidaps.utils.assumeAES256isSupported import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.hamcrest.CoreMatchers -import org.json.JSONException import org.junit.Assert import org.junit.Before import org.junit.Test @@ -30,6 +31,8 @@ class EncryptedPrefsFormatTest : TestBase() { @Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var sp: SP + var cryptoUtil: CryptoUtil = CryptoUtil(aapsLogger) + @Before fun mock() { AAPSMocker.prepareMock() @@ -52,9 +55,11 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") + assumeAES256isSupported(cryptoUtil) + Assert.assertThat(prefs.values.size, CoreMatchers.`is`(2)) Assert.assertThat(prefs.values["key1"], CoreMatchers.`is`("A")) Assert.assertThat(prefs.values["keyB"], CoreMatchers.`is`("2")) @@ -67,7 +72,7 @@ class EncryptedPrefsFormatTest : TestBase() { @Test fun preferenceSavingTest() { val storage = SingleStringStorage("") - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = Prefs( mapOf( "key1" to "A", @@ -84,7 +89,7 @@ class EncryptedPrefsFormatTest : TestBase() { @Test fun importExportStabilityTest() { val storage = SingleStringStorage("") - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefsIn = Prefs( mapOf( "testpref1" to "--1--", @@ -97,6 +102,8 @@ class EncryptedPrefsFormatTest : TestBase() { encryptedFormat.savePreferences(AAPSMocker.getMockedFile(), prefsIn, "tajemnica") val prefsOut = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "tajemnica") + assumeAES256isSupported(cryptoUtil) + Assert.assertThat(prefsOut.values.size, CoreMatchers.`is`(2)) Assert.assertThat(prefsOut.values["testpref1"], CoreMatchers.`is`("--1--")) Assert.assertThat(prefsOut.values["testpref2"], CoreMatchers.`is`("another")) @@ -121,7 +128,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "it-is-NOT-right-secret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -148,9 +155,11 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") + assumeAES256isSupported(cryptoUtil) + // contents were not tampered and we can decrypt them Assert.assertThat(prefs.values.size, CoreMatchers.`is`(2)) @@ -173,7 +182,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -188,7 +197,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -200,7 +209,7 @@ class EncryptedPrefsFormatTest : TestBase() { val frozenPrefs = "whatever man, i duno care" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") } @@ -219,7 +228,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt index d5b7ddd5f5..2ce83f0164 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt @@ -13,6 +13,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpDescription import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction @@ -156,6 +157,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { `when`(virtualPumpPlugin.shortStatus(ArgumentMatchers.anyBoolean())).thenReturn("Virtual Pump") `when`(virtualPumpPlugin.isSuspended).thenReturn(false) + `when`(virtualPumpPlugin.pumpDescription).thenReturn(PumpDescription()) `when`(treatmentsPlugin.lastCalculationTreatments).thenReturn(IobTotal(0)) `when`(treatmentsPlugin.lastCalculationTempBasals).thenReturn(IobTotal(0)) diff --git a/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt b/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt index c147c0810c..9d6b3ad9cd 100644 --- a/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt @@ -1,16 +1,31 @@ package info.nightscout.androidaps.utils import info.nightscout.androidaps.TestBase +import org.hamcrest.CoreMatchers.containsString +import org.hamcrest.CoreMatchers.not import org.junit.Assert +import org.junit.Assume.assumeThat import org.junit.Test import org.junit.runner.RunWith import org.powermock.core.classloader.annotations.PowerMockIgnore import org.powermock.modules.junit4.PowerMockRunner +// https://stackoverflow.com/questions/52344522/joseexception-couldnt-create-aes-gcm-nopadding-cipher-illegal-key-size +// https://stackoverflow.com/questions/47708951/can-aes-256-work-on-android-devices-with-api-level-26 +// Java prior to Oracle Java 8u161 does not have policy for 256 bit AES - but Android support it +// when test is run in Vanilla JVM without policy - Invalid key size exception is thrown +fun assumeAES256isSupported(cryptoUtil: CryptoUtil) { + cryptoUtil.lastException?.message?.let { exceptionMessage -> + assumeThat("Upgrade your testing environment Java (OpenJDK or Java 8u161) and JAVA_HOME - AES 256 is supported by Android so this exception should not happen!", exceptionMessage, not(containsString("key size"))) + } +} + @PowerMockIgnore("javax.crypto.*") @RunWith(PowerMockRunner::class) class CryptoUtilTest: TestBase() { + var cryptoUtil: CryptoUtil = CryptoUtil(aapsLogger) + @Test fun testFixedSaltCrypto() { val salt = byteArrayOf( @@ -19,30 +34,36 @@ class CryptoUtilTest: TestBase() { val password = "thisIsFixedPassword" val payload = "FIXED-PAYLOAD" - val encrypted = CryptoUtil.encrypt(password, salt, payload) + val encrypted = cryptoUtil.encrypt(password, salt, payload) + assumeAES256isSupported(cryptoUtil) Assert.assertNotNull(encrypted) - val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!) + + val decrypted = cryptoUtil.decrypt(password, salt, encrypted!!) + assumeAES256isSupported(cryptoUtil) Assert.assertEquals(decrypted, payload) } @Test fun testStandardCrypto() { - val salt = CryptoUtil.mineSalt() + val salt = cryptoUtil.mineSalt() val password = "topSikret" val payload = "{what:payloadYouWantToProtect}" - val encrypted = CryptoUtil.encrypt(password, salt, payload) + val encrypted = cryptoUtil.encrypt(password, salt, payload) + assumeAES256isSupported(cryptoUtil) Assert.assertNotNull(encrypted) - val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!) + + val decrypted = cryptoUtil.decrypt(password, salt, encrypted!!) + assumeAES256isSupported(cryptoUtil) Assert.assertEquals(decrypted, payload) } @Test fun testHashVector() { val payload = "{what:payloadYouWantToProtect}" - val hash = CryptoUtil.sha256(payload) + val hash = cryptoUtil.sha256(payload) Assert.assertEquals(hash, "a1aafe3ed6cc127e6d102ddbc40a205147230e9cfd178daf108c83543bbdcd13") } @@ -51,25 +72,25 @@ class CryptoUtilTest: TestBase() { val payload = "{what:payloadYouWantToProtect}" val password = "topSikret" val expectedHmac = "ea2213953d0f2e55047cae2d23fb4f0de1b805d55e6271efa70d6b85fb692bea" // generated using other HMAC tool - val hash = CryptoUtil.hmac256(payload, password) + val hash = cryptoUtil.hmac256(payload, password) Assert.assertEquals(hash, expectedHmac) } @Test fun testPlainPasswordCheck() { - Assert.assertTrue(CryptoUtil.checkPassword("same", "same")) - Assert.assertFalse(CryptoUtil.checkPassword("same", "other")) + Assert.assertTrue(cryptoUtil.checkPassword("same", "same")) + Assert.assertFalse(cryptoUtil.checkPassword("same", "other")) } @Test fun testHashedPasswordCheck() { - Assert.assertTrue(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("givenSecret"))) - Assert.assertFalse(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("otherSecret"))) + Assert.assertTrue(cryptoUtil.checkPassword("givenSecret", cryptoUtil.hashPassword("givenSecret"))) + Assert.assertFalse(cryptoUtil.checkPassword("givenSecret", cryptoUtil.hashPassword("otherSecret"))) - Assert.assertTrue(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenMashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:0fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:b0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertTrue(cryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenMashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenHashToCheck", "hmac:0fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:b0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) } }