[gnome-disk-utility] Add TrueCrypt/VeraCrypt support



commit 30422ec7cc0e86e9255aedaffe697d33a045d9d8
Author: segfault <segfault riseup net>
Date:   Tue Oct 10 02:33:15 2017 +0200

    Add TrueCrypt/VeraCrypt support
    
    This is a combination of 4 commits:
    
    - Disable "Encryption Options" for non-LUKS devices
    
    - Disable "Change Passphrase" for non-LUKS devices
    
    - Rename volume-content-luks to volume-content-crypto
    
    - Add TCRYPT option widgets to the unlock dialog
    
    This extends the unlock dialog by widgets which allow specifying the
    parameters supported by TrueCrypt and VeraCrypt volumes. This includes:
    
     - Whether the volume to be unlocked is hidden.
     - Whether the volume to be unlocked is a system partition.
       Note: TrueCrypt and VeraCrypt only support encrypting Windows system
       partitions [1], so the label for this option is "Windows system".
     - Whether to use a PIM [2].
     - Whether to use one or multiple keyfiles. In the beginning there is
       only one button to choose a single keyfile. When a keyfile is chosen,
       another button appears below to allow selecting another keyfile, and
       so on.
    
    Since TCRYPT volumes cannot be reliably detected as such, a label is
    displayed at the top of the unlock dialog to indicate to the user that
    this volume might not actually be encrypted.
    
    [1] https://www.veracrypt.fr/en/System%20Encryption.html
    [2] https://www.veracrypt.fr/en/Header%20Key%20Derivation.html

 src/disks/gduunlockdialog.c          | 179 +++++++++++++++++++++++++++++++++--
 src/disks/gduwindow.c                |   8 +-
 src/disks/ui/unlock-device-dialog.ui | 144 +++++++++++++++++++++++++++-
 3 files changed, 316 insertions(+), 15 deletions(-)
---
diff --git a/src/disks/gduunlockdialog.c b/src/disks/gduunlockdialog.c
index bbb34b5e..71af48e7 100644
--- a/src/disks/gduunlockdialog.c
+++ b/src/disks/gduunlockdialog.c
@@ -11,6 +11,8 @@
 
 #include <glib/gi18n.h>
 #include <libsecret/secret.h>
+#include <stdlib.h>
+#include <errno.h>
 
 #include "gduapplication.h"
 #include "gduwindow.h"
@@ -35,16 +37,28 @@ typedef struct
   UDisksObject *object;
   UDisksBlock *block;
   UDisksEncrypted *encrypted;
+  gchar* type;
 
   GduWindow *window;
   GtkBuilder *builder;
 
   GtkWidget *dialog;
+  GtkWidget *unlock_button;
   GtkWidget *infobar_vbox;
-  GtkWidget *entry;
+  GtkWidget *passphrase_entry;
   GtkWidget *show_passphrase_check_button;
+  GtkWidget *tcrypt_hidden_check_button;
+  GtkWidget *tcrypt_system_check_button;
+  GtkWidget *pim_entry;
+
+  GList *tcrypt_keyfile_button_list;
 
   gchar *passphrase;
+  GVariant *keyfiles;
+  guint num_keyfiles;
+  guint pim;
+  gboolean hidden_volume;
+  gboolean system_volume;
 } DialogData;
 
 static void
@@ -62,10 +76,57 @@ dialog_data_free (DialogData *data)
   if (data->builder != NULL)
     g_object_unref (data->builder);
 
+  if (g_strcmp0 (data->type, "crypto_TCRYPT") == 0 || g_strcmp0 (data->type, "crypto_unknown") == 0)
+    // The elements are already freed by the builder / parent widget
+    g_list_free (data->tcrypt_keyfile_button_list);
+
+  g_free (data->type);
   g_free (data->passphrase);
   g_free (data);
 }
 
