@@ -9,6 +9,9 @@ import androidx.annotation.VisibleForTesting
9
9
import app.passwordstore.util.time.UserClock
10
10
import app.passwordstore.util.totp.Otp
11
11
import app.passwordstore.util.totp.TotpFinder
12
+ import com.github.michaelbull.result.Err
13
+ import com.github.michaelbull.result.Ok
14
+ import com.github.michaelbull.result.Result
12
15
import com.github.michaelbull.result.mapBoth
13
16
import dagger.assisted.Assisted
14
17
import dagger.assisted.AssistedFactory
@@ -60,14 +63,22 @@ constructor(
60
63
require(totpSecret != null ) { " Cannot collect this flow without a TOTP secret" }
61
64
do {
62
65
val otp = calculateTotp()
63
- emit(otp)
64
- delay(THOUSAND_MILLIS .milliseconds)
66
+ if (otp.isOk) {
67
+ emit(otp.value)
68
+ delay(THOUSAND_MILLIS .milliseconds)
69
+ } else {
70
+ throw otp.error
71
+ }
65
72
} while (coroutineContext.isActive)
66
73
}
67
74
68
75
/* * Obtain the [Totp.value] for this [PasswordEntry] at the current time. */
69
76
public val currentOtp: String
70
- get() = calculateTotp().value
77
+ get() {
78
+ val otp = calculateTotp()
79
+ check(otp.isOk)
80
+ return otp.value.value
81
+ }
71
82
72
83
/* *
73
84
* String representation of [extraContent] but with authentication related data such as TOTP URIs
@@ -83,7 +94,14 @@ constructor(
83
94
extraContentWithoutAuthData = generateExtraContentWithoutAuthData()
84
95
extraContent = generateExtraContentPairs()
85
96
username = findUsername()
86
- totpSecret = totpFinder.findSecret(content)
97
+ // Verify the TOTP secret is valid and disable TOTP if not.
98
+ val secret = totpFinder.findSecret(content)
99
+ totpSecret =
100
+ if (secret != null && calculateTotp(secret).isOk) {
101
+ secret
102
+ } else {
103
+ null
104
+ }
87
105
}
88
106
89
107
public fun hasTotp (): Boolean {
@@ -175,26 +193,21 @@ constructor(
175
193
return null
176
194
}
177
195
178
- private fun calculateTotp (): Totp {
196
+ private fun calculateTotp (secret : String = totpSecret!! ): Result < Totp , Throwable > {
179
197
val digits = totpFinder.findDigits(content)
180
198
val totpPeriod = totpFinder.findPeriod(content)
181
199
val totpAlgorithm = totpFinder.findAlgorithm(content)
182
200
val issuer = totpFinder.findIssuer(content)
183
201
val millis = clock.millis()
184
202
val remainingTime = (totpPeriod - ((millis / THOUSAND_MILLIS ) % totpPeriod)).seconds
185
- Otp .calculateCode(
186
- totpSecret !! ,
203
+ return Otp .calculateCode(
204
+ secret ,
187
205
millis / (THOUSAND_MILLIS * totpPeriod),
188
206
totpAlgorithm,
189
207
digits,
190
208
issuer
191
209
)
192
- .mapBoth(
193
- { code ->
194
- return Totp (code, remainingTime)
195
- },
196
- { throwable -> throw throwable }
197
- )
210
+ .mapBoth({ code -> Ok (Totp (code, remainingTime)) }, ::Err )
198
211
}
199
212
200
213
@AssistedFactory
0 commit comments