Skip to content

Commit ff187f7

Browse files
committed
Better error handling and do not crash if an error occurres
1 parent 9e5a3fb commit ff187f7

File tree

9 files changed

+57
-26
lines changed

9 files changed

+57
-26
lines changed

src/interface/table.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ pub fn fill_table(table: &mut StatefulTable, elements: &[OTPElement]) {
7070
(i + 1).to_string(),
7171
element.issuer.to_owned(),
7272
label,
73-
element.get_otp_code().unwrap(),
73+
match element.get_otp_code() {
74+
Ok(result) => result,
75+
Err(error) => error.to_string(),
76+
},
7477
]);
7578
}
7679
}

src/otp/algorithms/hotp_maker.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ use sha1::Sha1;
1313
use sha2::{Sha256, Sha512};
1414

1515
use crate::otp::otp_algorithm::OTPAlgorithm;
16+
use crate::otp::otp_error::OtpError;
1617

17-
pub fn hotp(secret: &str, algorithm: OTPAlgorithm, counter: u64) -> Result<u32, String> {
18+
pub fn hotp(secret: &str, algorithm: OTPAlgorithm, counter: u64) -> Result<u32, OtpError> {
1819
match algorithm {
1920
OTPAlgorithm::Sha256 => generate_hotp::<Sha256>(secret, counter),
2021
OTPAlgorithm::Sha512 => generate_hotp::<Sha512>(secret, counter),
2122
_ => generate_hotp::<Sha1>(secret, counter),
2223
}
2324
}
2425

25-
fn generate_hotp<D>(secret: &str, counter: u64) -> Result<u32, String>
26+
fn generate_hotp<D>(secret: &str, counter: u64) -> Result<u32, OtpError>
2627
where
2728
D: CoreProxy,
2829
D::Core: HashMarker
@@ -37,21 +38,21 @@ where
3738
// decode the base32 secret
3839
let secret_decoded = match BASE32_NOPAD.decode(secret.as_bytes()) {
3940
Ok(result) => result,
40-
Err(e) => return Err(format!("{e:?}")),
41+
Err(e) => return Err(OtpError::SecretEncoding(e.kind, e.position)),
4142
};
4243

4344
let hash = hotp_hash::<D>(&secret_decoded, counter);
4445

4546
// calculate offset
4647
let offset: usize = match hash.last() {
4748
Some(result) => *result & 0xf,
48-
None => return Err(String::from("Invalid digest")),
49+
None => return Err(OtpError::InvalidOffset),
4950
} as usize;
5051

5152
// calculate code
5253
let code_bytes: [u8; 4] = match hash[offset..offset + 4].try_into() {
5354
Ok(x) => x,
54-
Err(_) => return Err(String::from("Invalid digest")),
55+
Err(_) => return Err(OtpError::InvalidDigest),
5556
};
5657
Ok(u32::from_be_bytes(code_bytes) & 0x7fffffff)
5758
}

src/otp/algorithms/motp_maker.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::time::SystemTime;
22

3+
use crate::otp::otp_error::OtpError;
34
use md5::{Digest, Md5};
45