+static void
+update_unlock_button_sensitivity (GObject *object,
+                                  gpointer user_data)
+{
+  DialogData *data = user_data;
+  gboolean password_entered = gtk_entry_get_text_length (GTK_ENTRY (data->passphrase_entry)) > 0;
+  gboolean keyfile_chosen = (data->tcrypt_keyfile_button_list != NULL &&
+                             g_list_length (data->tcrypt_keyfile_button_list) > 1);
+
+  gtk_widget_set_sensitive (data->unlock_button, password_entered || keyfile_chosen);
+}
+
+static void
+on_tcrypt_keyfile_set (GObject     *object,
+                       gpointer     user_data)
+{
+  DialogData *data = user_data;
+  GtkWidget *button;
+  GtkWidget *sibling;
+  GtkGrid *grid;
+  GList *list;
+
+  grid = GTK_GRID (gtk_builder_get_object (data->builder, "unlock-device-grid"));
+
+  button = gtk_file_chooser_button_new (_("Select a Keyfile"), GTK_FILE_CHOOSER_ACTION_OPEN);
+
+  sibling = NULL;
+  for (list = data->tcrypt_keyfile_button_list; list != NULL; list = list->next)
+    sibling = (GTK_WIDGET (list->data));
+
+  gtk_grid_attach_next_to (grid, button, sibling, GTK_POS_BOTTOM, 1, 1);
+  gtk_widget_show (button);
+
+  data->tcrypt_keyfile_button_list = g_list_append (data->tcrypt_keyfile_button_list, button);
+  g_signal_connect (button, "file-set", G_CALLBACK (on_tcrypt_keyfile_set), data);
+
+  // Don't call this function again for this instance
+  g_signal_handlers_disconnect_by_func (object, on_tcrypt_keyfile_set, user_data);
+
+  update_unlock_button_sensitivity (object, user_data);
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
@@ -83,7 +144,7 @@ unlock_cb (UDisksEncrypted *encrypted,
                                             &error))
     {
       gdu_utils_show_error (GTK_WINDOW (data->window),
-                            _("Error unlocking encrypted device"),
+                            _("Error unlocking device"),
                             error);
       g_error_free (error);
     }
@@ -93,9 +154,20 @@ unlock_cb (UDisksEncrypted *encrypted,
 static void
 do_unlock (DialogData *data)
 {
+  GVariantBuilder options_builder;
+  g_variant_builder_init (&options_builder, G_VARIANT_TYPE_VARDICT);
+  if (data->hidden_volume)
+    g_variant_builder_add (&options_builder, "{sv}", "hidden", g_variant_new_boolean (TRUE));
+  if (data->system_volume)
+    g_variant_builder_add (&options_builder, "{sv}", "system", g_variant_new_boolean (TRUE));
+  if (data->pim != 0)
+    g_variant_builder_add (&options_builder, "{sv}", "pim", g_variant_new_uint32 (data->pim));
+  if (data->num_keyfiles != 0)
+    g_variant_builder_add (&options_builder, "{sv}", "keyfiles", data->keyfiles);
+
   udisks_encrypted_call_unlock (data->encrypted,
                                 data->passphrase,
-                                g_variant_new ("a{sv}", NULL), /* options */
+                                g_variant_builder_end (&options_builder),
                                 NULL, /* cancellable */
                                 (GAsyncReadyCallback) unlock_cb,
                                 data);
@@ -105,15 +177,61 @@ static void
 show_dialog (DialogData *data)
 {
   gint response;
+  gchar *err;
+  GError *error;
+  GVariantBuilder *keyfiles_builder;
+  GList *list;
+  const char* filename;
+  const char* text_pim;
+  guint64 tmp_pim;
 
   gtk_widget_show_all (data->dialog);
-  gtk_widget_grab_focus (data->entry);
+  gtk_widget_grab_focus (data->passphrase_entry);
 
   response = gtk_dialog_run (GTK_DIALOG (data->dialog));
   if (response == GTK_RESPONSE_OK)
     {
       gtk_widget_hide (data->dialog);
-      data->passphrase = g_strdup (gtk_entry_get_text (GTK_ENTRY (data->entry)));
+      data->passphrase = g_strdup (gtk_entry_get_text (GTK_ENTRY (data->passphrase_entry)));
+
+      if (g_strcmp0 (data->type, "crypto_TCRYPT") == 0 || g_strcmp0 (data->type, "crypto_unknown") == 0)
+        {
+          data->hidden_volume = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(data->tcrypt_hidden_check_button));
+          data->system_volume = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON 
(data->tcrypt_system_check_button));
+
+          // Add keyfiles
+          data->num_keyfiles = 0;
+          keyfiles_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
+          for (list = data->tcrypt_keyfile_button_list; list != NULL; list = list->next)
+            {
+              filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (list->data));
+              if (filename == NULL)
+                continue;
+              g_variant_builder_add (keyfiles_builder, "s", filename);
+              data->num_keyfiles += 1;
+            }
+          data->keyfiles = g_variant_new ("as", keyfiles_builder);
+          g_variant_builder_unref (keyfiles_builder);
+
+          text_pim = gtk_entry_get_text (GTK_ENTRY (data->pim_entry));
+          if (text_pim && strlen (text_pim) > 0)
+            {
+              errno = 0;
+              tmp_pim = strtoul ( text_pim, &err, 10);
+              if (*err || errno || tmp_pim <= 0 || tmp_pim > G_MAXUINT32)
+                {
+                  error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+                                      _("Invalid PIM"));
+                  gdu_utils_show_error (GTK_WINDOW(data->window),
+                                       _("Error unlocking device"),
+                                       error);
+                  g_error_free (error);
+                  dialog_data_free (data);
+                  return;
+                }
+              data->pim = tmp_pim;
+            }
+        }
       do_unlock (data);
     }
   else
