diff options
author | Harsh Shandilya <msfjarvis@gmail.com> | 2020-06-29 12:08:59 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-29 12:08:59 +0530 |
commit | 063c1a1144bb50845ecfb7d56eea16e4db4540e4 (patch) | |
tree | eb237a470953264a54f2854f0e534bb2ab682927 /app/src/test/java/com | |
parent | 56c301dc7c5353d7f7021e04441104cfe42c063f (diff) |
Reintroduce TOTP support (#890)
Co-authored-by: Fabian Henneke <fabian@henneke.me>
Diffstat (limited to 'app/src/test/java/com')
3 files changed, 157 insertions, 63 deletions
diff --git a/app/src/test/java/com/zeapo/pwdstore/PasswordEntryTest.kt b/app/src/test/java/com/zeapo/pwdstore/PasswordEntryTest.kt deleted file mode 100644 index 2074f40b..00000000 --- a/app/src/test/java/com/zeapo/pwdstore/PasswordEntryTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package com.zeapo.pwdstore - -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNull -import kotlin.test.assertTrue - -class PasswordEntryTest { - @Test fun testGetPassword() { - assertEquals("fooooo", PasswordEntry("fooooo\nbla\n").password) - assertEquals("fooooo", PasswordEntry("fooooo\nbla").password) - assertEquals("fooooo", PasswordEntry("fooooo\n").password) - assertEquals("fooooo", PasswordEntry("fooooo").password) - assertEquals("", PasswordEntry("\nblubb\n").password) - assertEquals("", PasswordEntry("\nblubb").password) - assertEquals("", PasswordEntry("\n").password) - assertEquals("", PasswordEntry("").password) - } - - @Test fun testGetExtraContent() { - assertEquals("bla\n", PasswordEntry("fooooo\nbla\n").extraContent) - assertEquals("bla", PasswordEntry("fooooo\nbla").extraContent) - assertEquals("", PasswordEntry("fooooo\n").extraContent) - assertEquals("", PasswordEntry("fooooo").extraContent) - assertEquals("blubb\n", PasswordEntry("\nblubb\n").extraContent) - assertEquals("blubb", PasswordEntry("\nblubb").extraContent) - assertEquals("", PasswordEntry("\n").extraContent) - assertEquals("", PasswordEntry("").extraContent) - } - - @Test fun testGetUsername() { - for (field in PasswordEntry.USERNAME_FIELDS) { - assertEquals("username", PasswordEntry("\n$field username").username) - assertEquals("username", PasswordEntry("\n${field.toUpperCase()} username").username) - } - assertEquals( - "username", - PasswordEntry("secret\nextra\nlogin: username\ncontent\n").username) - assertEquals( - "username", - PasswordEntry("\nextra\nusername: username\ncontent\n").username) - assertEquals( - "username", PasswordEntry("\nUSERNaMe: username\ncontent\n").username) - assertEquals("username", PasswordEntry("\nlogin: username").username) - assertEquals("foo@example.com", PasswordEntry("\nemail: foo@example.com").username) - assertEquals("username", PasswordEntry("\nidentity: username\nlogin: another_username").username) - assertEquals("username", PasswordEntry("\nLOGiN:username").username) - assertNull(PasswordEntry("secret\nextra\ncontent\n").username) - } - - @Test fun testHasUsername() { - assertTrue(PasswordEntry("secret\nextra\nlogin: username\ncontent\n").hasUsername()) - assertFalse(PasswordEntry("secret\nextra\ncontent\n").hasUsername()) - assertFalse(PasswordEntry("secret\nlogin failed\n").hasUsername()) - assertFalse(PasswordEntry("\n").hasUsername()) - assertFalse(PasswordEntry("").hasUsername()) - } -} diff --git a/app/src/test/java/com/zeapo/pwdstore/model/PasswordEntryTest.kt b/app/src/test/java/com/zeapo/pwdstore/model/PasswordEntryTest.kt new file mode 100644 index 00000000..f31709df --- /dev/null +++ b/app/src/test/java/com/zeapo/pwdstore/model/PasswordEntryTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package com.zeapo.pwdstore.model + +import com.zeapo.pwdstore.utils.Otp +import com.zeapo.pwdstore.utils.TotpFinder +import org.junit.Test +import java.util.Date +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class PasswordEntryTest { + private fun makeEntry(content: String) = PasswordEntry(content, testFinder) + + @Test fun testGetPassword() { + assertEquals("fooooo", makeEntry("fooooo\nbla\n").password) + assertEquals("fooooo", makeEntry("fooooo\nbla").password) + assertEquals("fooooo", makeEntry("fooooo\n").password) + assertEquals("fooooo", makeEntry("fooooo").password) + assertEquals("", makeEntry("\nblubb\n").password) + assertEquals("", makeEntry("\nblubb").password) + assertEquals("", makeEntry("\n").password) + assertEquals("", makeEntry("").password) + } + + @Test fun testGetExtraContent() { + assertEquals("bla\n", makeEntry("fooooo\nbla\n").extraContent) + assertEquals("bla", makeEntry("fooooo\nbla").extraContent) + assertEquals("", makeEntry("fooooo\n").extraContent) + assertEquals("", makeEntry("fooooo").extraContent) + assertEquals("blubb\n", makeEntry("\nblubb\n").extraContent) + assertEquals("blubb", makeEntry("\nblubb").extraContent) + assertEquals("", makeEntry("\n").extraContent) + assertEquals("", makeEntry("").extraContent) + } + + @Test fun testGetUsername() { + for (field in PasswordEntry.USERNAME_FIELDS) { + assertEquals("username", makeEntry("\n$field username").username) + assertEquals("username", makeEntry("\n${field.toUpperCase()} username").username) + } + assertEquals( + "username", + makeEntry("secret\nextra\nlogin: username\ncontent\n").username) + assertEquals( + "username", + makeEntry("\nextra\nusername: username\ncontent\n").username) + assertEquals( + "username", makeEntry("\nUSERNaMe: username\ncontent\n").username) + assertEquals("username", makeEntry("\nlogin: username").username) + assertEquals("foo@example.com", makeEntry("\nemail: foo@example.com").username) + assertEquals("username", makeEntry("\nidentity: username\nlogin: another_username").username) + assertEquals("username", makeEntry("\nLOGiN:username").username) + assertNull(makeEntry("secret\nextra\ncontent\n").username) + } + + @Test fun testHasUsername() { + assertTrue(makeEntry("secret\nextra\nlogin: username\ncontent\n").hasUsername()) + assertFalse(makeEntry("secret\nextra\ncontent\n").hasUsername()) + assertFalse(makeEntry("secret\nlogin failed\n").hasUsername()) + assertFalse(makeEntry("\n").hasUsername()) + assertFalse(makeEntry("").hasUsername()) + } + + @Test fun testGeneratesOtpFromTotpUri() { + val entry = makeEntry("secret\nextra\n$TOTP_URI") + assertTrue(entry.hasTotp()) + val code = Otp.calculateCode( + entry.totpSecret!!, + // The hardcoded date value allows this test to stay reproducible. + Date(8640000).time / (1000 * entry.totpPeriod), + entry.totpAlgorithm, + entry.digits + ) + assertNotNull(code) { "Generated OTP cannot be null" } + assertEquals(entry.digits.toInt(), code.length) + assertEquals("545293", code) + } + + companion object { + const val TOTP_URI = "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30" + + // This implementation is hardcoded for the URI above. + val testFinder = object : TotpFinder { + override fun findSecret(content: String): String? { + return "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ" + } + + override fun findDigits(content: String): String { + return "6" + } + + override fun findPeriod(content: String): Long { + return 30 + } + + override fun findAlgorithm(content: String): String { + return "SHA1" + } + } + } +} diff --git a/app/src/test/java/com/zeapo/pwdstore/utils/OtpTest.kt b/app/src/test/java/com/zeapo/pwdstore/utils/OtpTest.kt new file mode 100644 index 00000000..710b0845 --- /dev/null +++ b/app/src/test/java/com/zeapo/pwdstore/utils/OtpTest.kt @@ -0,0 +1,50 @@ +package com.zeapo.pwdstore.utils + +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class OtpTest { + + @Test + fun testOtpGeneration6Digits() { + assertEquals("953550", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333298159 / (1000 * 30), "SHA1", "6")) + assertEquals("275379", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333571918 / (1000 * 30), "SHA1", "6")) + assertEquals("867507", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333600517 / (1000 * 57), "SHA1", "6")) + } + + @Test + fun testOtpGeneration10Digits() { + assertEquals("0740900914", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333655044 / (1000 * 30), "SHA1", "10")) + assertEquals("0070632029", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333691405 / (1000 * 30), "SHA1", "10")) + assertEquals("1017265882", Otp.calculateCode("JBSWY3DPEHPK3PXP", 1593333728893 / (1000 * 83), "SHA1", "10")) + } + + @Test + fun testOtpGenerationIllegalInput() { + assertNull(Otp.calculateCode("JBSWY3DPEHPK3PXP", 10000, "SHA0", "10")) + assertNull(Otp.calculateCode("JBSWY3DPEHPK3PXP", 10000, "SHA1", "a")) + assertNull(Otp.calculateCode("JBSWY3DPEHPK3PXP", 10000, "SHA1", "5")) + assertNull(Otp.calculateCode("JBSWY3DPEHPK3PXP", 10000, "SHA1", "11")) + assertNull(Otp.calculateCode("JBSWY3DPEHPK3PXPAAAAB", 10000, "SHA1", "6")) + } + + @Test + fun testOtpGenerationUnusualSecrets() { + assertEquals("127764", Otp.calculateCode("JBSWY3DPEHPK3PXPAAAAAAAA", 1593367111963 / (1000 * 30), "SHA1", "6")) + assertEquals("047515", Otp.calculateCode("JBSWY3DPEHPK3PXPAAAAA", 1593367171420 / (1000 * 30), "SHA1", "6")) + } + + @Test + fun testOtpGenerationUnpaddedSecrets() { + // Secret was generated with `echo 'string with some padding needed' | base32` + // We don't care for the resultant OTP's actual value, we just want both the padded and + // unpadded variant to generate the same one. + val unpaddedOtp = Otp.calculateCode("ON2HE2LOM4QHO2LUNAQHG33NMUQHAYLEMRUW4ZZANZSWKZDFMQFA", 1593367171420 / (1000 * 30), "SHA1", "6") + val paddedOtp = Otp.calculateCode("ON2HE2LOM4QHO2LUNAQHG33NMUQHAYLEMRUW4ZZANZSWKZDFMQFA====", 1593367171420 / (1000 * 30), "SHA1", "6") + assertNotNull(unpaddedOtp) + assertNotNull(paddedOtp) + assertEquals(unpaddedOtp, paddedOtp) + } +} |