Skip to content

Commit

Permalink
Add new providers
Browse files Browse the repository at this point in the history
* import/export plain/encrypted AuthenticatorPro backups
* import/export plain/encrypted 2FAS backups
* drop support for older glib and cleanup code
  • Loading branch information
paolostivanin committed Mar 1, 2024
1 parent ebb2b11 commit 1383208
Show file tree
Hide file tree
Showing 20 changed files with 1,263 additions and 276 deletions.
16 changes: 10 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(OTPClient VERSION "3.4.1" LANGUAGES "C")
project(OTPClient VERSION "3.5.0" LANGUAGES "C")
include(GNUInstallDirs)

configure_file("src/common/version.h.in" "version.h")
Expand Down Expand Up @@ -44,14 +44,14 @@ endif()

find_package(PkgConfig REQUIRED)
find_package(Protobuf 3.6.0 REQUIRED)
find_package(Gcrypt 1.8.0 REQUIRED)
find_package(Gcrypt 1.10.1 REQUIRED)
pkg_check_modules(COTP REQUIRED cotp>=3.0.0)
pkg_check_modules(PNG REQUIRED libpng>=1.6.30)
pkg_check_modules(JANSSON REQUIRED jansson>=2.12)
pkg_check_modules(ZBAR REQUIRED zbar>=0.20)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0>=3.24.0)
pkg_check_modules(GLIB2 REQUIRED glib-2.0>=2.64.0)
pkg_check_modules(GIO REQUIRED gio-2.0>=2.64.0)
pkg_check_modules(GLIB2 REQUIRED glib-2.0>=2.68.0)
pkg_check_modules(GIO REQUIRED gio-2.0>=2.68.0)
pkg_check_modules(UUID REQUIRED uuid>=2.34.0)
pkg_check_modules(PROTOC REQUIRED libprotobuf-c>=1.3.0)
pkg_check_modules(LIBSECRET REQUIRED libsecret-1>=0.20.0)
Expand Down Expand Up @@ -130,7 +130,9 @@ set(GUI_SOURCE_FILES
src/show-qr-cb.c
src/setup-signals-shortcuts.c
src/change-pwd-cb.c
src/dbinfo-cb.c)
src/dbinfo-cb.c
src/common/twofas.c
src/common/authpro.c)

set(CLI_HEADER_FILES
src/cli/get-data.h
Expand Down Expand Up @@ -159,7 +161,9 @@ set(CLI_SOURCE_FILES
src/common/aegis.c
src/common/freeotp.c
src/secret-schema.c
src/google-migration.pb-c.c)
src/google-migration.pb-c.c
src/common/twofas.c
src/common/authpro.c)

