mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 00:34:40 +01:00
parent
e40843c102
commit
733af58e89
14 changed files with 57 additions and 83 deletions
1
migrations/2021-01-23-022153_steam_provider/down.sql
Normal file
1
migrations/2021-01-23-022153_steam_provider/down.sql
Normal file
|
@ -0,0 +1 @@
|
|||
DELETE FROM "providers" WHERE "name"="Steam";
|
1
migrations/2021-01-23-022153_steam_provider/up.sql
Normal file
1
migrations/2021-01-23-022153_steam_provider/up.sql
Normal file
|
@ -0,0 +1 @@
|
|||
INSERT INTO "providers" ("name", "website", "help_url", "digits", "method") VALUES ("Steam", "https://steamcommunity.com/", "https://steamcommunity.com/favicon.ico", 5, "steam");
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{
|
||||
config,
|
||||
helpers::Keyring,
|
||||
models::ProvidersModel,
|
||||
models::{Keyring, ProvidersModel},
|
||||
widgets::{PreferencesWindow, ProvidersDialog, Window},
|
||||
};
|
||||
use gettextrs::gettext;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
mod keyring;
|
||||
pub mod qrcode;
|
||||
|
||||
pub use keyring::Keyring;
|
|
@ -1,50 +0,0 @@
|
|||
use crate::models::OTPUri;
|
||||
use anyhow::Result;
|
||||
use ashpd::{
|
||||
desktop::screenshot::{Screenshot, ScreenshotOptions, ScreenshotProxy},
|
||||
zbus, RequestProxy, Response, WindowIdentifier,
|
||||
};
|
||||
use gtk::{gio, prelude::*};
|
||||
use image::GenericImageView;
|
||||
use std::str::FromStr;
|
||||
use zbar_rust::ZBarImageScanner;
|
||||
|
||||
pub(crate) fn scan(screenshot: &gio::File) -> Result<OTPUri> {
|
||||
let (data, _) = screenshot.load_contents(gio::NONE_CANCELLABLE)?;
|
||||
|
||||
let img = image::load_from_memory(&data)?;
|
||||
|
||||
let (width, height) = img.dimensions();
|
||||
let img_data: Vec<u8> = img.to_luma8().to_vec();
|
||||
|
||||
let mut scanner = ZBarImageScanner::new();
|
||||
|
||||
let results = scanner
|
||||
.scan_y800(&img_data, width, height)
|
||||
.map_err(|e| anyhow::format_err!(e))?;
|
||||
|
||||
if let Some(ref result) = results.get(0) {
|
||||
let uri = String::from_utf8(result.data.clone())?;
|
||||
return Ok(OTPUri::from_str(&uri)?);
|
||||
}
|
||||
anyhow::bail!("Invalid QR code")
|
||||
}
|
||||
|
||||
pub(crate) fn screenshot_area<F: FnOnce(gio::File)>(
|
||||
window: gtk::Window,
|
||||
callback: F,
|
||||
) -> Result<()> {
|
||||
let connection = zbus::Connection::new_session()?;
|
||||
let proxy = ScreenshotProxy::new(&connection)?;
|
||||
let handle = proxy.screenshot(
|
||||
WindowIdentifier::from(window),
|
||||
ScreenshotOptions::default().interactive(true).modal(true),
|
||||
)?;
|
||||
let request = RequestProxy::new(&connection, &handle)?;
|
||||
request.on_response(move |response: Response<Screenshot>| {
|
||||
if let Ok(screenshot) = response {
|
||||
callback(gio::File::new_for_uri(&screenshot.uri));
|
||||
}
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
|
@ -9,7 +9,6 @@ use gettextrs::*;
|
|||
mod application;
|
||||
mod backup;
|
||||
mod config;
|
||||
mod helpers;
|
||||
mod models;
|
||||
mod schema;
|
||||
mod static_resources;
|
||||
|
|
|
@ -42,17 +42,15 @@ sources = files(
|
|||
'backup/freeotp.rs',
|
||||
'backup/legacy.rs',
|
||||
'backup/mod.rs',
|
||||
'helpers/database.rs',
|
||||
'helpers/keyring.rs',
|
||||
'helpers/mod.rs',
|
||||
'helpers/qrcode.rs',
|
||||
'models/account_sorter.rs',
|
||||
'models/account.rs',
|
||||
'models/accounts.rs',
|
||||
'models/algorithm.rs',
|
||||
'models/database.rs',
|
||||
'models/favicon.rs',
|
||||
'models/keyring.rs',
|
||||
'models/mod.rs',
|
||||
'models/otp.rs',
|
||||
'models/otp_uri.rs',
|
||||
'models/provider_sorter.rs',
|
||||
'models/provider.rs',
|
||||
|
|
|
@ -3,8 +3,7 @@ use super::{
|
|||
OTPMethod, OTPUri,
|
||||
};
|
||||
use crate::{
|
||||
helpers::Keyring,
|
||||
models::{database, otp},
|
||||
models::{database, otp, Keyring},
|
||||
schema::accounts,
|
||||
widgets::QRCodeData,
|
||||
};
|
||||
|
@ -268,7 +267,7 @@ impl Account {
|
|||
let provider = self.provider();
|
||||
|
||||
let counter = match provider.method() {
|
||||
OTPMethod::TOTP => {
|
||||
OTPMethod::TOTP | OTPMethod::Steam => {
|
||||
let timestamp = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
|
@ -282,21 +281,30 @@ impl Account {
|
|||
}
|
||||
old_counter as u64
|
||||
}
|
||||
OTPMethod::Steam => 1,
|
||||
};
|
||||
|
||||
let label = match otp::hotp(
|
||||
let otp_password: Result<String> = match provider.method() {
|
||||
OTPMethod::Steam => otp::steam(&self.token()),
|
||||
_ => {
|
||||
let token = otp::hotp(
|
||||
&self.token(),
|
||||
counter,
|
||||
provider.algorithm().into(),
|
||||
provider.digits() as u32,
|
||||
) {
|
||||
Ok(otp) => otp::format(otp, provider.digits() as usize),
|
||||
);
|
||||
|
||||
token.map(|d| otp::format(d, provider.digits() as usize))
|
||||
}
|
||||
};
|
||||
|
||||
let label = match otp_password {
|
||||
Ok(password) => password,
|
||||
Err(err) => {
|
||||
debug!("Could not generate HOTP {:?}", err);
|
||||
warn!("Failed to generate the OTP {}", err);
|
||||
"Error".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
self.set_property("otp", &label).unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use once_cell::sync::Lazy;
|
||||
|
||||
mod account;
|
||||
mod account_sorter;
|
||||
mod accounts;
|
||||
mod algorithm;
|
||||
pub mod database;
|
||||
mod favicon;
|
||||
mod keyring;
|
||||
pub mod otp;
|
||||
mod otp_uri;
|
||||
mod provider;
|
||||
|
@ -21,6 +21,7 @@ pub use self::{
|
|||
accounts::AccountsModel,
|
||||
algorithm::{Algorithm, OTPMethod},
|
||||
favicon::{FaviconError, FaviconScrapper},
|
||||
keyring::Keyring,
|
||||
otp_uri::OTPUri,
|
||||
provider::Provider,
|
||||
provider_sorter::ProviderSorter,
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
use super::Algorithm;
|
||||
use anyhow::Result;
|
||||
use data_encoding::BASE32_NOPAD;
|
||||
use ring::hmac;
|
||||
use std::convert::TryInto;
|
||||
|
||||
static STEAM_CHARS: &str = "23456789BCDFGHJKMNPQRTVWXY";
|
||||
static STEAM_DEFAULT_COUNTER: u64 = 30;
|
||||
static STEAM_DEFAULT_DIGITS: u32 = 5;
|
||||
|
||||
/// Code graciously taken from the rust-top crate.
|
||||
/// https://github.com/TimDumol/rust-otp/blob/master/src/lib.rs
|
||||
|
||||
|
@ -20,7 +25,7 @@ fn calc_digest(decoded_secret: &[u8], counter: u64, algorithm: hmac::Algorithm)
|
|||
}
|
||||
|
||||
/// Encodes the HMAC digest into a n-digit integer.
|
||||
fn encode_digest(digest: &[u8], digits: u32) -> Result<u32> {
|
||||
fn encode_digest(digest: &[u8]) -> Result<u32> {
|
||||
let offset = match digest.last() {
|
||||
Some(x) => *x & 0xf,
|
||||
None => anyhow::bail!("Invalid digest"),
|
||||
|
@ -30,7 +35,7 @@ fn encode_digest(digest: &[u8], digits: u32) -> Result<u32> {
|
|||
Err(_) => anyhow::bail!("Invalid digest"),
|
||||
};
|
||||
let code = u32::from_be_bytes(code_bytes);
|
||||
Ok((code & 0x7fffffff) % 10_u32.pow(digits))
|
||||
Ok(code & 0x7fffffff)
|
||||
}
|
||||
|
||||
/// Performs the [HMAC-based One-time Password Algorithm](http://en.wikipedia.org/wiki/HMAC-based_One-time_Password_Algorithm)
|
||||
|
@ -42,10 +47,27 @@ pub(crate) fn hotp(
|
|||
digits: u32,
|
||||
) -> Result<u32> {
|
||||
let decoded = decode_secret(secret)?;
|
||||
encode_digest(
|
||||
calc_digest(decoded.as_slice(), counter, algorithm).as_ref(),
|
||||
digits,
|
||||
)
|
||||
let digest = encode_digest(calc_digest(decoded.as_slice(), counter, algorithm).as_ref())?;
|
||||
Ok(digest % 10_u32.pow(digits))
|
||||
}
|
||||
|
||||
pub(crate) fn steam(secret: &str) -> Result<String> {
|
||||
let mut token = hotp(
|
||||
secret,
|
||||
STEAM_DEFAULT_COUNTER,
|
||||
Algorithm::SHA1.into(),
|
||||
STEAM_DEFAULT_DIGITS,
|
||||
)?;
|
||||
let mut code = String::new();
|
||||
let total_chars = STEAM_CHARS.len() as u32;
|
||||
for i in 0..5 {
|
||||
let pos = token % total_chars;
|
||||
println!("{:#?}", pos);
|
||||
let charachter = STEAM_CHARS.chars().nth(pos as usize).unwrap();
|
||||
code.push(charachter);
|
||||
token = token / total_chars;
|
||||
}
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
pub(crate) fn format(code: u32, digits: usize) -> String {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{config, helpers::Keyring};
|
||||
use crate::{config, models::Keyring};
|
||||
use glib::clone;
|
||||
use gtk::{gio, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
|
||||
use gtk_macros::{action, get_action};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{
|
||||
application::Application,
|
||||
config,
|
||||
helpers::Keyring,
|
||||
models::{Account, ProvidersModel},
|
||||
models::{Account, Keyring, ProvidersModel},
|
||||
widgets::{accounts::AccountDetailsPage, providers::ProvidersList, AccountAddDialog},
|
||||
window_state,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue