libr2fa 0.1.3

rust implementation for HTOP, TOTP and steam guard tow-factor-authentication
Documentation
# r2fa

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

Use [`ring`](https://crates.io/crates/ring) `0.16.20`,
may be incompatible with other version of `ring`.

## Features

- HOTP, TOTP
  - user configurable settings
    - digits
    - secret key
    - TOTP Key time step (period)
    - TOTP Key start time (t0)
    - HOTP Key initial counter
- steam guard (not implemented yet)
  - verification
  - code generate

## Cargo Features

### qrcode

- `qrcode`
  - `qrcodegen`
  - `qrcoderead`

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`](https://crates.io/crates/image)
crate, which will greatly increase the package size.

### log

- `log`

This feature provided log support for the library.

## TODO

- [x] log feature
- [ ] steam guard
  - [x] generate steam guard code from mafile
  - [ ] steam login
  - [ ] add phone number to steam
  - [ ] add steam guard method
  - [ ] remove steam guard method
  - [ ] confirmations

## Usage

### Manually Create the Struct

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

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

let code = hotp_key.get_code().unwrap();
```

### From URI Formate String

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

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

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

assert_eq!(totp_key1.get_name(), totp_key2.get_name());
assert_eq!(totp_key1.get_type(), totp_key2.get_type());
assert_eq!(totp_key1.get_code(), totp_key2.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 hotp_key = HOTPKey {
    key: "MFSWS5LGNBUXKZLBO5TGQ33JO5SWC2DGNF2WCZLIMZUXKZLXMFUGM2LVNFQWK53IMZUXK2A=".to_string(),
    // SHA1 is the default method, however it is deprecated
    hmac_type: HMACType::SHA1,
    ..Default::default()
};

let uri = hotp_key.get_uri();
```

### From URI QRCode

See the [Cargo Features](#cargo-features) part first.

The original qrcode:
![original qrcode](public/uri_qrcode_test.png)

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

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

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

assert_eq!(totp_key1.get_name(), totp_key2.get_name());
assert_eq!(totp_key1.get_type(), totp_key2.get_type());
assert_eq!(totp_key1.get_code(), totp_key2.get_code());
```

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

Note, all encoded image will be 2048x2048.

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

let totp_key = TOTPKey {
    name: "ACME Co:john.doe@email.com".to_string(),
    issuer: Some("ACME Co".to_string()),
    key: "HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ".to_string(),
    digits: 7,
    time_step: 60,
    hmac_type: HMACType::SHA256,
    ..Default::default()
};

let uri = totp_key.to_uri_struct();

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


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

The encoded qrcode:
![encoded qrcode](public/uri_qrcode_encode_test.png)

### 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](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Two-factor-authentication#user-content-creation).

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::from_file("./public/mafile_test.mafile");

assert!(mafile.is_ok());

let steam_key = SteamKey::from_mafile(mafile.unwrap());

assert!(steam_key.is_ok());

let mut steam_key = steam_key.unwrap();

let code = steam_key.get_code();

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:

- `sessionID` : session id
- `phoneNumber`: phone number

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:

- `op` : `get_phone_number`
- `input` : your phone number
- `sessionID` : your session id
- `confirmed` : `1`
- `checkfortos` : `1`
- `bisediting` : `0`
- `token` : `0`

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:

- `op` : `email_verification`
- `input` : empty
- `sessionID` : your session id
- `confirmed` : `1`
- `checkfortos` : `1`
- `bisediting` : `0`
- `token` : `0`

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:

- `op` : `get_sms_code`
- `input` : sms code you receive
- `sessionID` : your session id
- `confirmed` : `1`
- `checkfortos` : `1`
- `bisediting` : `0`
- `token` : `0`

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.