@@ -143,7 +261,7 @@ luks_find_passphrase_cb (GObject      *source,
                                            _("The encryption passphrase was retrieved from the keyring"),
                                            NULL);
       gtk_box_pack_start (GTK_BOX (data->infobar_vbox), infobar, TRUE, TRUE, 0);
-      gtk_entry_set_text (GTK_ENTRY (data->entry), passphrase);
+      gtk_entry_set_text (GTK_ENTRY (data->passphrase_entry), passphrase);
     }
   else
     {
@@ -166,24 +284,65 @@ gdu_unlock_dialog_show (GduWindow    *window,
   data->block = udisks_object_peek_block (object);
   data->encrypted = udisks_object_peek_encrypted (object);
   data->window = g_object_ref (window);
-
+  data->type = udisks_block_dup_id_type (data->block);
   data->dialog = GTK_WIDGET (gdu_application_new_widget (gdu_window_get_application (window),
                                                          "unlock-device-dialog.ui",
                                                          "unlock-device-dialog",
                                                          &data->builder));
+  data->unlock_button = GTK_WIDGET (gtk_builder_get_object (data->builder, "unlock-device-unlock-button"));
   data->infobar_vbox = GTK_WIDGET (gtk_builder_get_object (data->builder, "infobar-vbox"));
-  data->entry = GTK_WIDGET (gtk_builder_get_object (data->builder, "unlock-device-passphrase-entry"));
+  data->passphrase_entry = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-passphrase-entry"));
   data->show_passphrase_check_button = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-show-passphrase-check-button"));
 
+  // Add TCRYPT options if the device is (possibly) a TCRYPT volume
+  if (g_strcmp0 (data->type, "crypto_TCRYPT") == 0 || g_strcmp0 (data->type, "crypto_unknown") == 0)
+    {
+      GtkWidget *volume_type_label = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-volume-type-label"));
+      GtkWidget *keyfiles_label = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-keyfiles-label"));
+      GtkWidget *pim_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "unlock-device-pim-label"));
+      GtkWidget *button = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-tcrypt-keyfile-chooser-button"));
+
+      gtk_window_set_title (GTK_WINDOW (data->dialog), _("Set options to unlock"));
+
+      data->tcrypt_hidden_check_button = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-tcrypt-hidden-check-button"));
+      data->tcrypt_system_check_button = GTK_WIDGET (gtk_builder_get_object (data->builder, 
"unlock-device-tcrypt-system-check-button"));
+      data->pim_entry = GTK_WIDGET (gtk_builder_get_object (data->builder, "unlock-device-pim-entry"));
+      data->tcrypt_keyfile_button_list = NULL;
+      data->tcrypt_keyfile_button_list = g_list_append (data->tcrypt_keyfile_button_list, button);
+
+      gtk_widget_set_visible (volume_type_label, TRUE);
+      gtk_widget_set_visible (pim_label, TRUE);
+      gtk_widget_set_visible (keyfiles_label, TRUE);
+      gtk_widget_set_visible (button, TRUE);
+      gtk_widget_set_visible (data->tcrypt_hidden_check_button, TRUE);
+      gtk_widget_set_visible (data->tcrypt_system_check_button, TRUE);
+      gtk_widget_set_visible (data->pim_entry, TRUE);
+
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->tcrypt_hidden_check_button), FALSE);
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->tcrypt_system_check_button), FALSE);
+      gtk_entry_set_text (GTK_ENTRY (data->pim_entry), "");
+
+      g_signal_connect (button, "file-set", G_CALLBACK (on_tcrypt_keyfile_set), data);
+
+      if (g_strcmp0 (data->type, "crypto_unknown") == 0)
+        {
+          GtkWidget *unknown_crypto_label;
+          unknown_crypto_label = GTK_WIDGET (gtk_builder_get_object (data->builder, "unknown-crypto-label"));
+          gtk_widget_set_visible (unknown_crypto_label, TRUE);
+          gtk_widget_set_no_show_all (unknown_crypto_label, FALSE);
+        }
+    }
+
   gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->window));
   gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK);
 
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->show_passphrase_check_button), FALSE);
-  gtk_entry_set_text (GTK_ENTRY (data->entry), "");
+  gtk_entry_set_text (GTK_ENTRY (data->passphrase_entry), "");
+  g_signal_connect (data->passphrase_entry, "changed", G_CALLBACK (update_unlock_button_sensitivity), data);
 
   g_object_bind_property (data->show_passphrase_check_button,
                           "active",
-                          data->entry,
+                          data->passphrase_entry,
                           "visibility",
                           G_BINDING_SYNC_CREATE);
 