if(BUILD_GUI AND BUILD_CLI)
list(APPEND CLI_SOURCE_FILES
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Highly secure and easy to use GTK+ software for two-factor authentication that s
| Name | Min Version |
|----------------------------------------------------|-------------|
| GTK+ | 3.24 |
| Glib | 2.64.0 |
| Glib | 2.68.0 |
| jansson | 2.12 |
| libgcrypt | 1.8.0 |
| libgcrypt | 1.10.1 |
| libpng | 1.6.30 |
| [libcotp](https://github.com/paolostivanin/libcotp) | 3.0.0 |
| zbar | 0.20 |
Expand All @@ -38,6 +38,8 @@ See this [wiki section](https://github.com/paolostivanin/OTPClient/wiki/Secure-M
- import and export encrypted/plain [andOTP](https://github.com/flocke/andOTP) backup
- import and export encrypted/plain [Aegis](https://github.com/beemdevelopment/Aegis) backup
- import and export plain [FreeOTPPlus](https://github.com/helloworld1/FreeOTPPlus) backup (key URI format only)
- import and export encrypted [AuthenticatorPro](https://github.com/jamie-mh/AuthenticatorPro) backup
- import and export encrypted [2FAS](https://github.com/twofas) backup
- import of Google's migration QR codes
- local database is encrypted using AES256-GCM
- key is derived using PBKDF2 with SHA512 and 100k iterations
Expand Down
8 changes: 8 additions & 0 deletions src/app.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,11 +536,19 @@ set_action_group (GtkBuilder *builder,
{ .name = FREEOTPPLUS_IMPORT_ACTION_NAME, .activate = select_file_cb },
{ .name = AEGIS_IMPORT_ACTION_NAME, .activate = select_file_cb },
{ .name = AEGIS_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb },
{ .name = AUTHPRO_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb },
{ .name = AUTHPRO_IMPORT_PLAIN_ACTION_NAME, .activate = select_file_cb },
{ .name = TWOFAS_IMPORT_ENC_ACTION_NAME, .activate = select_file_cb },
{ .name = TWOFAS_IMPORT_PLAIN_ACTION_NAME, .activate = select_file_cb },
{ .name = ANDOTP_EXPORT_ACTION_NAME, .activate = export_data_cb },
{ .name = ANDOTP_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb },
{ .name = FREEOTPPLUS_EXPORT_ACTION_NAME, .activate = export_data_cb },
{ .name = AEGIS_EXPORT_ACTION_NAME, .activate = export_data_cb },
{ .name = AEGIS_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb },
{ .name = AUTHPRO_EXPORT_ENC_ACTION_NAME, .activate = export_data_cb },
{ .name = AUTHPRO_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb },
{ .name = TWOFAS_EXPORT_ENC_ACTION_NAME, .activate = export_data_cb },
{ .name = TWOFAS_EXPORT_PLAIN_ACTION_NAME, .activate = export_data_cb },
{ .name = GOOGLE_MIGRATION_FILE_ACTION_NAME, .activate = add_qr_from_file },
{ .name = GOOGLE_MIGRATION_WEBCAM_ACTION_NAME, .activate = webcam_add_cb },
{ .name = "create_newdb", .activate = new_db_cb },
Expand Down
8 changes: 0 additions & 8 deletions src/cli/get-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,13 @@ show_token (DatabaseData *db_data,

// Translators: please do not translate 'account'
GString *msg = g_string_new (_("Given account: %s"));
#if GLIB_CHECK_VERSION(2, 68, 0)
g_string_replace (msg, "%s", account != NULL ? account : "<none>", 0);
#else
g_string_replace_backported (msg, "%s", account != NULL ? account : "<none>", 0);
#endif
g_printerr ("%s\n", msg->str);
g_string_free (msg, TRUE);

// Translators: please do not translate 'issuer'
msg = g_string_new (_("Given issuer: %s"));
#if GLIB_CHECK_VERSION(2, 68, 0)
g_string_replace (msg, "%s", issuer != NULL ? issuer : "<none>", 0);
#else
g_string_replace_backported (msg, "%s", issuer != NULL ? issuer : "<none>", 0);
#endif
g_printerr ("%s\n", msg->str);
g_string_free (msg, TRUE);

Expand Down
45 changes: 23 additions & 22 deletions src/common/aegis.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,22 @@ static GSList *get_otps_from_encrypted_backup (const gchar *path,
gint32 max_file_size,
GError **err);

static GSList *parse_json_data (const gchar *data,
static GSList *parse_aegis_json_data (const gchar *data,
GError **err);


GSList *
get_aegis_data (const gchar *path,
const gchar *password,
gint32 max_file_size,
gboolean encrypted,
GError **err)
{
if (g_file_test (path, G_FILE_TEST_IS_SYMLINK | G_FILE_TEST_IS_DIR) ) {
g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "Selected file is either a symlink or a directory.");
return NULL;
}

return (encrypted == TRUE) ? get_otps_from_encrypted_backup(path, password, max_file_size, err) : get_otps_from_plain_backup(path, err);
return (password != NULL) ? get_otps_from_encrypted_backup (path, password, max_file_size, err) : get_otps_from_plain_backup (path, err);
}


Expand All @@ -53,8 +52,8 @@ get_otps_from_plain_backup (const gchar *path,
return NULL;
}

gchar *dumped_json = json_dumps(json_object_get (json, "db"), 0);
GSList *otps = parse_json_data (dumped_json, err);
gchar *dumped_json = json_dumps (json_object_get (json, "db"), 0);
GSList *otps = parse_aegis_json_data (dumped_json, err);
gcry_free (dumped_json);

return otps;
Expand Down Expand Up @@ -204,7 +203,7 @@ get_otps_from_encrypted_backup (const gchar *path,
g_regex_unref (regex);
gcry_free (decrypted_db);

GSList *otps = parse_json_data (cleaned_db, err);
GSList *otps = parse_aegis_json_data (cleaned_db, err);
gcry_free (cleaned_db);

return otps;
Expand All @@ -213,8 +212,8 @@ get_otps_from_encrypted_backup (const gchar *path,

gchar *
export_aegis (const gchar *export_path,
json_t *json_db_data,
const gchar *password)
const gchar *password,
json_t *json_db_data)
{
GError *err = NULL;
json_t *root = json_object ();
Expand Down Expand Up @@ -422,8 +421,8 @@ export_aegis (const gchar *export_path,


static GSList *
parse_json_data (const gchar *data,
GError **err)
parse_aegis_json_data (const gchar *data,
GError **err)
{
json_error_t jerr;
json_t *root = json_loads (data, JSON_DISABLE_EOF_CHECK, &jerr);
Expand Down Expand Up @@ -451,6 +450,7 @@ parse_json_data (const gchar *data,
otp->secret = secure_strdup (json_string_value (json_object_get (info_obj, "secret")));
otp->digits = (guint32) json_integer_value (json_object_get(info_obj, "digits"));

gboolean skip = FALSE;
const gchar *type = json_string_value (json_object_get (obj, "type"));
if (g_ascii_strcasecmp (type, "TOTP") == 0) {
otp->type = g_strdup (type);
Expand All @@ -468,11 +468,8 @@ parse_json_data (const gchar *data,
g_free (otp->issuer);
otp->issuer = g_strdup ("Steam");
} else {
g_set_error (err, generic_error_gquark (), GENERIC_ERRCODE, "otp type is neither TOTP nor HOTP");
gcry_free (otp->secret);
g_free (otp);
json_decref (obj);
return NULL;
g_printerr ("Skipping token due to unsupported type: %s\n", type);
skip = TRUE;
}

const gchar *algo = json_string_value (json_object_get (info_obj, "algo"));
Expand All @@ -481,16 +478,20 @@ parse_json_data (const gchar *data,
g_ascii_strcasecmp (algo, "SHA512") == 0) {
otp->algo = g_ascii_strup (algo, -1);
} else {
g_printerr ("algo not supported (must be either one of: sha1, sha256 or sha512\n");
g_printerr ("Skipping token due to unsupported algo: %s\n", algo);
skip = TRUE;
}

if (!skip) {
otps = g_slist_append (otps, otp);
} else {
gcry_free (otp->secret);
g_free (otp->issuer);
g_free (otp->account_name);
g_free (otp->algo);
g_free (otp->type);
g_free (otp);
json_decref (obj);
json_decref (info_obj);
return NULL;
}

otps = g_slist_append (otps, g_memdupX (otp, sizeof (otp_t)));
g_free (otp);
}

json_decref (root);
Expand Down
Loading

0 comments on commit 1383208

Please sign in to comment.