LCOV - code coverage report
Current view: top level - gcr - gcr-secret-exchange.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 271 329 82.4 %
Date: 2022-09-04 10:20:22 Functions: 28 30 93.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 101 200 50.5 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * gnome-keyring
       3                 :            :  *
       4                 :            :  * Copyright (C) 2010 Stefan Walter
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU Lesser General Public License as
       8                 :            :  * published by the Free Software Foundation; either version 2.1 of
       9                 :            :  * the License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful, but
      12                 :            :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14                 :            :  * Lesser General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU Lesser General Public
      17                 :            :  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
      18                 :            :  */
      19                 :            : 
      20                 :            : #include "config.h"
      21                 :            : 
      22                 :            : #include "gcr-secret-exchange.h"
      23                 :            : 
      24                 :            : #include "egg/egg-dh.h"
      25                 :            : #include "egg/egg-hkdf.h"
      26                 :            : #include "egg/egg-libgcrypt.h"
      27                 :            : #include "egg/egg-padding.h"
      28                 :            : #include "egg/egg-secure-memory.h"
      29                 :            : 
      30                 :            : #include <string.h>
      31                 :            : #include <gcrypt.h>
      32                 :            : 
      33                 :         78 : EGG_SECURE_DECLARE (secret_exchange);
      34                 :            : 
      35                 :            : /**
      36                 :            :  * GcrSecretExchange:
      37                 :            :  *
      38                 :            :  * Allows exchange of secrets between two processes on the same system without
      39                 :            :  * exposing those secrets to things like loggers, non-pageable memory etc.
      40                 :            :  *
      41                 :            :  * This does not protect against active attacks like MITM attacks.
      42                 :            :  *
      43                 :            :  * Each side creates a secret exchange object, and one of the sides calls
      44                 :            :  * [method@SecretExchange.begin]. This creates a string, which should be passed
      45                 :            :  * to the other side. Each side passes the strings it receives into
      46                 :            :  * [method@SecretExchange.receive].
      47                 :            :  *
      48                 :            :  * In order to send a reply (either with or without a secret) use
      49                 :            :  * [method@SecretExchange.send]. A side must have successfully called
      50                 :            :  * [method@SecretExchange.receive] before it can use
      51                 :            :  * [method@SecretExchange.send].
      52                 :            :  *
      53                 :            :  * The secret exchange objects can be used for multiple iterations of the
      54                 :            :  * conversation, or for just one request/reply. The only limitation being that
      55                 :            :  * the initial request cannot contain a secret.
      56                 :            :  *
      57                 :            :  * Caveat: Information about the approximate length (rounded up to the nearest
      58                 :            :  * 16 bytes) may be leaked. If this is considered inacceptable, do not use
      59                 :            :  * [class@SecretExchange].
      60                 :            :  */
      61                 :            : 
      62                 :            : /**
      63                 :            :  * GCR_SECRET_EXCHANGE_PROTOCOL_1:
      64                 :            :  *
      65                 :            :  * The current secret exchange protocol. Key agreement is done using DH with the
      66                 :            :  * 1536 bit IKE parameter group. Keys are derived using SHA256 with HKDF. The
      67                 :            :  * transport encryption is done with 128 bit AES.
      68                 :            :  */
      69                 :            : 
      70                 :            : #define SECRET_EXCHANGE_PROTOCOL_1_PREFIX "[" GCR_SECRET_EXCHANGE_PROTOCOL_1 "]\n"
      71                 :            : 
      72                 :            : enum {
      73                 :            :         PROP_0,
      74                 :            :         PROP_PROTOCOL
      75                 :            : };
      76                 :            : 
      77                 :            : typedef struct _GcrSecretExchangeDefault GcrSecretExchangeDefault;
      78                 :            : 
      79                 :            : struct _GcrSecretExchangePrivate {
      80                 :            :         GcrSecretExchangeDefault *default_exchange;
      81                 :            :         GDestroyNotify destroy_exchange;
      82                 :            :         gboolean explicit_protocol;
      83                 :            :         gboolean generated;
      84                 :            :         guchar *publi;
      85                 :            :         gsize n_publi;
      86                 :            :         gboolean derived;
      87                 :            :         gchar *secret;
      88                 :            :         gsize n_secret;
      89                 :            : };
      90                 :            : 
      91   [ +  +  +  -  :        293 : G_DEFINE_TYPE_WITH_PRIVATE (GcrSecretExchange, gcr_secret_exchange, G_TYPE_OBJECT);
                   +  + ]
      92                 :            : 
      93                 :            : static void
      94                 :         68 : key_file_set_base64 (GKeyFile *key_file, const gchar *section,
      95                 :            :                      const gchar *field, gconstpointer data, gsize n_data)
      96                 :            : {
      97                 :            :         gchar *value;
      98                 :            : 
      99                 :         68 :         value = g_base64_encode (data, n_data);
     100                 :         68 :         g_key_file_set_value (key_file, section, field, value);
     101                 :         68 :         g_free (value);
     102                 :         68 : }
     103                 :            : 
     104                 :            : static gpointer
     105                 :         54 : key_file_get_base64 (GKeyFile *key_file, const gchar *section,
     106                 :            :                      const gchar *field, gsize *n_result)
     107                 :            : {
     108                 :         54 :         gpointer result = NULL;
     109                 :            :         gchar *data;
     110                 :            : 
     111         [ -  + ]:         54 :         g_return_val_if_fail (key_file, NULL);
     112         [ -  + ]:         54 :         g_return_val_if_fail (section, NULL);
     113         [ -  + ]:         54 :         g_return_val_if_fail (field, NULL);
     114         [ -  + ]:         54 :         g_return_val_if_fail (n_result, NULL);
     115                 :            : 
     116                 :         54 :         data = g_key_file_get_value (key_file, section, field, NULL);
     117         [ +  - ]:         54 :         if (data != NULL)
     118                 :         54 :                 result = g_base64_decode (data, n_result);
     119                 :         54 :         g_free (data);
     120                 :         54 :         return result;
     121                 :            : }
     122                 :            : 
     123                 :            : static void
     124                 :         42 : gcr_secret_exchange_init (GcrSecretExchange *self)
     125                 :            : {
     126                 :         42 :         self->pv = gcr_secret_exchange_get_instance_private (self);
     127                 :         42 : }
     128                 :            : 
     129                 :            : 
     130                 :            : static void
     131                 :         42 : gcr_secret_exchange_set_property (GObject *obj,
     132                 :            :                                   guint prop_id,
     133                 :            :                                   const GValue *value,
     134                 :            :                                   GParamSpec *pspec)
     135                 :            : {
     136                 :         42 :         GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
     137                 :            :         const gchar *protocol;
     138                 :            : 
     139         [ +  - ]:         42 :         switch (prop_id) {
     140                 :         42 :         case PROP_PROTOCOL:
     141                 :         42 :                 protocol = g_value_get_string (value);
     142         [ +  - ]:         42 :                 if (protocol == NULL) {
     143                 :         42 :                         g_debug ("automatically selecting secret exchange protocol");
     144                 :            : 
     145                 :            :                 } else {
     146         [ #  # ]:          0 :                         if (g_str_equal (protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1)) {
     147                 :          0 :                                 g_debug ("explicitly using secret exchange protocol: %s",
     148                 :            :                                          GCR_SECRET_EXCHANGE_PROTOCOL_1);
     149                 :          0 :                                 self->pv->explicit_protocol = TRUE;
     150                 :            :                         } else {
     151                 :          0 :                                 g_warning ("the GcrSecretExchange protocol %s is unsupported defaulting to %s",
     152                 :            :                                            protocol, GCR_SECRET_EXCHANGE_PROTOCOL_1);
     153                 :            :                         }
     154                 :            :                 }
     155                 :         42 :                 break;
     156                 :          0 :         default:
     157                 :          0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
     158                 :          0 :                 break;
     159                 :            :         }
     160                 :         42 : }
     161                 :            : 
     162                 :            : static void
     163                 :          0 : gcr_secret_exchange_get_property (GObject *obj,
     164                 :            :                                   guint prop_id,
     165                 :            :                                   GValue *value,
     166                 :            :                                   GParamSpec *pspec)
     167                 :            : {
     168                 :          0 :         GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
     169                 :            : 
     170         [ #  # ]:          0 :         switch (prop_id) {
     171                 :          0 :         case PROP_PROTOCOL:
     172                 :          0 :                 g_value_set_string (value, gcr_secret_exchange_get_protocol (self));
     173                 :          0 :                 break;
     174                 :          0 :         default:
     175                 :          0 :                 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
     176                 :          0 :                 break;
     177                 :            :         }
     178                 :          0 : }
     179                 :            : 
     180                 :            : static void
     181                 :         61 : clear_secret_exchange (GcrSecretExchange *self)
     182                 :            : {
     183                 :         61 :         g_free (self->pv->publi);
     184                 :         61 :         self->pv->publi = NULL;
     185                 :         61 :         self->pv->n_publi = 0;
     186                 :         61 :         self->pv->derived = FALSE;
     187                 :         61 :         self->pv->generated = TRUE;
     188                 :         61 :         egg_secure_free (self->pv->secret);
     189                 :         61 :         self->pv->secret = NULL;
     190                 :         61 :         self->pv->n_secret = 0;
     191                 :         61 : }
     192                 :            : 
     193                 :            : static void
     194                 :         40 : gcr_secret_exchange_finalize (GObject *obj)
     195                 :            : {
     196                 :         40 :         GcrSecretExchange *self = GCR_SECRET_EXCHANGE (obj);
     197                 :            : 
     198         [ +  - ]:         40 :         if (self->pv->destroy_exchange)
     199                 :         40 :                 (self->pv->destroy_exchange) (self->pv->default_exchange);
     200                 :            : 
     201                 :         40 :         clear_secret_exchange (self);
     202                 :            : 
     203                 :         40 :         G_OBJECT_CLASS (gcr_secret_exchange_parent_class)->finalize (obj);
     204                 :         40 : }
     205                 :            : 
     206                 :            : /**
     207                 :            :  * gcr_secret_exchange_new:
     208                 :            :  * @protocol: (nullable): the exchange protocol to use
     209                 :            :  *
     210                 :            :  * Create a new secret exchange object.
     211                 :            :  *
     212                 :            :  * Specify a protocol of %NULL to allow any protocol. This is especially
     213                 :            :  * relevant on the side of the exchange that does not call
     214                 :            :  * [method@SecretExchange.begin], that is the originator. Currently the only
     215                 :            :  * protocol supported is %GCR_SECRET_EXCHANGE_PROTOCOL_1.
     216                 :            :  *
     217                 :            :  * Returns: (transfer full): A new #GcrSecretExchange object
     218                 :            :  */
     219                 :            : GcrSecretExchange *
     220                 :         42 : gcr_secret_exchange_new (const gchar *protocol)
     221                 :            : {
     222                 :         42 :         return g_object_new (GCR_TYPE_SECRET_EXCHANGE,
     223                 :            :                              "protocol", protocol,
     224                 :            :                              NULL);
     225                 :            : }
     226                 :            : 
     227                 :            : /**
     228                 :            :  * gcr_secret_exchange_get_protocol:
     229                 :            :  * @self: a #GcrSecretExchange object
     230                 :            :  * Get the secret exchange protocol.
     231                 :            :  *
     232                 :            :  * Will return %NULL if no protocol was specified, and either
     233                 :            :  * [method@SecretExchange.begin] or [method@SecretExchange.receive] have not
     234                 :            :  * been called successfully.
     235                 :            :  *
     236                 :            :  * Returns: the protocol or %NULL
     237                 :            :  */
     238                 :            : const gchar *
     239                 :          0 : gcr_secret_exchange_get_protocol (GcrSecretExchange *self)
     240                 :            : {
     241   [ #  #  #  #  :          0 :         g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
             #  #  #  # ]
     242   [ #  #  #  # ]:          0 :         if (self->pv->explicit_protocol || self->pv->generated)
     243                 :          0 :                 return GCR_SECRET_EXCHANGE_PROTOCOL_1;
     244                 :          0 :         return NULL;
     245                 :            : }
     246                 :            : 
     247                 :            : /**
     248                 :            :  * gcr_secret_exchange_begin:
     249                 :            :  * @self: a #GcrSecretExchange object
     250                 :            :  *
     251                 :            :  * Begin the secret exchange. The resulting string should be sent to the other
     252                 :            :  * side of the exchange. The other side should use [method@SecretExchange.receive]
     253                 :            :  * to process the string.
     254                 :            :  *
     255                 :            :  * Returns: (transfer full): A newly allocated string to be sent to the other
     256                 :            :  *     side of the secret exchange
     257                 :            :  */
     258                 :            : gchar *
     259                 :         21 : gcr_secret_exchange_begin (GcrSecretExchange *self)
     260                 :            : {
     261                 :            :         GcrSecretExchangeClass *klass;
     262                 :            :         GKeyFile *output;
     263                 :            :         gchar *result;
     264                 :            : 
     265   [ -  +  +  -  :         21 :         g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
             +  -  -  + ]
     266                 :            : 
     267                 :         21 :         klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
     268         [ -  + ]:         21 :         g_return_val_if_fail (klass->generate_exchange_key, NULL);
     269                 :            : 
     270                 :         21 :         clear_secret_exchange (self);
     271                 :            : 
     272                 :         21 :         output = g_key_file_new ();
     273                 :            : 
     274         [ -  + ]:         21 :         if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
     275                 :         21 :                                              &self->pv->publi, &self->pv->n_publi))
     276                 :          0 :                 g_return_val_if_reached (NULL);
     277                 :         21 :         self->pv->generated = TRUE;
     278                 :            : 
     279                 :         21 :         key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public",
     280                 :         21 :                              self->pv->publi, self->pv->n_publi);
     281                 :            : 
     282                 :         21 :         result = g_key_file_to_data (output, NULL, NULL);
     283         [ -  + ]:         21 :         g_return_val_if_fail (result != NULL, NULL);
     284                 :            : 
     285                 :         21 :         g_strchug (result);
     286                 :            : 
     287                 :         21 :         gchar *string = g_strescape (result, "");
     288                 :         21 :         g_debug ("beginning the secret exchange: %s", string);
     289                 :         21 :         g_free (string);
     290                 :            : 
     291         [ -  + ]:         21 :         if (!g_str_has_prefix (result, SECRET_EXCHANGE_PROTOCOL_1_PREFIX))
     292                 :          0 :                 g_warning ("the prepared data does not have the correct protocol prefix");
     293                 :            : 
     294                 :         21 :         g_key_file_free (output);
     295                 :            : 
     296                 :         21 :         return result;
     297                 :            : }
     298                 :            : 
     299                 :            : static gboolean
     300                 :         38 : derive_key (GcrSecretExchange *self,
     301                 :            :             GKeyFile *input)
     302                 :            : {
     303                 :            :         GcrSecretExchangeClass *klass;
     304                 :            :         gboolean ret;
     305                 :            :         guchar *peer;
     306                 :            :         gsize n_peer;
     307                 :            : 
     308                 :         38 :         klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
     309         [ -  + ]:         38 :         g_return_val_if_fail (klass->derive_transport_key, FALSE);
     310                 :            : 
     311                 :         38 :         g_debug ("deriving shared transport key");
     312                 :            : 
     313                 :         38 :         peer = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", &n_peer);
     314         [ -  + ]:         38 :         if (peer == NULL) {
     315                 :          0 :                 g_message ("secret-exchange: invalid or missing 'public' argument");
     316                 :          0 :                 return FALSE;
     317                 :            :         }
     318                 :            : 
     319                 :         38 :         ret = (klass->derive_transport_key) (self, peer, n_peer);
     320                 :         38 :         self->pv->derived = ret;
     321                 :            : 
     322                 :         38 :         g_free (peer);
     323                 :         38 :         return ret;
     324                 :            : }
     325                 :            : 
     326                 :            : static gboolean
     327                 :          8 : perform_decrypt (GcrSecretExchange *self,
     328                 :            :                  GKeyFile *input,
     329                 :            :                  guchar **secret,
     330                 :            :                  gsize *n_secret)
     331                 :            : {
     332                 :            :         GcrSecretExchangeClass *klass;
     333                 :            :         gpointer iv, value;
     334                 :            :         guchar *result;
     335                 :            :         gsize n_result, n_iv, n_value;
     336                 :            :         gboolean ret;
     337                 :            : 
     338                 :          8 :         klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
     339         [ -  + ]:          8 :         g_return_val_if_fail (klass->decrypt_transport_data, FALSE);
     340                 :            : 
     341                 :          8 :         iv = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", &n_iv);
     342                 :            : 
     343                 :          8 :         value = key_file_get_base64 (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", &n_value);
     344         [ -  + ]:          8 :         if (value == NULL) {
     345                 :          0 :                 g_message ("secret-exchange: invalid or missing value");
     346                 :          0 :                 g_free (iv);
     347                 :          0 :                 return FALSE;
     348                 :            :         }
     349                 :            : 
     350                 :          8 :         ret = (klass->decrypt_transport_data) (self, egg_secure_realloc, value, n_value,
     351                 :            :                                                iv, n_iv, &result, &n_result);
     352                 :            : 
     353                 :          8 :         g_free (value);
     354                 :          8 :         g_free (iv);
     355                 :            : 
     356         [ -  + ]:          8 :         if (!ret)
     357                 :          0 :                 return FALSE;
     358                 :            : 
     359                 :            :         /* Reallocate a null terminator */
     360         [ +  - ]:          8 :         if (result) {
     361                 :          8 :                 result = egg_secure_realloc (result, n_result + 1);
     362                 :          8 :                 result[n_result] = 0;
     363                 :            :         }
     364                 :            : 
     365                 :          8 :         *secret = result;
     366                 :          8 :         *n_secret = n_result;
     367                 :            : 
     368                 :          8 :         return TRUE;
     369                 :            : }
     370                 :            : 
     371                 :            : /**
     372                 :            :  * gcr_secret_exchange_receive:
     373                 :            :  * @self: a #GcrSecretExchange object
     374                 :            :  * @exchange: the string received
     375                 :            :  *
     376                 :            :  * Receive a string from the other side of secret exchange. This string will
     377                 :            :  * have been created by [method@SecretExchange.begin] or
     378                 :            :  * [method@SecretExchange.send].
     379                 :            :  *
     380                 :            :  * After this call completes successfully the value returned from
     381                 :            :  * gcr_secret_exchange_get_secret() will have changed.
     382                 :            :  *
     383                 :            :  * Returns: whether the string was successfully parsed and received
     384                 :            :  */
     385                 :            : gboolean
     386                 :         52 : gcr_secret_exchange_receive (GcrSecretExchange *self,
     387                 :            :                              const gchar *exchange)
     388                 :            : {
     389                 :            :         GcrSecretExchangeClass *klass;
     390                 :         52 :         gchar *secret = NULL;
     391                 :         52 :         gsize n_secret = 0;
     392                 :            :         GKeyFile *input;
     393                 :            :         gboolean ret;
     394                 :            : 
     395   [ -  +  +  -  :         52 :         g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), FALSE);
             +  -  -  + ]
     396         [ -  + ]:         52 :         g_return_val_if_fail (exchange != NULL, FALSE);
     397                 :            : 
     398                 :         52 :         klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
     399         [ -  + ]:         52 :         g_return_val_if_fail (klass->generate_exchange_key, FALSE);
     400         [ -  + ]:         52 :         g_return_val_if_fail (klass->derive_transport_key, FALSE);
     401                 :            : 
     402                 :         52 :         gchar *string = g_strescape (exchange, "");
     403                 :         52 :         g_debug ("receiving secret exchange: %s", string);
     404                 :         52 :         g_free (string);
     405                 :            : 
     406                 :            :         /* Parse the input */
     407                 :         52 :         input = g_key_file_new ();
     408         [ -  + ]:         52 :         if (!g_key_file_load_from_data (input, exchange, strlen (exchange),
     409                 :            :                                         G_KEY_FILE_NONE, NULL)) {
     410                 :          0 :                 g_key_file_free (input);
     411                 :          0 :                 g_message ("couldn't parse secret exchange data");
     412                 :          0 :                 return FALSE;
     413                 :            :         }
     414                 :            : 
     415         [ +  + ]:         52 :         if (!self->pv->generated) {
     416         [ -  + ]:         21 :                 if (!(klass->generate_exchange_key) (self, GCR_SECRET_EXCHANGE_PROTOCOL_1,
     417                 :         21 :                                                      &self->pv->publi, &self->pv->n_publi))
     418                 :          0 :                         g_return_val_if_reached (FALSE);
     419                 :         21 :                 self->pv->generated = TRUE;
     420                 :            :         }
     421                 :            : 
     422                 :         52 :         ret = TRUE;
     423                 :            : 
     424         [ +  + ]:         52 :         if (!self->pv->derived) {
     425         [ -  + ]:         38 :                 if (!derive_key (self, input))
     426                 :          0 :                         ret = FALSE;
     427                 :            :         }
     428                 :            : 
     429   [ +  -  +  + ]:         52 :         if (ret && g_key_file_has_key (input, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", NULL))
     430                 :          8 :                 ret = perform_decrypt (self, input, (guchar **)&secret, &n_secret);
     431                 :            : 
     432         [ +  - ]:         52 :         if (ret) {
     433                 :         52 :                 egg_secure_free (self->pv->secret);
     434                 :         52 :                 self->pv->secret = secret;
     435                 :         52 :                 self->pv->n_secret = n_secret;
     436                 :            :         }
     437                 :            : 
     438                 :         52 :         g_key_file_free (input);
     439                 :         52 :         return ret;
     440                 :            : }
     441                 :            : 
     442                 :            : /**
     443                 :            :  * gcr_secret_exchange_get_secret:
     444                 :            :  * @self: a #GcrSecretExchange object
     445                 :            :  * @secret_len: (out) (optional): optionally, a location to store the length of returned secret
     446                 :            :  *
     447                 :            :  * Returns the last secret received. If no secret has yet been received this
     448                 :            :  * will return %NULL. The string is owned by the #GcrSecretExchange object
     449                 :            :  * and will be valid until the next time that gcr_secret_exchange_receive()
     450                 :            :  * is called on this object, or the object is destroyed.
     451                 :            :  *
     452                 :            :  * Depending on the secret passed into the other side of the secret exchange,
     453                 :            :  * the result may be a binary string. It does however have a null terminator,
     454                 :            :  * so if you're certain that it is does not contain arbitrary binary data,
     455                 :            :  * it can be used as a string.
     456                 :            :  *
     457                 :            :  * Returns: (transfer none) (array length=secret_len): the last secret received
     458                 :            :  */
     459                 :            : const gchar *
     460                 :         11 : gcr_secret_exchange_get_secret (GcrSecretExchange *self,
     461                 :            :                                 gsize *secret_len)
     462                 :            : {
     463   [ -  +  +  -  :         11 :         g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
             +  -  -  + ]
     464                 :            : 
     465         [ -  + ]:         11 :         if (secret_len)
     466                 :          0 :                 *secret_len = self->pv->n_secret;
     467                 :         11 :         return self->pv->secret;
     468                 :            : }
     469                 :            : 
     470                 :            : static gboolean
     471                 :          8 : perform_encrypt (GcrSecretExchange *self,
     472                 :            :                  GKeyFile *output,
     473                 :            :                  const gchar *secret,
     474                 :            :                  gsize n_secret)
     475                 :            : {
     476                 :            :         GcrSecretExchangeClass *klass;
     477                 :            :         guchar *result, *iv;
     478                 :            :         gsize n_result, n_iv;
     479                 :            : 
     480                 :          8 :         klass = GCR_SECRET_EXCHANGE_GET_CLASS (self);
     481         [ -  + ]:          8 :         g_return_val_if_fail (klass->encrypt_transport_data, FALSE);
     482                 :            : 
     483         [ -  + ]:          8 :         if (!(klass->encrypt_transport_data) (self, g_realloc, (const guchar *)secret,
     484                 :            :                                               n_secret, &iv, &n_iv, &result, &n_result))
     485                 :          0 :                 return FALSE;
     486                 :            : 
     487                 :          8 :         key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "secret", result, n_result);
     488                 :          8 :         key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "iv", iv, n_iv);
     489                 :            : 
     490                 :          8 :         g_free (result);
     491                 :          8 :         g_free (iv);
     492                 :            : 
     493                 :          8 :         return TRUE;
     494                 :            : }
     495                 :            : 
     496                 :            : /**
     497                 :            :  * gcr_secret_exchange_send:
     498                 :            :  * @self: a #GcrSecretExchange object
     499                 :            :  * @secret: (nullable): optionally, a secret to send to the other side
     500                 :            :  * @secret_len: length of @secret, or -1 if null terminated
     501                 :            :  *
     502                 :            :  * Send a reply to the other side of the secret exchange, optionally sending a
     503                 :            :  * secret.
     504                 :            :  *
     505                 :            :  * [method@SecretExchange.receive] must have been successfully called at least
     506                 :            :  * once on this object. In other words this object must have received data
     507                 :            :  * from the other side of the secret exchange, before we can send a secret.
     508                 :            :  *
     509                 :            :  * Returns: (transfer full): a newly allocated string to be sent to the other
     510                 :            :  *     side of the secret exchange
     511                 :            :  */
     512                 :            : gchar *
     513                 :         31 : gcr_secret_exchange_send (GcrSecretExchange *self,
     514                 :            :                           const gchar *secret,
     515                 :            :                           gssize secret_len)
     516                 :            : {
     517                 :            :         GKeyFile *output;
     518                 :            :         gchar *result;
     519                 :            : 
     520   [ -  +  +  -  :         31 :         g_return_val_if_fail (GCR_IS_SECRET_EXCHANGE (self), NULL);
             +  -  -  + ]
     521                 :            : 
     522         [ -  + ]:         31 :         if (!self->pv->derived) {
     523                 :          0 :                 g_warning ("gcr_secret_exchange_receive() must be called "
     524                 :            :                            "before calling this function");
     525                 :          0 :                 return NULL;
     526                 :            :         }
     527                 :            : 
     528                 :         31 :         output = g_key_file_new ();
     529                 :         31 :         key_file_set_base64 (output, GCR_SECRET_EXCHANGE_PROTOCOL_1, "public", self->pv->publi,
     530                 :         31 :                              self->pv->n_publi);
     531                 :            : 
     532         [ +  + ]:         31 :         if (secret != NULL) {
     533         [ +  - ]:          8 :                 if (secret_len < 0)
     534                 :          8 :                         secret_len = strlen (secret);
     535         [ -  + ]:          8 :                 if (!perform_encrypt (self, output, secret, secret_len)) {
     536                 :          0 :                         g_key_file_free (output);
     537                 :          0 :                         return NULL;
     538                 :            :                 }
     539                 :            :         }
     540                 :            : 
     541                 :         31 :         result = g_key_file_to_data (output, NULL, NULL);
     542         [ -  + ]:         31 :         g_return_val_if_fail (result != NULL, NULL);
     543                 :            : 
     544                 :         31 :         g_strchug (result);
     545                 :            : 
     546                 :         31 :         gchar *string = g_strescape (result, "");
     547                 :         31 :         g_debug ("sending the secret exchange: %s", string);
     548                 :         31 :         g_free (string);
     549                 :            : 
     550         [ -  + ]:         31 :         if (!g_str_has_prefix (result, SECRET_EXCHANGE_PROTOCOL_1_PREFIX))
     551                 :          0 :                 g_warning ("the prepared data does not have the correct protocol prefix: %s", result);
     552                 :            : 
     553                 :         31 :         g_key_file_free (output);
     554                 :         31 :         return result;
     555                 :            : }
     556                 :            : 
     557                 :            : /*
     558                 :            :  * This is the only set we support so far. It includes:
     559                 :            :  *  - DH with the 1536 ike modp group for key exchange
     560                 :            :  *  - HKDF SHA256 for hashing of the key to appropriate size
     561                 :            :  *  - AES 128 CBC for encryption
     562                 :            :  *  - PKCS#7 style padding
     563                 :            :  */
     564                 :            : 
     565                 :            : #define EXCHANGE_1_IKE_NAME     "ietf-ike-grp-modp-1536"
     566                 :            : #define EXCHANGE_1_KEY_LENGTH   16
     567                 :            : #define EXCHANGE_1_IV_LENGTH    16
     568                 :            : #define EXCHANGE_1_HASH_ALGO    "sha256"
     569                 :            : #define EXCHANGE_1_CIPHER_ALGO  GCRY_CIPHER_AES128
     570                 :            : #define EXCHANGE_1_CIPHER_MODE  GCRY_CIPHER_MODE_CBC
     571                 :            : 
     572                 :            : struct _GcrSecretExchangeDefault {
     573                 :            :         gcry_mpi_t prime;
     574                 :            :         gcry_mpi_t base;
     575                 :            :         gcry_mpi_t pub;
     576                 :            :         gcry_mpi_t priv;
     577                 :            :         gpointer key;
     578                 :            : };
     579                 :            : 
     580                 :            : static guchar *
     581                 :         42 : mpi_to_data (gcry_mpi_t mpi,
     582                 :            :              gsize *n_data)
     583                 :            : {
     584                 :            :         gcry_error_t gcry;
     585                 :            :         guchar *data;
     586                 :            : 
     587                 :            :         /* Get the size */
     588                 :         42 :         gcry = gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, n_data, mpi);
     589         [ -  + ]:         42 :         g_return_val_if_fail (gcry == 0, NULL);
     590                 :            : 
     591                 :         42 :         data = g_malloc0 (*n_data);
     592                 :            : 
     593                 :            :         /* Write into buffer */
     594                 :         42 :         gcry = gcry_mpi_print (GCRYMPI_FMT_USG, data, *n_data, n_data, mpi);
     595         [ -  + ]:         42 :         g_return_val_if_fail (gcry == 0, NULL);
     596                 :            : 
     597                 :         42 :         return data;
     598                 :            : }
     599                 :            : 
     600                 :            : static gcry_mpi_t
     601                 :         38 : mpi_from_data (const guchar *data,
     602                 :            :                gsize n_data)
     603                 :            : {
     604                 :            :         gcry_mpi_t mpi;
     605                 :            :         gcry_error_t gcry;
     606                 :            : 
     607                 :         38 :         gcry = gcry_mpi_scan (&mpi, GCRYMPI_FMT_USG, data, n_data, NULL);
     608         [ +  - ]:         38 :         return (gcry == 0) ? mpi : NULL;
     609                 :            : }
     610                 :            : 
     611                 :            : static void
     612                 :         40 : gcr_secret_exchange_default_free (gpointer to_free)
     613                 :            : {
     614                 :         40 :         GcrSecretExchangeDefault *data = to_free;
     615                 :         40 :         gcry_mpi_release (data->prime);
     616                 :         40 :         gcry_mpi_release (data->base);
     617                 :         40 :         gcry_mpi_release (data->pub);
     618                 :         40 :         gcry_mpi_release (data->priv);
     619         [ +  + ]:         40 :         if (data->key) {
     620                 :         36 :                 egg_secure_clear (data->key, EXCHANGE_1_KEY_LENGTH);
     621                 :         36 :                 egg_secure_free (data->key);
     622                 :            :         }
     623                 :         40 :         g_free (data);
     624                 :         40 : }
     625                 :            : 
     626                 :            : static gboolean
     627                 :         42 : gcr_secret_exchange_default_generate_exchange_key (GcrSecretExchange *exchange,
     628                 :            :                                                    const gchar *scheme,
     629                 :            :                                                    guchar **public_key,
     630                 :            :                                                    gsize *n_public_key)
     631                 :            : {
     632                 :         42 :         GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
     633                 :            : 
     634                 :         42 :         g_debug ("generating public key");
     635                 :            : 
     636         [ +  - ]:         42 :         if (data == NULL) {
     637                 :         42 :                 data = g_new0 (GcrSecretExchangeDefault, 1);
     638         [ -  + ]:         42 :                 if (!egg_dh_default_params (EXCHANGE_1_IKE_NAME, &data->prime, &data->base))
     639                 :          0 :                         g_return_val_if_reached (FALSE);
     640                 :            : 
     641                 :         42 :                 exchange->pv->default_exchange = data;
     642                 :         42 :                 exchange->pv->destroy_exchange = gcr_secret_exchange_default_free;
     643                 :            :         }
     644                 :            : 
     645                 :         42 :         gcry_mpi_release (data->priv);
     646                 :         42 :         data->priv = NULL;
     647                 :         42 :         gcry_mpi_release (data->pub);
     648                 :         42 :         data->pub = NULL;
     649                 :         42 :         egg_secure_free (data->key);
     650                 :         42 :         data->key = NULL;
     651                 :            : 
     652         [ -  + ]:         42 :         if (!egg_dh_gen_pair (data->prime, data->base, 0,
     653                 :            :                               &data->pub, &data->priv))
     654                 :          0 :                 g_return_val_if_reached (FALSE);
     655                 :            : 
     656                 :         42 :         *public_key = mpi_to_data (data->pub, n_public_key);
     657                 :         42 :         return *public_key != NULL;
     658                 :            : }
     659                 :            : 
     660                 :            : static gboolean
     661                 :         38 : gcr_secret_exchange_default_derive_transport_key (GcrSecretExchange *exchange,
     662                 :            :                                                   const guchar *peer,
     663                 :            :                                                   gsize n_peer)
     664                 :            : {
     665                 :         38 :         GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
     666                 :            :         gpointer ikm;
     667                 :            :         gsize n_ikm;
     668                 :            :         gcry_mpi_t mpi;
     669                 :            : 
     670                 :         38 :         g_debug ("deriving transport key");
     671                 :            : 
     672         [ -  + ]:         38 :         g_return_val_if_fail (data != NULL, FALSE);
     673         [ -  + ]:         38 :         g_return_val_if_fail (data->priv != NULL, FALSE);
     674                 :            : 
     675                 :         38 :         mpi = mpi_from_data (peer, n_peer);
     676         [ -  + ]:         38 :         if (mpi == NULL) {
     677                 :          0 :                 g_debug ("invalid peer mpi");
     678                 :          0 :                 return FALSE;
     679                 :            :         }
     680                 :            : 
     681                 :            :         /* Build up a key we can use */
     682                 :         38 :         ikm = egg_dh_gen_secret (mpi, data->priv, data->prime, &n_ikm);
     683         [ -  + ]:         38 :         g_return_val_if_fail (ikm != NULL, FALSE);
     684                 :            : 
     685         [ +  - ]:         38 :         if (data->key == NULL)
     686                 :         38 :                 data->key = egg_secure_alloc (EXCHANGE_1_KEY_LENGTH);
     687                 :            : 
     688         [ -  + ]:         38 :         if (!egg_hkdf_perform (EXCHANGE_1_HASH_ALGO, ikm, n_ikm, NULL, 0,
     689                 :            :                                NULL, 0, data->key, EXCHANGE_1_KEY_LENGTH))
     690                 :          0 :                 g_return_val_if_reached (FALSE);
     691                 :            : 
     692                 :         38 :         egg_secure_free (ikm);
     693                 :         38 :         gcry_mpi_release (mpi);
     694                 :            : 
     695                 :         38 :         return TRUE;
     696                 :            : }
     697                 :            : 
     698                 :            : static gboolean
     699                 :          8 : gcr_secret_exchange_default_encrypt_transport_data (GcrSecretExchange *exchange,
     700                 :            :                                                     GckAllocator allocator,
     701                 :            :                                                     const guchar *plain_text,
     702                 :            :                                                     gsize n_plain_text,
     703                 :            :                                                     guchar **iv,
     704                 :            :                                                     gsize *n_iv,
     705                 :            :                                                     guchar **cipher_text,
     706                 :            :                                                     gsize *n_cipher_text)
     707                 :            : {
     708                 :          8 :         GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
     709                 :            :         gcry_cipher_hd_t cih;
     710                 :            :         gcry_error_t gcry;
     711                 :            :         guchar *padded;
     712                 :            :         gsize n_result;
     713                 :            :         guchar *result;
     714                 :            :         gsize pos;
     715                 :            : 
     716         [ -  + ]:          8 :         g_return_val_if_fail (data != NULL, FALSE);
     717         [ -  + ]:          8 :         g_return_val_if_fail (data->key != NULL, FALSE);
     718                 :            : 
     719                 :          8 :         g_debug ("encrypting data");
     720                 :            : 
     721                 :          8 :         gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
     722         [ -  + ]:          8 :         if (gcry != 0) {
     723                 :          0 :                 g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
     724                 :          0 :                 g_free (iv);
     725                 :          0 :                 return FALSE;
     726                 :            :         }
     727                 :            : 
     728                 :          8 :         *iv = (allocator) (NULL, EXCHANGE_1_IV_LENGTH);
     729         [ -  + ]:          8 :         g_return_val_if_fail (*iv != NULL, FALSE);
     730                 :          8 :         gcry_create_nonce (*iv, EXCHANGE_1_IV_LENGTH);
     731                 :          8 :         *n_iv = EXCHANGE_1_IV_LENGTH;
     732                 :            : 
     733                 :            :         /* 16 = 128 bits */
     734                 :          8 :         gcry = gcry_cipher_setkey (cih, data->key, EXCHANGE_1_KEY_LENGTH);
     735         [ -  + ]:          8 :         g_return_val_if_fail (gcry == 0, FALSE);
     736                 :            : 
     737                 :            :         /* 16 = 128 bits */
     738                 :          8 :         gcry = gcry_cipher_setiv (cih, *iv, EXCHANGE_1_IV_LENGTH);
     739         [ -  + ]:          8 :         g_return_val_if_fail (gcry == 0, FALSE);
     740                 :            : 
     741                 :            :         /* Pad the text properly */
     742         [ -  + ]:          8 :         if (!egg_padding_pkcs7_pad (egg_secure_realloc, 16, plain_text, n_plain_text,
     743                 :            :                                     (gpointer*)&padded, &n_result))
     744                 :          0 :                 g_return_val_if_reached (FALSE);
     745                 :          8 :         result = (allocator) (NULL, n_result);
     746         [ -  + ]:          8 :         g_return_val_if_fail (result != NULL, FALSE);
     747                 :            : 
     748         [ +  + ]:         16 :         for (pos = 0; pos < n_result; pos += 16) {
     749                 :          8 :                 gcry = gcry_cipher_encrypt (cih, result + pos, 16, padded + pos, 16);
     750         [ -  + ]:          8 :                 g_return_val_if_fail (gcry == 0, FALSE);
     751                 :            :         }
     752                 :            : 
     753                 :          8 :         gcry_cipher_close (cih);
     754                 :            : 
     755                 :          8 :         egg_secure_clear (padded, n_result);
     756                 :          8 :         egg_secure_free (padded);
     757                 :            : 
     758                 :          8 :         *cipher_text = result;
     759                 :          8 :         *n_cipher_text = n_result;
     760                 :          8 :         return TRUE;
     761                 :            : }
     762                 :            : 
     763                 :            : static gboolean
     764                 :          8 : gcr_secret_exchange_default_decrypt_transport_data (GcrSecretExchange *exchange,
     765                 :            :                                                     GckAllocator allocator,
     766                 :            :                                                     const guchar *cipher_text,
     767                 :            :                                                     gsize n_cipher_text,
     768                 :            :                                                     const guchar *iv,
     769                 :            :                                                     gsize n_iv,
     770                 :            :                                                     guchar **plain_text,
     771                 :            :                                                     gsize *n_plain_text)
     772                 :            : {
     773                 :          8 :         GcrSecretExchangeDefault *data = exchange->pv->default_exchange;
     774                 :            :         guchar* padded;
     775                 :            :         guchar* result;
     776                 :            :         gsize n_result;
     777                 :            :         gsize pos;
     778                 :            :         gcry_cipher_hd_t cih;
     779                 :            :         gcry_error_t gcry;
     780                 :            : 
     781         [ -  + ]:          8 :         g_return_val_if_fail (data != NULL, FALSE);
     782         [ -  + ]:          8 :         g_return_val_if_fail (data->key != NULL, FALSE);
     783                 :            : 
     784                 :          8 :         g_debug ("decrypting data");
     785                 :            : 
     786   [ +  -  -  + ]:          8 :         if (iv == NULL || n_iv != EXCHANGE_1_IV_LENGTH) {
     787                 :          0 :                 g_message ("secret-exchange: invalid or missing iv");
     788                 :          0 :                 return FALSE;
     789                 :            :         }
     790                 :            : 
     791         [ -  + ]:          8 :         if (n_cipher_text % 16 != 0) {
     792                 :          0 :                 g_message ("secret-message: invalid length for cipher text");
     793                 :          0 :                 return FALSE;
     794                 :            :         }
     795                 :            : 
     796                 :          8 :         gcry = gcry_cipher_open (&cih, EXCHANGE_1_CIPHER_ALGO, EXCHANGE_1_CIPHER_MODE, 0);
     797         [ -  + ]:          8 :         if (gcry != 0) {
     798                 :          0 :                 g_warning ("couldn't create aes cipher context: %s", gcry_strerror (gcry));
     799                 :          0 :                 return FALSE;
     800                 :            :         }
     801                 :            : 
     802                 :            :         /* 16 = 128 bits */
     803                 :          8 :         gcry = gcry_cipher_setkey (cih, data->key, EXCHANGE_1_KEY_LENGTH);
     804         [ -  + ]:          8 :         g_return_val_if_fail (gcry == 0, FALSE);
     805                 :            : 
     806                 :            :         /* 16 = 128 bits */
     807                 :          8 :         gcry = gcry_cipher_setiv (cih, iv, n_iv);
     808         [ -  + ]:          8 :         g_return_val_if_fail (gcry == 0, FALSE);
     809                 :            : 
     810                 :            :         /* Allocate memory for the result */
     811                 :          8 :         padded = (allocator) (NULL, n_cipher_text);
     812         [ -  + ]:          8 :         g_return_val_if_fail (padded != NULL, FALSE);
     813                 :            : 
     814         [ +  + ]:         16 :         for (pos = 0; pos < n_cipher_text; pos += 16) {
     815                 :          8 :                 gcry = gcry_cipher_decrypt (cih, padded + pos, 16, (guchar *)cipher_text + pos, 16);
     816         [ -  + ]:          8 :                 g_return_val_if_fail (gcry == 0, FALSE);
     817                 :            :         }
     818                 :            : 
     819                 :          8 :         gcry_cipher_close (cih);
     820                 :            : 
     821         [ -  + ]:          8 :         if (!egg_padding_pkcs7_unpad (allocator, 16, padded, n_cipher_text,
     822                 :            :                                       (gpointer*)&result, &n_result))
     823                 :          0 :                 result = NULL;
     824                 :            : 
     825                 :            :         /* Free the padded text */
     826                 :          8 :         (allocator) (padded, 0);
     827                 :            : 
     828                 :          8 :         *plain_text = result;
     829                 :          8 :         *n_plain_text = n_result;
     830                 :          8 :         return TRUE;
     831                 :            : }
     832                 :            : 
     833                 :            : static void
     834                 :          2 : gcr_secret_exchange_class_init (GcrSecretExchangeClass *klass)
     835                 :            : {
     836                 :          2 :         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     837                 :            : 
     838                 :          2 :         gobject_class->get_property = gcr_secret_exchange_get_property;
     839                 :          2 :         gobject_class->set_property = gcr_secret_exchange_set_property;
     840                 :          2 :         gobject_class->finalize = gcr_secret_exchange_finalize;
     841                 :            : 
     842                 :          2 :         klass->generate_exchange_key = gcr_secret_exchange_default_generate_exchange_key;
     843                 :          2 :         klass->derive_transport_key = gcr_secret_exchange_default_derive_transport_key;
     844                 :          2 :         klass->decrypt_transport_data = gcr_secret_exchange_default_decrypt_transport_data;
     845                 :          2 :         klass->encrypt_transport_data = gcr_secret_exchange_default_encrypt_transport_data;
     846                 :            : 
     847                 :          2 :         egg_libgcrypt_initialize ();
     848                 :            : 
     849                 :            :         /**
     850                 :            :          * GcrSecretExchange:protocol:
     851                 :            :          *
     852                 :            :          * The protocol being used for the exchange.
     853                 :            :          *
     854                 :            :          * Will be %NULL if no protocol was specified when creating this object,
     855                 :            :          * and either [method@SecretExchange.begin] or [method@SecretExchange.receive]
     856                 :            :          * have not been called successfully.
     857                 :            :          */
     858                 :          2 :         g_object_class_install_property (gobject_class, PROP_PROTOCOL,
     859                 :            :                    g_param_spec_string ("protocol", "Protocol", "Exchange protocol",
     860                 :            :                                         GCR_SECRET_EXCHANGE_PROTOCOL_1,
     861                 :            :                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
     862                 :          2 : }

Generated by: LCOV version 1.14