5-
pub fn motp(secret: &str, pin: &str, period: u64, digits: usize) -> Result<String, String> {
6+
pub fn motp(secret: &str, pin: &str, period: u64, digits: usize) -> Result<String, OtpError> {
67
let seconds = SystemTime::now()
78
.duration_since(SystemTime::UNIX_EPOCH)
89
.unwrap()
@@ -17,7 +18,7 @@ fn get_motp_code(
1718
period: u64,
1819
digits: usize,
1920
seconds: u64,
20-
) -> Result<String, String> {
21+
) -> Result<String, OtpError> {
2122
// TODO MOTP Secrets are hex encoded, so do not use BASE32 at all
2223
let hex_secret = secret;
2324
let counter = seconds / period;

src/otp/algorithms/steam_otp_maker.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Ported from https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/java/com/beemdevelopment/aegis/crypto/otp/OTP.java
22

33
use crate::otp::otp_algorithm::OTPAlgorithm;
4+
use crate::otp::otp_error::OtpError;
45

56
use super::totp_maker::totp;
67

78
const STEAM_ALPHABET: &str = "23456789BCDFGHJKMNPQRTVWXY";
89

9-
pub fn steam(secret: &str, algorithm: OTPAlgorithm, digits: usize) -> Result<String, String> {
10+
pub fn steam(secret: &str, algorithm: OTPAlgorithm, digits: usize) -> Result<String, OtpError> {
1011
match totp(secret, algorithm) {
1112
Ok(v) => Ok(to_steam_string(v as usize, digits)),
1213
Err(e) => Err(e),

src/otp/algorithms/totp_maker.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use std::time::SystemTime;
22

33
use crate::otp::otp_algorithm::OTPAlgorithm;
4+
use crate::otp::otp_error::OtpError;
45

56
use super::hotp_maker::hotp;
67

7-
pub fn totp(secret: &str, algorithm: OTPAlgorithm) -> Result<u32, String> {
8+
pub fn totp(secret: &str, algorithm: OTPAlgorithm) -> Result<u32, OtpError> {
89
let time = SystemTime::now()
910
.duration_since(SystemTime::UNIX_EPOCH)
1011
.unwrap()
@@ -18,7 +19,7 @@ fn generate_totp(
1819
time: u64,
1920
time_step: u64,
2021
skew: i64,
21-
) -> Result<u32, String> {
22+
) -> Result<u32, OtpError> {
2223
hotp(secret, algorithm, ((time as i64 + skew) as u64) / time_step)
2324
}
2425

src/otp/algorithms/yandex_otp_maker.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use sha1::{Digest, Sha1};
1616
use sha2::{Sha256, Sha512};
1717

1818
use crate::otp::otp_algorithm::OTPAlgorithm;
19+
use crate::otp::otp_error::OtpError;
1920

2021
use super::hotp_maker::hotp_hash;
2122

@@ -28,7 +29,7 @@ pub fn yandex(
2829
period: u64,
2930
digits: usize,
3031
algorithm: OTPAlgorithm,
31-
) -> Result<String, String> {
32+
) -> Result<String, OtpError> {
3233
let seconds = SystemTime::now()
3334
.duration_since(SystemTime::UNIX_EPOCH)
3435
.unwrap()
@@ -52,7 +53,7 @@ fn calculate_yandex_code<D>(
5253
period: u64,
5354
digits: usize,
5455
seconds: u64,
55-
) -> Result<String, String>
56+
) -> Result<String, OtpError>
5657
where
5758
D: CoreProxy,
5859
D::Core: HashMarker
@@ -66,7 +67,7 @@ where
6667
{
6768
let decoded_secret = match BASE32_NOPAD.decode(secret.as_bytes()) {
6869
Ok(r) => r,
69-
Err(_) => return Err(String::from("Error during secret parsing")),
70+
Err(e) => return Err(OtpError::SecretEncoding(e.kind, e.position)),
7071
};
7172

7273
let parsed_secret = &decoded_secret.as_slice()[0..SECRET_LENGHT];
@@ -89,15 +90,15 @@ where
8990
// calculate offset
9091
let offset: usize = match period_hash.last() {
9192
Some(result) => *result & 0xf,
92-
None => return Err(String::from("Invalid digest")),
93+
None => return Err(OtpError::InvalidOffset),
9394
} as usize;
9495

9596
period_hash[offset] &= 0x7f;
9697

9798
// calculate code
9899
let code_bytes: [u8; 8] = match period_hash[offset..offset + 8].try_into() {
99100
Ok(x) => x,
100-
Err(_) => return Err(String::from("Invalid digest")),
101+
Err(_) => return Err(OtpError::InvalidDigest),
101102
};
102103

103104
let code = u64::from_be_bytes(code_bytes);

src/otp/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ pub mod algorithms;
22
pub mod migrations;
33
pub mod otp_algorithm;
44
pub mod otp_element;
5+
mod otp_error;
56
pub mod otp_type;

src/otp/otp_element.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{fs::File, io::Write, path::PathBuf, vec};
22

3+
use crate::otp::otp_error::OtpError;
34
use crate::{
45
crypto::cryptography::{argon_derive_key, encrypt_string_with_key, gen_salt},
56
utils,
@@ -252,7 +253,7 @@ impl OTPElement {
252253
.build()
253254
}
254255

255-
pub fn get_otp_code(&self) -> Result<String, String> {
256+
pub fn get_otp_code(&self) -> Result<String, OtpError> {
256257
match self.type_ {
257258
OTPType::Totp => {
258259
let code = totp(&self.secret, self.algorithm)?;
@@ -265,9 +266,7 @@ impl OTPElement {
265266

266267
Ok(self.format_code(code))
267268
}
268-
None => Err(String::from(
269-
"The element is an HOTP code but there is no counter value.",
270-
)),
269+
None => Err(OtpError::MissingCounter),
271270
},
272271
OTPType::Steam => steam(&self.secret, self.algorithm, self.digits as usize),
273272
OTPType::Yandex => match &self.pin {
@@ -278,9 +277,7 @@ impl OTPElement {
278277
self.digits as usize,
279278
self.algorithm,
280279
),
281-
None => Err(String::from(
282-
"This element is a Yandex code but there is not pin value",
283-
)),
280+
None => Err(OtpError::MissingPin),
284281
},
285282
OTPType::Motp => match &self.pin {
286283
Some(pin) => motp(
@@ -289,9 +286,7 @@ impl OTPElement {
289286
self.period,
290287
self.digits as usize,
291288
),
292-
None => Err(String::from(
293-
"This element is an MOTP code but the is not pin value",
294-
)),
289+
None => Err(OtpError::MissingPin),
295290
},
296291
}
297292
}

src/otp/otp_error.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use data_encoding::DecodeKind;
2+
use std::fmt::{Display, Formatter};
3+
4+
#[derive(Debug, PartialEq)]
5+
pub enum OtpError {
6+
SecretEncoding(DecodeKind, usize), // Secret encoding error, of given kind at give position
7+
MissingPin, // Missing Pin for Yandex / MOTP Codes
8+
MissingCounter, // Missing counter for HOTP codes
9+
InvalidOffset, // Invalid offset
10+
InvalidDigest, // Invalid digest
11+
}
12+
13+
impl Display for OtpError {
14+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
15+
match self {
16+
OtpError::SecretEncoding(decode_kind, position) => {
17+
f.write_str(format!("Decode error {decode_kind} at {position}").as_str())
18+
}
19+
OtpError::MissingPin => f.write_str("Missing pin value"),
20+
OtpError::MissingCounter => f.write_str("Missing counter value"),
21+
OtpError::InvalidDigest => f.write_str("Invalid digest"),
22+
OtpError::InvalidOffset => f.write_str("Invalid offset"),
23+
}
24+
}
25+
}
26+
27+
impl std::error::Error for OtpError {}

0 commit comments

Comments
 (0)