diff options
Diffstat (limited to 'src/tests/otp.rs')
-rw-r--r-- | src/tests/otp.rs | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/tests/otp.rs b/src/tests/otp.rs new file mode 100644 index 0000000..e96bbfe --- /dev/null +++ b/src/tests/otp.rs @@ -0,0 +1,227 @@ +use std::ops::Deref; +use {Admin, Authenticate, CommandError, CommandStatus, Config, ConfigureOtp, DeviceWrapper, GenerateOtp, + OtpMode, OtpSlotData}; +use tests::util::{ADMIN_PASSWORD, USER_PASSWORD}; + +// test suite according to RFC 4226, Appendix D +static HOTP_SECRET: &str = "3132333435363738393031323334353637383930"; +static HOTP_CODES: &[&str] = &[ + "755224", "287082", "359152", "969429", "338314", "254676", "287922", "162583", "399871", + "520489", +]; + +// test suite according to RFC 6238, Appendix B +static TOTP_SECRET: &str = "3132333435363738393031323334353637383930"; +static TOTP_CODES: &[(u64, &str)] = &[ + (59, "94287082"), + (1111111109, "07081804"), + (1111111111, "14050471"), + (1234567890, "89005924"), + (2000000000, "69279037"), + (20000000000, "65353130"), +]; + +fn get_admin_test_device() -> Admin<DeviceWrapper> { + ::connect().expect("Could not connect to the Nitrokey Pro.") + .authenticate_admin(ADMIN_PASSWORD) + .expect("Could not login as admin.") +} + +fn configure_hotp(admin: &ConfigureOtp) { + let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_hotp_slot(slot_data, 0)); +} + +fn check_hotp_codes(device: &GenerateOtp) { + for code in HOTP_CODES { + let result = device.get_hotp_code(1); + assert_eq!(code, &result.unwrap()); + } +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn hotp_no_pin() { + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, false); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + + configure_hotp(&admin); + check_hotp_codes(admin.deref()); + + configure_hotp(&admin); + check_hotp_codes(&admin.device()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn hotp_pin() { + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, true); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + + configure_hotp(&admin); + let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); + check_hotp_codes(&user); + + assert!(user.device().get_hotp_code(1).is_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn hotp_slot_name() { + let admin = get_admin_test_device(); + let slot_data = OtpSlotData::new(1, "test-hotp", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_hotp_slot(slot_data, 0)); + + let device = admin.device(); + let result = device.get_hotp_slot_name(1); + assert_eq!("test-hotp", result.unwrap()); + let result = device.get_hotp_slot_name(4); + assert_eq!(CommandError::InvalidSlot, result.unwrap_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn hotp_error() { + let admin = get_admin_test_device(); + let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!( + CommandStatus::Error(CommandError::NoName), + admin.write_hotp_slot(slot_data, 0) + ); + let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!( + CommandStatus::Error(CommandError::InvalidSlot), + admin.write_hotp_slot(slot_data, 0) + ); + let code = admin.get_hotp_code(4); + assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn hotp_erase() { + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, false); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + let slot_data = OtpSlotData::new(1, "test1", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_hotp_slot(slot_data, 0)); + let slot_data = OtpSlotData::new(2, "test2", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_hotp_slot(slot_data, 0)); + + assert_eq!(CommandStatus::Success, admin.erase_hotp_slot(1)); + + let device = admin.device(); + let result = device.get_hotp_slot_name(1); + assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + let result = device.get_hotp_code(1); + assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + + assert_eq!("test2", device.get_hotp_slot_name(2).unwrap()); +} + +fn configure_totp(admin: &ConfigureOtp) { + let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); + assert_eq!(CommandStatus::Success, admin.write_totp_slot(slot_data, 30)); +} + +fn check_totp_codes(device: &GenerateOtp) { + for (i, &(time, code)) in TOTP_CODES.iter().enumerate() { + assert_eq!(CommandStatus::Success, device.set_time(time)); + let result = device.get_totp_code(1); + assert!(result.is_ok()); + let result_code = result.unwrap(); + assert_eq!( + code, result_code, + "TOTP code {} should be {} but is {}", + i, code, result_code + ); + } +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn totp_no_pin() { + // TODO: this test may fail due to bad timing --> find solution + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, false); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + + configure_totp(&admin); + check_totp_codes(admin.deref()); + + configure_totp(&admin); + check_totp_codes(&admin.device()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn totp_pin() { + // TODO: this test may fail due to bad timing --> find solution + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, true); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + + configure_totp(&admin); + let user = admin.device().authenticate_user(USER_PASSWORD).unwrap(); + check_totp_codes(&user); + + assert!(user.device().get_totp_code(1).is_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn totp_slot_name() { + let admin = get_admin_test_device(); + let slot_data = OtpSlotData::new(1, "test-totp", TOTP_SECRET, OtpMode::EightDigits); + assert_eq!(CommandStatus::Success, admin.write_totp_slot(slot_data, 0)); + + let device = admin.device(); + let result = device.get_totp_slot_name(1); + assert!(result.is_ok()); + assert_eq!("test-totp", result.unwrap()); + let result = device.get_totp_slot_name(16); + assert_eq!(CommandError::InvalidSlot, result.unwrap_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn totp_error() { + let admin = get_admin_test_device(); + let slot_data = OtpSlotData::new(1, "", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!( + CommandStatus::Error(CommandError::NoName), + admin.write_hotp_slot(slot_data, 0) + ); + let slot_data = OtpSlotData::new(4, "test", HOTP_SECRET, OtpMode::SixDigits); + assert_eq!( + CommandStatus::Error(CommandError::InvalidSlot), + admin.write_hotp_slot(slot_data, 0) + ); + let code = admin.get_hotp_code(4); + assert_eq!(CommandError::InvalidSlot, code.unwrap_err()); +} + +#[test] +#[cfg_attr(not(feature = "test-pro"), ignore)] +fn totp_erase() { + let admin = get_admin_test_device(); + let config = Config::new(None, None, None, false); + assert_eq!(CommandStatus::Success, admin.write_config(config)); + let slot_data = OtpSlotData::new(1, "test1", TOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_totp_slot(slot_data, 0)); + let slot_data = OtpSlotData::new(2, "test2", TOTP_SECRET, OtpMode::SixDigits); + assert_eq!(CommandStatus::Success, admin.write_totp_slot(slot_data, 0)); + + assert_eq!(CommandStatus::Success, admin.erase_totp_slot(1)); + + let device = admin.device(); + let result = device.get_totp_slot_name(1); + assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + let result = device.get_totp_code(1); + assert_eq!(CommandError::SlotNotProgrammed, result.unwrap_err()); + + assert_eq!("test2", device.get_totp_slot_name(2).unwrap()); +} + |