r2fa

Rust implementation for HTOP, TOTP and steam guard tow-factor-authentication.

Use ring 0.16.20, may be incompatible with other version of ring.

Features

Cargo Features

qrcode

The qrcode feature is enabled by default, need to add default-features = false to disable the default feature.

Or, you can enable the qrcodegen feature explicitly which used to generate the qrcode with the given opt auth data.

Or, the qrcoderead feature which used to read the qrcode with the given opt auth qrcode.

Both qrcodegen and qrcoderead feature use the image crate, which will greatly increase the package size.

log

This feature provided log support for the library.

TODO

Usage

Manually Create the Struct

```rust use libr2fa::HOTPKey; use libr2fa::HMACType; use libr2fa::Key;

let mut hotpkey = HOTPKey { key: "MFSWS5LGNBUXKZLBO5TGQ33JO5SWC2DGNF2WCZLIMZUXKZLXMFUGM2LVNFQWK53IMZUXK2A=".tostring(), // SHA1 is the default method, however it is deprecated hmac_type: HMACType::SHA1, ..Default::default() };

let code = hotpkey.getcode().unwrap(); ```

From URI Formate String

```rust use libr2fa::otpauthfromuri; use libr2fa::TOTPKey; use libr2fa::HMACType; use libr2fa::Key;

let totpkey1 = otpauthfromuri("otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA256&digits=7&period=60"); if let Err(err) = totpkey1 { panic!("{}", err); } let mut totpkey1 = totpkey1.unwrap();

let mut totpkey2 = TOTPKey { name: "ACME Co:john.doe@email.com".tostring(), key: "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ".tostring(), digits: 7, timestep: 60, hmactype: HMACType::SHA256, issuer: Some("ACME Co".tostring()), ..Default::default() };

asserteq!(totpkey1.getname(), totpkey2.getname()); asserteq!(totpkey1.gettype(), totpkey2.gettype()); asserteq!(totpkey1.getcode(), totpkey2.get_code()); ```

If given a opt auth struct, it can also be converted to a uri formate string.

```rust use libr2fa::HOTPKey; use libr2fa::HMACType; use libr2fa::Key;

let mut hotpkey = HOTPKey { key: "MFSWS5LGNBUXKZLBO5TGQ33JO5SWC2DGNF2WCZLIMZUXKZLXMFUGM2LVNFQWK53IMZUXK2A=".tostring(), // SHA1 is the default method, however it is deprecated hmac_type: HMACType::SHA1, ..Default::default() };

let uri = hotpkey.geturi(); ```

From URI QRCode

See the Cargo Features part first.

The original qrcode: original qrcode

```rust use libr2fa::otpauthfromuri_qrcode; use libr2fa::TOTPKey; use libr2fa::HMACType; use libr2fa::Key;

let totpkey1 = otpauthfromuriqrcode("public/uriqrcodetest.png"); if let Err(err) = totpkey1 { panic!("{}", err); } let mut totpkey1 = totp_key1.unwrap();

let mut totpkey2 = TOTPKey { name: "ACME Co:john.doe@email.com".tostring(), issuer: Some("ACME Co".tostring()), key: "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ".tostring(), digits: 7, timestep: 60, hmactype: HMACType::SHA256, ..Default::default() };

asserteq!(totpkey1.getname(), totpkey2.getname()); asserteq!(totpkey1.gettype(), totpkey2.gettype()); asserteq!(totpkey1.getcode(), totpkey2.get_code()); ```

Or, generate the qrcode with the given opt auth data.

Note, all encoded image will be 2048x2048.

```rust use libr2fa::otpauthfromuri_qrcode; use libr2fa::TOTPKey; use libr2fa::HMACType; use libr2fa::Key; use libr2fa::OptAuthKey;

let totpkey = TOTPKey { name: "ACME Co:john.doe@email.com".tostring(), issuer: Some("ACME Co".tostring()), key: "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ".tostring(), digits: 7, timestep: 60, hmactype: HMACType::SHA256, ..Default::default() };

let uri = totpkey.touri_struct();

// convert to image::DynamicImage data let img: image::DynamicImage = uri.into();

// Or, save to a path uri.toqrcode("public/uriqrcodeencode_test.png").unwrap(); ```

The encoded qrcode: encoded qrcode

Steam Guard Code Generation

You need to have a mafile first.

On what is a mafile and how to get a mafile, follow ASF 2FA.

It will give you a .maFile at config folder.

Get Steam Guard Code

```rust use libr2fa::SteamKey; use libr2fa::Key; use libr2fa::steam::MaFile;

let mafile = MaFile::fromfile("./public/mafiletest.mafile");

assert!(mafile.is_ok());

let steamkey = SteamKey::frommafile(mafile.unwrap());

assert!(steamkey.isok());

let mut steamkey = steamkey.unwrap();

let code = steamkey.getcode();

assert!(code.is_ok());

let code = code.unwrap();

println!("steam code: {}", code); ```

Steam API

Phone Validate API

Test whether a phone number is valid and is a voip.

Host: store.steampowered.com

Endpoint: /phone/validate

Method: POST

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

Request Body:

Response: json

Response Sample:

json { "success":true, "number":"your phone number", "is_valid":true, "is_voip":false, "is_fixed":false }

Add Phone Number

This is a multi process procedure.

  1. First you send you phone number to steam.
  2. Then it is likely that steam will ask for your Email Verification.
  3. You click the email verification link send to your mailbox.
  4. You send a request to steam says that you have clicked the link.
  5. Then steam will send a sms code to the phone number.
  6. You send a request to steam that contain the sms code.
  7. Done.

However all this process have the same host, endpoint, method and content type. The only difference is the request body.

Host: store.steampowered.com

Endpoint: /phone/add_ajaxop

Method: POST

Content-Type: application/x-www-form-urlencoded; charset=UTF-8

Send the phone number

Request Body:

Response Sample:

json { "success":true, "showResend":false, "state":"email_verification", "errorText":"", "token":"0", "phoneNumber":"your phone number" }

The state is email_verification means you could go for email verification. The state is get_sms_code means you could go for check sms code.

Email Verification

Request Body:

Response Sample:

json { "success":true, "showResend":false, "state":"get_sms_code", "errorText":"", "token":"0", "inputSize":"20", "maxLength":"5" }

The state is email_verification means you could go for email verification. The state is get_sms_code means you could go for check sms code.

SMS Code Verification

Request Body:

Response Sample:

json { "success":true, "showResend":false, "state":"done", "errorText":"", "token":"0", "vac_policy":0, "tos_policy":2, "showDone":true, "maxLength":"5" }

The state is done means the process is done.