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 support
  • md5: MD5 support
  • sha1: SHA1 support
  • words: Translation to and from dictionary words
  • dyndig: Support for any digest that implements digest::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§

OTPChallenge
A parsed OTP challenge string per Section 2.1 of IETF RFC 2243.
OTPInit
A parsed OTP init string per Section 4.1 of IETF RFC 2243.

Enums§

HexOrWords
A Hex value or dictionary words
OTPResponse
A parsed OTP response per Sections 3 and 4 of IETF RFC 2243.

Constants§

STANDARD_DICTIONARY
Defined in IETF RFC 1760 for use in S/KEY, but used OTP in IETF RFC 2289.

Functions§

calculate_checksum
Calculate the checksum, per section 6.0 of IETF RFC 2289.
calculate_md4_otp
Calculates the One-Time Pad using the md4 algorithm.
calculate_md5_otp
Calculates the One-Time Pad using the md5 algorithm.
calculate_otp
Calculate an OTP value from supplied parameters, per Section 6.0 of IETF RFC 2289.
calculate_sha1_otp
Calculates the One-Time Pad using the sha1 algorithm.
convert_to_word_format
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_word_format_with_std_dict
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.
fold_md
Folds an arbitrary-length input (greater than 8 bytes) to 8 bytes according to the algorithm in Appendix A of IETF RFC 2289.
fold_sha1
See Appendix A of IETF RFC 2289.
parse_otp_challenge
A parsed OTP init string per Section 4.1 of IETF RFC 2243.
parse_otp_init
Parse OTP init strings per Section 4.1 of IETF RFC 2243.
parse_otp_response
Parse OTP response strings per Sections 3 and 4 of IETF RFC 2243.

Type Aliases§

Hex64Bit