#totp #htop #2fa #secret-key #qr #steamguard

libr2fa

Rust语言的HTOP、TOTP和Steam Guard双因素认证实现

4个版本

0.1.3 2023年6月5日
0.1.2 2023年4月11日
0.1.1 2023年4月10日
0.1.0 2023年4月10日

身份认证类别中排名第407

每月下载量29

MIT许可

69KB
1K SLoC

r2fa

Rust语言的HTOP、TOTP和Steam Guard双因素认证实现。

使用ring 0.16.20,可能与其他版本的ring不兼容。

特性

  • HOTP, TOTP
    • 用户可配置设置
      • 数字
      • 密钥
      • TOTP密钥时间步长(周期)
      • TOTP密钥起始时间(t0)
      • HOTP密钥初始计数器
  • Steam Guard(尚未实现)
    • 验证
    • 代码生成

Cargo特性

qrcode

  • qrcode
    • qrcodegen
    • qrcoderead

qrcode特性默认启用,需要添加default-features = false来禁用默认特性。

或者,可以显式启用qrcodegen特性,该特性用于使用给定的opt认证数据生成二维码。

或者,启用qrcoderead特性,该特性用于使用给定的opt认证二维码读取二维码。

这两个qrcodegenqrcoderead特性都使用了image包,这将大大增加包的大小。

日志

  • 日志

此特性为库提供了日志支持。

待办事项

  • 日志特性
  • Steam Guard
    • 从mafile生成Steam Guard代码
    • Steam登录
    • 添加电话号码到Steam
    • 添加Steam Guard方法
    • 删除Steam Guard方法
    • 确认

用法

手动创建结构体

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();

从URI格式字符串

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

let totp_key1 = otpauth_from_uri("otpauth://totp/ACME%20Co:[email protected]?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:[email protected]".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());

如果给定一个opt认证结构体,也可以将其转换为URI格式字符串。

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();

从URI二维码

首先查看Cargo特性部分。

原始二维码:原始二维码

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:[email protected]".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());

或者,使用给定的opt认证数据生成二维码。

请注意,所有编码后的图像都将为2048x2048。

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:[email protected]".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();

编码后的二维码:encoded qrcode

Steam Guard 代码生成

首先您需要一个 mafile 文件。

关于mafile是什么以及如何获取mafile,请参考 ASF 2FA

它将在 config 文件夹中给您一个 .maFile 文件。

获取Steam Guard代码

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

手机验证API

测试手机号码是否有效并且是VoIP。

主机:store.steampowered.com

端点:/phone/validate

方法:POST

内容类型:application/x-www-form-urlencoded; charset=UTF-8

请求体

  • sessionID : 会话ID
  • phoneNumber: 手机号码

响应: json

响应示例

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

添加手机号码

这是一个多步骤过程。

  1. 首先您将您的 phone number 发送到Steam。
  2. 然后Steam可能会要求您进行 Email Verification
  3. 您点击发送到您邮箱的邮件验证链接。
  4. 您向Steam发送请求,表明您已点击链接。
  5. 然后Steam将向手机号码发送 sms code
  6. 您向Steam发送包含 sms code 的请求。
  7. 完成。

然而,所有这些过程的 hostendpointmethodcontent type 都相同。唯一的区别是 request body

主机:store.steampowered.com

端点:/phone/add_ajaxop

方法:POST

内容类型:application/x-www-form-urlencoded; charset=UTF-8

发送手机号码

请求体

  • op : get_phone_number
  • input : 您的手机号码
  • sessionID : 您的会话ID
  • confirmed : 1
  • checkfortos : 1
  • bisediting : 0
  • token : 0

响应示例

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

如果 stateemail_verification,则表示您可以进行邮件验证。如果 stateget_sms_code,则表示您可以去检查 sms code

邮件验证

请求体

  • op : email_verification
  • input : 空值
  • sessionID : 您的会话ID
  • confirmed : 1
  • checkfortos : 1
  • bisediting : 0
  • token : 0

响应示例

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

如果 stateemail_verification,则表示您可以进行邮件验证。如果 stateget_sms_code,则表示您可以去检查 sms code

短信代码验证

请求体

  • op : get_sms_code
  • input : 您收到的短信代码
  • sessionID : 您的会话ID
  • confirmed : 1
  • checkfortos : 1
  • bisediting : 0
  • token : 0

响应示例

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

如果 statedone,则表示过程已完成。

依赖项

~10–31MB
~497K SLoC