Browse Source

Refactor crypto stuff

Matthias Vogelgesang 7 years ago
parent
commit
19fb107ed0
4 changed files with 281 additions and 180 deletions
  1. 195 0
      src/iridium-crypto.c
  2. 60 0
      src/iridium-crypto.h
  3. 25 180
      src/iridium-standard-file.c
  4. 1 0
      src/meson.build

+ 195 - 0
src/iridium-crypto.c

@@ -0,0 +1,195 @@
+/* iridium-crypto.c
+ *
+ * Copyright 2018 Matthias Vogelgesang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <nettle/aes.h>
+#include <nettle/cbc.h>
+#include <nettle/sha2.h>
+#include <nettle/hmac.h>
+#include <nettle/pbkdf2.h>
+#include "iridium-crypto.h"
+
+IridiumAuthParams *
+iridium_crypto_auth_params_new (void)
+{
+  IridiumAuthParams *params;
+
+  params = g_new0 (IridiumAuthParams, 1);
+  params->func = IRIDIUM_CRYPTO_SF_FUNC_PBKDF2;
+  params->hash = IRIDIUM_CRYPTO_SF_HASH_SHA512;
+  params->cost = 101000;
+  params->key_size = 512;
+  params->salt = g_strdup ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+  return params;
+}
+
+void
+iridium_crypto_auth_params_free (IridiumAuthParams *params)
+{
+  g_free (params->salt);
+  g_free (params);
+}
+
+void
+iridium_crypto_derive_keys (IridiumAuthParams *params,
+                            const gchar *password)
+{
+  struct hmac_sha512_ctx context;
+  guint8 dst[96];
+
+  hmac_sha512_set_key (&context, strlen ((gchar *) password), (const guint8 *) password);
+
+  PBKDF2 (&context, hmac_sha512_update, hmac_sha512_digest, SHA512_DIGEST_SIZE,
+          params->cost, strlen (params->salt), (guint8 *) params->salt, 96, dst);
+
+  memcpy (&params->keys, dst, sizeof (dst));
+}
+
+guint8 *
+iridium_crypto_unhexlify (const gchar *s, gsize length)
+{
+  guint8 *result;
+
+  result = g_malloc0 (length / 2);
+
+  for (gsize i = 0; i < length / 2; i++)
+    sscanf (&s[i * 2], "%2hhx", &result[i]);
+
+  return result;
+}
+
+gchar *
+iridium_crypto_hexlify (const guint8 *s, gsize length)
+{
+  gchar *result;
+
+  result = g_malloc0 (2 * length + 1);
+
+  for (gsize i = 0; i < length; i++)
+    g_snprintf (&result[2 * i], 3, "%02x", s[i]);
+
+  return result;
+}
+
+static gchar *
+iridium_crypto_decrypt (const gchar *s,
+                        const gchar *check_uuid,
+                        const guint8 *enc_key,
+                        const guint8 *auth_key,
+                        gsize key_length)
+{
+  gchar **v;
+  gchar *to_auth;
+  gchar *hash;
+  gsize cipher_length;
+  gsize raw_length;
+  const gchar *version;
+  const gchar *auth_hash;
+  const gchar *uuid;
+  const gchar *iv;
+  const gchar *cipher_text;
+  guint8 *iv_bytes;
+  guint8 *dst;
+  guchar *cipher_raw_text;
+  struct hmac_sha256_ctx hmac_context;
+  guint8 digest[SHA256_DIGEST_SIZE];
+  struct aes_ctx aes_context;
+
+  v = g_strsplit (s, ":", 0);
+  g_assert_nonnull (v[0]); version = v[0];
+  g_assert_nonnull (v[1]); auth_hash = v[1];
+  g_assert_nonnull (v[2]); uuid = v[2];
+  g_assert_nonnull (v[3]); iv = v[3];
+  g_assert_nonnull (v[4]); cipher_text = v[4];
+
+  if (g_strcmp0 (uuid, check_uuid)) {
+    g_warning ("UUID %s does not match %s\n", uuid, check_uuid);
+    g_strfreev (v);
+    return NULL;
+  }
+
+  to_auth = g_strjoin (":", version, uuid, iv, cipher_text, NULL);
+  hmac_sha256_set_key (&hmac_context, key_length, auth_key);
+  hmac_sha256_update (&hmac_context, strlen (to_auth), (guint8 *) to_auth);
+  hmac_sha256_digest (&hmac_context, SHA256_DIGEST_SIZE, digest);
+
+  hash = iridium_crypto_hexlify (digest, sizeof (digest));
+
+  if (g_strcmp0 (hash, auth_hash)) {
+    g_free (hash);
+    g_free (to_auth);
+    g_strfreev (v);
+    return NULL;
+  }
+
+  g_free (hash);
+
+  cipher_length = strlen (cipher_text);
+  cipher_length = AES_BLOCK_SIZE * (cipher_length / AES_BLOCK_SIZE + (cipher_length % AES_BLOCK_SIZE ? 1 : 0));
+  dst = g_malloc0 (cipher_length);
+
+  aes_set_decrypt_key (&aes_context, key_length, enc_key);
+  iv_bytes = iridium_crypto_unhexlify (iv, strlen (iv));
+  cipher_raw_text = g_base64_decode (cipher_text, &raw_length);
+
+  cbc_decrypt (&aes_context, (nettle_cipher_func *) &aes_decrypt, AES_BLOCK_SIZE, iv_bytes,
+               raw_length, dst, (guint8 *) cipher_raw_text);
+
+  dst[raw_length] = '\0';
+  g_free (cipher_raw_text);
+  g_free (iv_bytes);
+  g_free (to_auth);
+  g_strfreev (v);
+  return (gchar *) dst;
+}
+
+gchar *
+iridium_crypto_decrypt_item (const gchar *enc_content,
+                             IridiumAuthParams *params,
+                             const gchar *enc_item_key,
+                             const gchar *uuid)
+{
+  gchar *enc_auth_key;
+  const gchar *enc_key;
+  const gchar *auth_key;
+  guint8 *enc_key_bytes;
+  guint8 *auth_key_bytes;
+  gsize enc_key_size;
+  gchar *content;
+
+  enc_auth_key = iridium_crypto_decrypt (enc_item_key, uuid, params->keys.master, params->keys.auth, sizeof (params->keys.master));
+
+  if (enc_auth_key == NULL)
+    return NULL;
+
+  enc_key = (gchar *) enc_auth_key;
+  enc_key_size = strlen (enc_auth_key) / 2 - 8;
+  auth_key = &enc_auth_key[enc_key_size];
+
+  enc_key_bytes = iridium_crypto_unhexlify (enc_key, enc_key_size);
+  auth_key_bytes = iridium_crypto_unhexlify (auth_key, enc_key_size);
+
+  content = iridium_crypto_decrypt (enc_content, uuid, enc_key_bytes, auth_key_bytes, enc_key_size / 2);
+
+  g_free (enc_key_bytes);
+  g_free (auth_key_bytes);
+  g_free (enc_auth_key);
+  return content;
+}

+ 60 - 0
src/iridium-crypto.h

@@ -0,0 +1,60 @@
+/* iridium-crypto.h
+ *
+ * Copyright 2018 Matthias Vogelgesang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  guint cost;
+  guint key_size;
+  gchar *salt;
+  enum {
+    IRIDIUM_CRYPTO_SF_FUNC_PBKDF2,
+  } func;
+  enum {
+    IRIDIUM_CRYPTO_SF_HASH_SHA512,
+  } hash;
+  enum {
+    IRIDIUM_CRYPTO_SF_VERSION_001,
+    IRIDIUM_CRYPTO_SF_VERSION_002,
+  } version;
+  struct {
+    guint8 password[32];
+    guint8 master[32];
+    guint8 auth[32];
+  } keys;
+} IridiumAuthParams;
+
+
+IridiumAuthParams *iridium_crypto_auth_params_new   (void);
+void               iridium_crypto_auth_params_free  (IridiumAuthParams  *params);
+void               iridium_crypto_derive_keys       (IridiumAuthParams  *params,
+                                                     const gchar        *password);
+gchar             *iridium_crypto_decrypt_item      (const gchar        *enc_content,
+                                                     IridiumAuthParams  *params,
+                                                     const gchar        *enc_item_key,
+                                                     const gchar        *uuid);
+gchar             *iridium_crypto_hexlify           (const guint8       *s,
+                                                     gsize               length);
+guint8            *iridium_crypto_unhexlify         (const gchar        *s,
+                                                     gsize               length);
+
+G_END_DECLS

+ 25 - 180
src/iridium-standard-file.c

@@ -25,36 +25,10 @@
 #include <nettle/sha2.h>
 #include <nettle/hmac.h>
 #include <nettle/pbkdf2.h>
+#include "iridium-crypto.h"
 #include "iridium-standard-file.h"
 #include "iridium-note.h"
 
-typedef enum {
-  SF_VERSION_001,
-  SF_VERSION_002,
-} StandardFileVersion;
-
-typedef enum {
-  SF_FUNC_PBKDF2,
-} StandardFileFunc;
-
-typedef enum {
-  SF_HASH_SHA512,
-} StandardFileHash;
-
-typedef struct {
-  guint cost;
-  guint key_size;
-  gchar *salt;
-  StandardFileFunc func;
-  StandardFileFunc hash;
-  StandardFileVersion version;
-  struct {
-    guint8 password[32];
-    guint8 master[32];
-    guint8 auth[32];
-  } keys;
-} StandardFileAuthParams;
-
 typedef struct {
   IridiumStandardFile *client;
   gchar *password;
@@ -68,7 +42,7 @@ struct _IridiumStandardFile
   gchar         *token;
   SoupSession   *session;
   SoupURI       *base_uri;
-  StandardFileAuthParams auth_params;
+  IridiumAuthParams auth_params;
 };
 
 G_DEFINE_TYPE (IridiumStandardFile, iridium_standard_file, G_TYPE_OBJECT)
@@ -93,148 +67,6 @@ iridium_standard_file_new (void)
   return g_object_new (IRIDIUM_TYPE_STANDARD_FILE, NULL);
 }
 
-static guint8 *
-unhexlify (const gchar *s, gsize length)
-{
-  guint8 *result;
-
-  result = g_malloc0 (length / 2);
-
-  for (gsize i = 0; i < length / 2; i++)
-    sscanf (&s[i * 2], "%2hhx", &result[i]);
-
-  return result;
-}
-
-static gchar *
-hexlify (const guint8 *s, gsize length)
-{
-  gchar *result;
-
-  result = g_malloc0 (2 * length + 1);
-
-  for (gsize i = 0; i < length; i++)
-    g_snprintf (&result[2 * i], 3, "%02x", s[i]);
-
-  return result;
-}
-
-static void
-derive_keys (const guint8 *password,
-             StandardFileAuthParams *params)
-{
-  struct hmac_sha512_ctx context;
-  guint8 dst[96];
-
-  g_assert_nonnull (params->salt);
-
-  hmac_sha512_set_key (&context, strlen ((gchar *) password), password);
-
-  PBKDF2 (&context, hmac_sha512_update, hmac_sha512_digest, SHA512_DIGEST_SIZE,
-          params->cost, strlen (params->salt), (guint8 *) params->salt, 96, dst);
-
-  memcpy (&params->keys, dst, sizeof (dst));
-}
-
-static gchar *
-decrypt (const gchar *s,
-         const gchar *check_uuid,
-         const guint8 *enc_key,
-         const guint8 *auth_key,
-         gsize key_length)
-{
-  gchar **v;
-  gchar *to_auth;
-  gchar *hash;
-  gsize cipher_length;
-  gsize raw_length;
-  const gchar *version;
-  const gchar *auth_hash;
-  const gchar *uuid;
-  const gchar *iv;
-  const gchar *cipher_text;
-  guint8 *iv_bytes;
-  guint8 *dst;
-  guchar *cipher_raw_text;
-  struct hmac_sha256_ctx hmac_contextt;
-  guint8 digest[SHA256_DIGEST_SIZE];
-  struct aes_ctx aes_context;
-
-  v = g_strsplit (s, ":", 0);
-  g_assert_nonnull (v[0]); version = v[0];
-  g_assert_nonnull (v[1]); auth_hash = v[1];
-  g_assert_nonnull (v[2]); uuid = v[2];
-  g_assert_nonnull (v[3]); iv = v[3];
-  g_assert_nonnull (v[4]); cipher_text = v[4];
-
-  g_assert_cmpstr (uuid, ==, check_uuid);
-
-  to_auth = g_strjoin (":", version, uuid, iv, cipher_text, NULL);
-  hmac_sha256_set_key (&hmac_contextt, key_length, auth_key);
-  hmac_sha256_update (&hmac_contextt, strlen (to_auth), (guint8 *) to_auth);
-  hmac_sha256_digest (&hmac_contextt, SHA256_DIGEST_SIZE, digest);
-
-  hash = hexlify (digest, sizeof (digest));
-  g_assert_cmpstr (hash, ==, (gchar *) auth_hash);
-  g_free (hash);
-
-  cipher_length = strlen (cipher_text);
-  cipher_length = AES_BLOCK_SIZE * (cipher_length / AES_BLOCK_SIZE + (cipher_length % AES_BLOCK_SIZE ? 1 : 0));
-  dst = g_malloc0 (cipher_length);
-
-  aes_set_decrypt_key (&aes_context, key_length, enc_key);
-  iv_bytes = unhexlify (iv, strlen (iv));
-  cipher_raw_text = g_base64_decode (cipher_text, &raw_length);
-
-  cbc_decrypt (&aes_context, (nettle_cipher_func *) &aes_decrypt, AES_BLOCK_SIZE, iv_bytes,
-               raw_length, dst, (guint8 *) cipher_raw_text);
-
-  dst[raw_length] = '\0';
-  g_free (cipher_raw_text);
-  g_free (iv_bytes);
-  g_free (to_auth);
-  g_strfreev (v);
-  return (gchar *) dst;
-}
-
-static gchar *
-decrypt_item (JsonObject *item, StandardFileAuthParams *params)
-{
-  const gchar *enc_item_key;
-  const gchar *uuid;
-  gchar *enc_auth_key;
-  const gchar *enc_key;
-  const gchar *auth_key;
-  guint8 *enc_key_bytes;
-  guint8 *auth_key_bytes;
-  gsize enc_key_size;
-  const gchar *enc_content;
-  gchar *content;
-
-  uuid = json_object_get_string_member (item, "uuid");
-  enc_item_key = json_object_get_string_member (item, "enc_item_key");
-
-  if (!g_strcmp0 (enc_item_key, ""))
-    return NULL;
-
-  enc_auth_key = decrypt (enc_item_key, uuid, params->keys.master, params->keys.auth, sizeof (params->keys.master));
-
-  enc_key = (gchar *) enc_auth_key;
-  enc_key_size = strlen (enc_auth_key) / 2 - 8;
-  auth_key = &enc_auth_key[enc_key_size];
-
-  enc_key_bytes = unhexlify (enc_key, enc_key_size);
-  auth_key_bytes = unhexlify (auth_key, enc_key_size);
-
-  enc_content = json_object_get_string_member (item, "content");
-  content = decrypt (enc_content, uuid, enc_key_bytes, auth_key_bytes, enc_key_size / 2);
-
-  g_free (enc_key_bytes);
-  g_free (auth_key_bytes);
-  g_free (enc_auth_key);
-  return content;
-}
-
 static IridiumNote *
 deserialize_note (JsonObject *meta, JsonObject *data)
 {
@@ -284,8 +116,8 @@ get_auth_params (JsonParser *parser,
 
   object = json_node_get_object (json_parser_get_root (parser));
 
-  params->client->auth_params.func = SF_FUNC_PBKDF2;
-  params->client->auth_params.hash = SF_HASH_SHA512;
+  params->client->auth_params.func = IRIDIUM_CRYPTO_SF_FUNC_PBKDF2;
+  params->client->auth_params.hash = IRIDIUM_CRYPTO_SF_HASH_SHA512;
   params->client->auth_params.cost = json_object_get_int_member (object, "pw_cost");
   params->client->auth_params.key_size = json_object_get_int_member (object, "pw_key_size");
   params->client->auth_params.salt = g_strdup (json_object_get_string_member (object, "pw_salt"));
@@ -305,9 +137,9 @@ get_auth_params (JsonParser *parser,
   s = json_object_get_string_member (object, "version");
 
   if (!g_strcmp0 (s, "001"))
-    params->client->auth_params.version = SF_VERSION_001;
+    params->client->auth_params.version = IRIDIUM_CRYPTO_SF_VERSION_001;
   else if (!g_strcmp0 (s, "002"))
-    params->client->auth_params.version = SF_VERSION_002;
+    params->client->auth_params.version = IRIDIUM_CRYPTO_SF_VERSION_002;
   else {
     g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                          "StandardFile protocols other than 001 and 002 are not supported");
@@ -408,7 +240,7 @@ on_auth_params_response_parsed (GObject *object,
     return;
   }
 
-  derive_keys ((guint8 *) data->password, &data->client->auth_params);
+  iridium_crypto_derive_keys (&data->client->auth_params, data->password);
 
   g_debug ("StandardFile parameters: version=%i func=%i hash=%i key_size=%u iterations=%u",
            data->client->auth_params.version,
@@ -425,7 +257,7 @@ on_auth_params_response_parsed (GObject *object,
     return;
   }
 
-  password = hexlify (data->client->auth_params.keys.password,
+  password = iridium_crypto_hexlify (data->client->auth_params.keys.password,
                       sizeof (data->client->auth_params.keys.password));
   body = g_strdup_printf ("{\"email\": \"%s\", \"password\": \"%s\"}", data->client->email, password);
   msg = soup_request_http_get_message (request);
@@ -457,7 +289,7 @@ on_send_auth_params_message (GObject *object,
   }
 
   parser = json_parser_new ();
-  json_parser_load_from_stream_async (parser, stream, g_task_get_cancellable (task),          
+  json_parser_load_from_stream_async (parser, stream, g_task_get_cancellable (task),
                                       on_auth_params_response_parsed, task);
   g_object_unref (object);
   g_object_unref (stream);
@@ -469,7 +301,7 @@ on_sync_response_parsed (GObject *object,
                          gpointer user_data)
 {
   GTask *task;
-  StandardFileAuthParams *auth_params;
+  IridiumAuthParams *auth_params;
   JsonParser *parser;
   JsonObject *root;
   JsonArray *array;
@@ -491,10 +323,20 @@ on_sync_response_parsed (GObject *object,
   for (guint i = 0; i < json_array_get_length (array); i++) {
     JsonObject *data;
     GObject *item;
+    const gchar *uuid;
+    const gchar *enc_item_key;
+    const gchar *enc_content;
     gchar *content;
 
     data = json_array_get_object_element (array, i);
-    content = decrypt_item (data, auth_params);
+    uuid = json_object_get_string_member (data, "uuid");
+    enc_item_key = json_object_get_string_member (data, "enc_item_key");
+
+    if (!g_strcmp0 (enc_item_key, ""))
+      continue;
+
+    enc_content = json_object_get_string_member (data, "content");
+    content = iridium_crypto_decrypt_item (enc_content, auth_params, enc_item_key, uuid);
 
     if (content) {
       item = deserialize_item (data, content);
@@ -637,7 +479,10 @@ iridium_standard_file_dispose (GObject *object)
 
   self = IRIDIUM_STANDARD_FILE (object);
   g_object_unref (self->session);
-  soup_uri_free (self->base_uri);
+
+  if (self->base_uri)
+    soup_uri_free (self->base_uri);
+
   G_OBJECT_CLASS (iridium_standard_file_parent_class)->dispose (object);
 }
 

+ 1 - 0
src/meson.build

@@ -2,6 +2,7 @@ cc = meson.get_compiler('c')
 
 iridium_sources = [
   'main.c',
+  'iridium-crypto.c',
   'iridium-note.c',
   'iridium-note-row.c',
   'iridium-tag.c',