diff --git a/src/disks/gduwindow.c b/src/disks/gduwindow.c
index e8aa6c52..5fe8df8c 100644
--- a/src/disks/gduwindow.c
+++ b/src/disks/gduwindow.c
@@ -2788,14 +2788,18 @@ update_device_page_for_block (GduWindow          *window,
         {
           show_flags->volume_buttons |= SHOW_FLAGS_VOLUME_BUTTONS_ENCRYPTED_LOCK;
           /* Translators: Shown as in-use part of 'Contents' if the encrypted device is unlocked */
-          in_use_markup = g_strdup (C_("volume-content-luks", "Unlocked"));
+          in_use_markup = g_strdup (C_("volume-content-crypto", "Unlocked"));
         }
       else
         {
           show_flags->volume_buttons |= SHOW_FLAGS_VOLUME_BUTTONS_ENCRYPTED_UNLOCK;
           /* Translators: Shown as in-use part of 'Contents' if the encrypted device is unlocked */
-          in_use_markup = g_strdup (C_("volume-content-luks", "Locked"));
+          in_use_markup = g_strdup (C_("volume-content-crypto", "Locked"));
         }
+    }
+
+  if (g_strcmp0 (udisks_block_get_id_type (block), "crypto_LUKS") == 0)
+    {
       show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_CONFIGURE_CRYPTTAB;
       show_flags->volume_menu |= SHOW_FLAGS_VOLUME_MENU_CHANGE_PASSPHRASE;
     }
diff --git a/src/disks/ui/unlock-device-dialog.ui b/src/disks/ui/unlock-device-dialog.ui
index 937ca14b..288086fc 100644
--- a/src/disks/ui/unlock-device-dialog.ui
+++ b/src/disks/ui/unlock-device-dialog.ui
@@ -2,6 +2,7 @@
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkDialog" id="unlock-device-dialog">
+    <property name="width_request">400</property>
     <property name="can_focus">False</property>
     <property name="border_width">12</property>
     <property name="title" translatable="yes">Enter passphrase to unlock</property>
