Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 3.5.0 #350

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: 2.0

jobs:
ubuntu2004:
ubuntu2204:
docker:
- image: ubuntu:20.04
- image: ubuntu:22.04
steps:
- checkout
- run: apt update && DEBIAN_FRONTEND=noninteractive apt -y install git gcc clang cmake libgcrypt20-dev libgtk-3-dev libzip-dev libjansson-dev libpng-dev libzbar-dev libprotobuf-c-dev libsecret-1-dev uuid-dev libprotobuf-dev libqrencode-dev
Expand Down Expand Up @@ -50,7 +50,7 @@ workflows:
version: 2
build:
jobs:
- ubuntu2004
- ubuntu2204
- ubuntuLatestRolling
- debianLatestStable
- fedoraLatestStable
Expand Down
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/plain [AuthenticatorPro](https://github.com/jamie-mh/AuthenticatorPro) backup
- import and export encrypted/plain [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
6 changes: 4 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ The following list describes whether a version is eligible or not for security u

| Version | Supported | EOL |
|---------|--------------------|-------------|
| 3.4.x | :white_check_mark: | - |
| 3.3.x | :white_check_mark: | 03-Mar-2024 |
| 3.5.x | :white_check_mark: | - |
| 3.4.1 | :white_check_mark: | 31-May-2024 |
| 3.4.0 | :x: | 29-Feb-2024 |
| 3.3.x | :x: | 29-Feb-2024 |
| 3.2.x | :x: | 31-Jan-2024 |
| 3.1.x | :x: | 30-Nov-2023 |
| 3.0.x | :x: | 31-Dec-2022 |
Expand Down
6 changes: 1 addition & 5 deletions data/com.github.paolostivanin.OTPClient.appdata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<metadata_license>CC-BY-4.0</metadata_license>
<project_license>GPL-3.0+</project_license>
<name>OTPClient</name>
<summary>GTK+ application for managing TOTP and HOTP tokens with built-in encryption.</summary>
<summary>Application for managing TOTP/HOTP tokens with built-in encryption</summary>

<keywords>
<keyword>otp</keyword>
Expand Down Expand Up @@ -561,8 +561,4 @@
</description>
</release>
</releases>
<custom>
<value key="Purism::form_factor">workstation</value>
<value key="Purism::form_factor">mobile</value>
</custom>
</component>
2 changes: 1 addition & 1 deletion data/com.github.paolostivanin.OTPClient.desktop
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Type=Application
Exec=otpclient
Icon=com.github.paolostivanin.OTPClient
Keywords=otp;totp;hotp;
Keywords=otp;totp;hotp;2fa
Terminal=false
Name=OTPClient
Comment=GTK+ TOTP and HOTP client
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
2 changes: 1 addition & 1 deletion src/cli/exec-action.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ gboolean exec_action (CmdlineOpts *cmdline_opts,
}
}
exported_file_path = g_build_filename (export_directory, export_pwd != NULL ? "aegis_exports.json.aes" : "aegis_exports.json", NULL);
ret_msg = export_aegis (exported_file_path, db_data->json_data, export_pwd);
ret_msg = export_aegis (exported_file_path, export_pwd, db_data->json_data);
gcry_free (export_pwd);
exported = TRUE;
}
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
Loading