aboutsummaryrefslogtreecommitdiffstats
path: root/format-common/src/test/kotlin/app/passwordstore/util
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2022-12-19 16:47:17 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2022-12-19 16:47:17 +0530
commit8cfe6ec84c1bd8119fd59fbb76d729da284ced27 (patch)
tree7035f03675f8924bfc3870b68b6345faf020aaac /format-common/src/test/kotlin/app/passwordstore/util
parent39a0284cd55298eb4930db681e813260aea58867 (diff)
downloadHardenedPass-8cfe6ec84c1bd8119fd59fbb76d729da284ced27.tar.gz
HardenedPass-8cfe6ec84c1bd8119fd59fbb76d729da284ced27.zip
refactor: merge format-common-impl into format-common
Also converts the format-common module into an Android library since UriTotpFinder requires the Android SDK.
Diffstat (limited to 'format-common/src/test/kotlin/app/passwordstore/util')
-rw-r--r--format-common/src/test/kotlin/app/passwordstore/util/time/TestUserClock.kt33
-rw-r--r--format-common/src/test/kotlin/app/passwordstore/util/totp/OtpTest.kt168
-rw-r--r--format-common/src/test/kotlin/app/passwordstore/util/totp/UriTotpFinderTest.kt60
3 files changed, 261 insertions, 0 deletions
diff --git a/format-common/src/test/kotlin/app/passwordstore/util/time/TestUserClock.kt b/format-common/src/test/kotlin/app/passwordstore/util/time/TestUserClock.kt
new file mode 100644
index 00000000..8a860a39
--- /dev/null
+++ b/format-common/src/test/kotlin/app/passwordstore/util/time/TestUserClock.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.time
+
+import java.time.Clock
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZoneOffset.UTC
+
+/**
+ * Implementation of [UserClock] that is fixed to [Instant.EPOCH] for deterministic time-based tests
+ */
+class TestUserClock(instant: Instant) : UserClock() {
+
+ constructor() : this(Instant.EPOCH)
+
+ private var clock = fixed(instant, UTC)
+
+ override fun withZone(zone: ZoneId): Clock = clock.withZone(zone)
+
+ override fun getZone(): ZoneId = UTC
+
+ override fun instant(): Instant = clock.instant()
+
+ companion object {
+ fun withAddedSeconds(secondsToAdd: Long): TestUserClock {
+ return TestUserClock(Instant.EPOCH.plusSeconds(secondsToAdd))
+ }
+ }
+}
diff --git a/format-common/src/test/kotlin/app/passwordstore/util/totp/OtpTest.kt b/format-common/src/test/kotlin/app/passwordstore/util/totp/OtpTest.kt
new file mode 100644
index 00000000..54ac492d
--- /dev/null
+++ b/format-common/src/test/kotlin/app/passwordstore/util/totp/OtpTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.totp
+
+import com.github.michaelbull.result.get
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+
+class OtpTest {
+
+ private fun generateOtp(
+ counter: Long,
+ secret: String = "JBSWY3DPEHPK3PXP",
+ algorithm: String = "SHA1",
+ digits: String = "6",
+ issuer: String? = null,
+ ): String? {
+ return Otp.calculateCode(secret, counter, algorithm, digits, issuer).get()
+ }
+
+ @Test
+ fun otpGeneration6Digits() {
+ assertEquals(
+ "953550",
+ generateOtp(
+ counter = 1593333298159 / (1000 * 30),
+ )
+ )
+ assertEquals(
+ "275379",
+ generateOtp(
+ counter = 1593333571918 / (1000 * 30),
+ )
+ )
+ assertEquals(
+ "867507",
+ generateOtp(
+ counter = 1593333600517 / (1000 * 57),
+ )
+ )
+ }
+
+ @Test
+ fun otpGeneration10Digits() {
+ assertEquals(
+ "0740900914",
+ generateOtp(
+ counter = 1593333655044 / (1000 * 30),
+ digits = "10",
+ )
+ )
+ assertEquals(
+ "0070632029",
+ generateOtp(
+ counter = 1593333691405 / (1000 * 30),
+ digits = "10",
+ )
+ )
+ assertEquals(
+ "1017265882",
+ generateOtp(
+ counter = 1593333728893 / (1000 * 83),
+ digits = "10",
+ )
+ )
+ }
+
+ @Test
+ fun otpGenerationIllegalInput() {
+ assertNull(
+ generateOtp(
+ counter = 10000,
+ algorithm = "SHA0",
+ digits = "10",
+ )
+ )
+ assertNull(
+ generateOtp(
+ counter = 10000,
+ digits = "a",
+ )
+ )
+ assertNull(
+ generateOtp(
+ counter = 10000,
+ algorithm = "SHA1",
+ digits = "5",
+ )
+ )
+ assertNull(
+ generateOtp(
+ counter = 10000,
+ digits = "11",
+ )
+ )
+ assertNull(
+ generateOtp(
+ counter = 10000,
+ secret = "JBSWY3DPEHPK3PXPAAAAB",
+ digits = "6",
+ )
+ )
+ }
+
+ @Test
+ fun otpGenerationUnusualSecrets() {
+ assertEquals(
+ "127764",
+ generateOtp(
+ counter = 1593367111963 / (1000 * 30),
+ secret = "JBSWY3DPEHPK3PXPAAAAAAAA",
+ )
+ )
+ assertEquals(
+ "047515",
+ generateOtp(
+ counter = 1593367171420 / (1000 * 30),
+ secret = "JBSWY3DPEHPK3PXPAAAAA",
+ )
+ )
+ }
+
+ @Test
+ fun otpGenerationUnpaddedSecrets() {
+ // 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 =
+ generateOtp(
+ counter = 1593367171420 / (1000 * 30),
+ secret = "ON2HE2LOM4QHO2LUNAQHG33NMUQHAYLEMRUW4ZZANZSWKZDFMQFA",
+ )
+ val paddedOtp =
+ generateOtp(
+ counter = 1593367171420 / (1000 * 30),
+ secret = "ON2HE2LOM4QHO2LUNAQHG33NMUQHAYLEMRUW4ZZANZSWKZDFMQFA====",
+ )
+
+ assertNotNull(unpaddedOtp)
+ assertNotNull(paddedOtp)
+ assertEquals(unpaddedOtp, paddedOtp)
+ }
+
+ @Test
+ fun generateSteamTotp() {
+ val issuerOtp =
+ generateOtp(
+ counter = 48297900 / (1000 * 30),
+ secret = "STK7746GVMCHMNH5FBIAQXGPV3I7ZHRG",
+ issuer = "Steam",
+ )
+ val digitsOtp =
+ generateOtp(
+ counter = 48297900 / (1000 * 30),
+ secret = "STK7746GVMCHMNH5FBIAQXGPV3I7ZHRG",
+ digits = "s",
+ )
+ assertNotNull(issuerOtp)
+ assertNotNull(digitsOtp)
+ assertEquals("6M3CT", issuerOtp)
+ assertEquals("6M3CT", digitsOtp)
+ }
+}
diff --git a/format-common/src/test/kotlin/app/passwordstore/util/totp/UriTotpFinderTest.kt b/format-common/src/test/kotlin/app/passwordstore/util/totp/UriTotpFinderTest.kt
new file mode 100644
index 00000000..c62df0e7
--- /dev/null
+++ b/format-common/src/test/kotlin/app/passwordstore/util/totp/UriTotpFinderTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.totp
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+@RunWith(RobolectricTestRunner::class)
+@Config(sdk = [23])
+class UriTotpFinderTest {
+
+ private val totpFinder = UriTotpFinder()
+
+ @Test
+ fun findSecret() {
+ assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret(TOTP_URI))
+ assertEquals(
+ "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ",
+ totpFinder.findSecret("name\npassword\ntotp: HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ")
+ )
+ assertEquals("HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", totpFinder.findSecret(PASS_FILE_CONTENT))
+ }
+
+ @Test
+ fun findDigits() {
+ assertEquals("12", totpFinder.findDigits(TOTP_URI))
+ assertEquals("12", totpFinder.findDigits(PASS_FILE_CONTENT))
+ }
+
+ @Test
+ fun findPeriod() {
+ assertEquals(25, totpFinder.findPeriod(TOTP_URI))
+ assertEquals(25, totpFinder.findPeriod(PASS_FILE_CONTENT))
+ }
+
+ @Test
+ fun findAlgorithm() {
+ assertEquals("SHA256", totpFinder.findAlgorithm(TOTP_URI))
+ assertEquals("SHA256", totpFinder.findAlgorithm(PASS_FILE_CONTENT))
+ }
+
+ @Test
+ fun findIssuer() {
+ assertEquals("ACME Co", totpFinder.findIssuer(TOTP_URI))
+ assertEquals("ACME Co", totpFinder.findIssuer(PASS_FILE_CONTENT))
+ }
+
+ companion object {
+
+ const val TOTP_URI =
+ "otpauth://totp/ACME%20Co:john@example.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA256&digits=12&period=25"
+ const val PASS_FILE_CONTENT = "password\n$TOTP_URI"
+ }
+}