Skip to content

Commit be75311

Browse files
authored
[PM-13420] Support case-insensitive TOTP generation (#1144)
## 🎟️ Tracking <!-- Paste the link to the Jira or GitHub issue or otherwise describe / point to where this change is coming from. --> https://bitwarden.atlassian.net/browse/PM-13420 ## 📔 Objective Update TOTP parsing to be case insensitive by lowercasing the input initially. `decode_b32` is already converting the input to uppercase so secret decoding will still work the same. I've also removed the `to_uppercase` from algorithm now that we know the string is lowercase. ## ⏰ Reminders before review - Contributor guidelines followed - All formatters and local linters executed and passed - Written new unit and / or integration tests where applicable - Protected functional changes with optionality (feature flags) - Used internationalization (i18n) for all UI strings - CI builds passed - Communicated to DevOps any deployment requirements - Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team ## 🦮 Reviewer guidelines <!-- Suggested interactions but feel free to use (or not) as you desire! --> - 👍 (`:+1:`) or similar for great changes - 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info - ❓ (`:question:`) for questions - 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion - 🎨 (`:art:`) for suggestions / improvements - ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention - 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt - ⛏ (`:pick:`) for minor or nitpick changes
1 parent 39358ac commit be75311

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

crates/bitwarden-vault/src/totp.rs

+38-6
Original file line numberDiff line numberDiff line change
@@ -157,17 +157,19 @@ impl FromStr for Totp {
157157
/// - OTP Auth URI
158158
/// - Steam URI
159159
fn from_str(key: &str) -> Result<Self, Self::Err> {
160+
let key = key.to_lowercase();
161+
160162
let params = if key.starts_with("otpauth://") {
161-
let url = Url::parse(key).map_err(|_| TotpError::InvalidOtpauth)?;
163+
let url = Url::parse(&key).map_err(|_| TotpError::InvalidOtpauth)?;
162164
let parts: HashMap<_, _> = url.query_pairs().collect();
163165

164166
Totp {
165167
algorithm: parts
166168
.get("algorithm")
167-
.and_then(|v| match v.to_uppercase().as_ref() {
168-
"SHA1" => Some(Algorithm::Sha1),
169-
"SHA256" => Some(Algorithm::Sha256),
170-
"SHA512" => Some(Algorithm::Sha512),
169+
.and_then(|v| match v.as_ref() {
170+
"sha1" => Some(Algorithm::Sha1),
171+
"sha256" => Some(Algorithm::Sha256),
172+
"sha512" => Some(Algorithm::Sha512),
171173
_ => None,
172174
})
173175
.unwrap_or(DEFAULT_ALGORITHM),
@@ -200,7 +202,7 @@ impl FromStr for Totp {
200202
algorithm: DEFAULT_ALGORITHM,
201203
digits: DEFAULT_DIGITS,
202204
period: DEFAULT_PERIOD,
203-
secret: decode_b32(key),
205+
secret: decode_b32(&key),
204206
}
205207
};
206208

@@ -285,6 +287,7 @@ mod tests {
285287
("PIUD1IS!EQYA=", "829846"), // sanitized
286288
// Steam
287289
("steam://HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", "7W6CJ"),
290+
("StEam://HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ", "7W6CJ"),
288291
("steam://ABCD123", "N26DF"),
289292
// Various weird lengths
290293
("ddfdf", "932653"),
@@ -321,6 +324,20 @@ mod tests {
321324
assert_eq!(response.period, 30);
322325
}
323326

327+
#[test]
328+
fn test_generate_otpauth_uppercase() {
329+
let key = "OTPauth://totp/test-account?secret=WQIQ25BRKZYCJVYP".to_string();
330+
let time = Some(
331+
DateTime::parse_from_rfc3339("2023-01-01T00:00:00.000Z")
332+
.unwrap()
333+
.with_timezone(&Utc),
334+
);
335+
let response = generate_totp(key, time).unwrap();
336+
337+
assert_eq!(response.code, "194506".to_string());
338+
assert_eq!(response.period, 30);
339+
}
340+
324341
#[test]
325342
fn test_generate_otpauth_period() {
326343
let key = "otpauth://totp/test-account?secret=WQIQ25BRKZYCJVYP&period=60".to_string();
@@ -335,6 +352,21 @@ mod tests {
335352
assert_eq!(response.period, 60);
336353
}
337354

355+
#[test]
356+
fn test_generate_otpauth_algorithm_sha256() {
357+
let key =
358+
"otpauth://totp/test-account?secret=WQIQ25BRKZYCJVYP&algorithm=SHA256".to_string();
359+
let time = Some(
360+
DateTime::parse_from_rfc3339("2023-01-01T00:00:00.000Z")
361+
.unwrap()
362+
.with_timezone(&Utc),
363+
);
364+
let response = generate_totp(key, time).unwrap();
365+
366+
assert_eq!(response.code, "842615".to_string());
367+
assert_eq!(response.period, 30);
368+
}
369+
338370
#[test]
339371
fn test_generate_totp_cipher_view() {
340372
let view = CipherListView {

0 commit comments

Comments
 (0)