Crate rfc2289_otp
source ·Expand description
§RFC 2289 One-Time Password
Implements the One-Time Password (OTP) algorithm described in IETF RFC 2289, including functions for parsing strings and mapping between dictionary words and OTP values.
This algorithm is NOT the same as the TOTP and HOTP algorithms widely in use
today for multifactor authentication: these are defined in other RFCs. This
algorithm, however, is used in the OTP
SASL mechanism as described in
IETF RFC 2444.
§Security
Note that there are only three hash algorithms defined for use with this algorithm, all of which are no longer considered secure. These are:
- MD4
- MD5
- SHA1
However, I am of the non-professional opinion that these algorithms are
generally fine for the way that they are used by the OTP algorithm, because the
OTP algorithm performs the hash a fixed number of times with a fixed seed and a
passphrase. Still, I highly recommend using the sha1
algorithm
exclusively. It is the newest and most secure of the three.
If more algorithms are ever made official, you should see the new algorithms here.
§Feature Flags
The feature flags for this library are:
md4
: MD4 supportmd5
: MD5 supportsha1
: SHA1 supportwords
: Translation to and from dictionary wordsdyndig
: Support for any digest that implementsdigest::DynDigest
parsing
: Parsing OTP strings
All of the above are enabled by default.
§Usage
Let’s say you receive an OTP challenge as a string like so:
otp-md5 499 ke1234 ext
Decode this string like so:
let challenge_str = "otp-md5 487 dog2";
let challenge = rfc2289_otp::parse_otp_challenge(challenge_str).unwrap();
If it is a valid string, you should get a data structure that looks like this:
pub struct OTPChallenge <'a> {
pub hash_alg: &'a str,
pub hash_count: usize,
pub seed: &'a str,
}
You can use this data structure to calculate the OTP like so:
let challenge = rfc2289_otp::OTPChallenge {
hash_alg: "md5",
hash_count: 200,
seed: "wibby123",
};
let extremely_secure_passphrase = "banana";
let otp = rfc2289_otp::calculate_otp(
challenge.hash_alg,
extremely_secure_passphrase,
challenge.seed,
challenge.hash_count,
None,
).unwrap();
If the algorithm was understood, and there wasn’t any other problem, you should
get a [u8; 8]
back (64-bits), which is your OTP value.
You can directly convert this value to hex, and prepend hex:
to it to produce
a valid OTP response.
Or, you can convert it to dictionary words using the standard dictionary defined
in the specification using convert_to_word_format
. Join these words with
spaces and prefix it with word:
.
If implementing an OTP server, you can parse these responses like so:
let otp_response = "hex:5Bf0 75d9 959d 036f";
let r = rfc2289_otp::parse_otp_response(&otp_response).unwrap();
If the syntax is valid, you should get an OTPResponse
as shown below:
type Hex64Bit = [u8; 8];
pub enum HexOrWords <'a> {
Hex(Hex64Bit),
Words(&'a str),
}
pub struct OTPInit <'a> {
pub current_otp: HexOrWords<'a>,
pub new_otp: HexOrWords<'a>,
pub new_alg: &'a str,
pub new_seq_num: usize,
pub new_seed: &'a str,
}
pub enum OTPResponse <'a> {
Init(OTPInit <'a>),
Current(HexOrWords<'a>)
}
The server will need to calculate the OTP and compare that value to the decoded
hex or words supplied by the client. You can decode words to the binary OTP
value using decode_word_format_with_std_dict
like so:
let words = [ "AURA", "ALOE", "HURL", "WING", "BERG", "WAIT" ];
let decoded = rfc2289_otp::decode_word_format_with_std_dict(words).unwrap();
If the client response is one of the Init
variants, how the server chooses to
handle this is an implementation detail.
Structs§
- A parsed OTP challenge string per Section 2.1 of IETF RFC 2243.
- A parsed OTP init string per Section 4.1 of IETF RFC 2243.
Enums§
- A Hex value or dictionary words
- A parsed OTP response per Sections 3 and 4 of IETF RFC 2243.
Constants§
- Defined in IETF RFC 1760 for use in S/KEY, but used OTP in IETF RFC 2289.
Functions§
- Calculate the checksum, per section 6.0 of IETF RFC 2289.
- Calculates the One-Time Pad using the
md4
algorithm. - Calculates the One-Time Pad using the
md5
algorithm. - Calculate an OTP value from supplied parameters, per Section 6.0 of IETF RFC 2289.
- Calculates the One-Time Pad using the
sha1
algorithm. - Encode a 64-bit value using the standard dictionary words defined in IETF RFC 1760 for use in S/KEY, and used OTP in IETF RFC 2289.
- Decode a 64-bit value using the standard dictionary words defined in IETF RFC 1760 for use in S/KEY, and used OTP in IETF RFC 2289.
- Folds an arbitrary-length input (greater than 8 bytes) to 8 bytes according to the algorithm in Appendix A of IETF RFC 2289.
- See Appendix A of IETF RFC 2289.
- A parsed OTP init string per Section 4.1 of IETF RFC 2243.
- Parse OTP init strings per Section 4.1 of IETF RFC 2243.
- Parse OTP response strings per Sections 3 and 4 of IETF RFC 2243.