@@ -14,6 +15,8 @@
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
         <property name="spacing">12</property>
+        <property name="margin_left">20</property>
+        <property name="margin_right">20</property>
         <child>
           <object class="GtkBox" id="infobar-vbox">
             <property name="visible">True</property>
@@ -30,7 +33,24 @@
           </packing>
         </child>
         <child>
-          <object class="GtkGrid" id="grid1">
+          <object class="GtkLabel" id="unknown-crypto-label">
+            <property name="visible">False</property>
+            <property name="no_show_all">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_bottom">5</property>
+            <property name="label" translatable="yes">This volume might be a VeraCrypt volume as it contains 
random data.</property>
+            <property name="wrap">True</property>
+            <property name="width_chars">30</property>
+            <property name="max_width_chars">30</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="unlock-device-grid">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <property name="row_spacing">6</property>
@@ -56,6 +76,7 @@
             </child>
             <child>
               <object class="GtkEntry" id="unlock-device-passphrase-entry">
+                <property name="placeholder-text" translatable="yes">If specified</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="hexpand">True</property>
@@ -88,6 +109,122 @@
                 <property name="height">1</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkLabel" id="unlock-device-volume-type-label">
+                <property name="visible">False</property>
+                <property name="no_show_all">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Volume type</property>
+                <property name="use_underline">True</property>
+                <property name="xalign">1</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="unlock-device-volume-type-box">
+                <property name="spacing">10</property>
+                <property name="margin_top">3</property>
+                <property name="margin_bottom">3</property>
+                <child>
+                  <object class="GtkCheckButton" id="unlock-device-tcrypt-hidden-check-button">
+                    <property name="label" translatable="yes">_Hidden</property>
+                    <property name="tooltip_markup" translatable="yes">Instead of unlocking this volume, 
attempt to unlock a secondary volume hidden inside.</property>
+                    <property name="visible">False</property>
+                    <property name="no_show_all">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="unlock-device-tcrypt-system-check-button">
+                    <property name="label" translatable="yes">Windows _system</property>
+                    <property name="tooltip_markup" translatable="yes">Unlock an encrypted Windows system 
partition or drive.</property>
+                    <property name="visible">False</property>
+                    <property name="no_show_all">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="unlock-device-pim-label">
+                <property name="visible">False</property>
+                <property name="no_show_all">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">PI_M</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">unlock-device-pim-entry</property>
+                <property name="xalign">1</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="unlock-device-pim-entry">
+                <property name="placeholder-text" translatable="yes">If specified</property>
+                <property name="tooltip_markup" translatable="yes">If set, the VeraCrypt PIM (Personal 
Iterations Multiplier) numeric value to use for this volume.</property>
+                <property name="visible">False</property>
+                <property name="no_show_all">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="activates_default">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="unlock-device-keyfiles-label">
+                <property name="visible">False</property>
+                <property name="no_show_all">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Keyfiles</property>
+                <property name="use_underline">True</property>
+                <property name="xalign">1</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFileChooserButton" id="unlock-device-tcrypt-keyfile-chooser-button">
+                <property name="title" translatable="yes">Select a Keyfile</property>
+                <property name="visible">False</property>
+                <property name="no_show_all">True</property>
+                <property name="can_focus">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">4</property>
+              </packing>
+            </child>
             <child>
               <placeholder/>
             </child>
@@ -117,9 +254,10 @@
               </packing>
             </child>
             <child>
-              <object class="GtkButton" id="button6">
+              <object class="GtkButton" id="unlock-device-unlock-button">
                 <property name="label" translatable="yes">_Unlock</property>
                 <property name="visible">True</property>
+                <property name="sensitive">False</property>
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>
                 <property name="receives_default">True</property>
@@ -143,7 +281,7 @@
     </child>
     <action-widgets>
       <action-widget response="-6">button5</action-widget>
-      <action-widget response="-5">button6</action-widget>
+      <action-widget response="-5">unlock-device-unlock-button</action-widget>
     </action-widgets>
   </object>
 </interface>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]