Skip to main content

Crate otp_offline

Crate otp_offline 

Source
Expand description

§YubiKey OTP Verification Library

Offline verification primitives for YubiKey OTPs.

This crate provides:

  • modhex parsing and formatting
  • OTP parsing and AES-128 decryption
  • replay-detection validation helpers
  • a small file-backed example store behind an optional feature

§Feature flags

Default features:

  • decryption
  • modhex

Optional features:

  • simple_store — enables a small example file-backed store implementation
  • decryption implies the optional aes dependency
  • simple_store depends on both decryption and modhex

§Modhex basics

YubiKey OTPs are encoded with modhex, a keyboard-layout-friendly alphabet.

The mapping is:

Hex0123456789abcdef
Modhexcbdefghijklnrtuv

§Examples

§Encode and decode modhex

use otp_offline::modhex::ModHex;

let raw = [0x01, 0x23, 0xab, 0xcd];
let encoded = ModHex::from(&raw[..]).to_string();

assert_eq!(encoded, "cbdelnrt");

let decoded = ModHex::try_from(encoded.as_str()).unwrap();
assert_eq!(decoded.raw_bytes(), &raw);

assert!(ModHex::is_valid("cbdelnrt").is_ok());
assert!(ModHex::is_valid("not-modhex").is_err());

§Parse and decrypt a YubiKey OTP

use otp_offline::otp::Otp;

let otp = Otp::from_modhex("cbcdcecfcgchkgnhckifdncgiflkcediddgrldhuubth").unwrap();
let decrypted = otp.decrypt(&[0; 16]).unwrap();

assert_eq!(otp.id.to_string(), "cbcdcecfcgch");
assert_eq!(decrypted.private.id.raw_bytes, [7, 8, 9, 10, 11, 12]);
assert_eq!(decrypted.private.usage_counter, 1);
assert_eq!(decrypted.private.session_counter, 1);

§Validate a decrypted OTP against a previous one

The crate exposes validation helpers for replay detection and monotonic counter checks.

use otp_offline::otp::{DecryptedOtp, DecryptedPrivateData, Otp};

let otp = Otp::from_modhex("cbcdcecfcgchkgnhckifdncgiflkcediddgrldhuubth").unwrap();
let decrypted = otp.decrypt(&[0; 16]).unwrap();

let previous = DecryptedOtp {
    id: decrypted.id,
    private: DecryptedPrivateData {
        id: decrypted.private.id,
        usage_counter: 0,
        session_counter: 0,
        timestamp: 1000,
        random: [0; 2],
    },
};

assert!(decrypted.validate(&previous).is_ok());

§Notes on validation behavior

otp_offline::otp validates decrypted OTPs using the embedded counters and identifiers:

  • public IDs must match
  • private IDs must match
  • usage counters must not decrease
  • if the usage counter is unchanged, the session counter must increase
  • usage counter 0x7fff is treated as exhausted and rejected

CRC validation happens during decryption of the private 16-byte OTP payload.

§License

MIT

Modules§

modhex
Modhex encoding and decoding functions for YubiKey OTP
otp
YubiKey OTP decryption and validation module
store
OTP store trait