Now hashing blobs instead of ASCII keys. Old tests run, yay. import_key_2.1
authorKrista 'DarthMama' Bennett <krista@pep.foundation>
Thu, 28 May 2020 20:17:10 +0200
branchimport_key_2.1
changeset 4717333a32f6f7b3
parent 4712 e01853b3f59d
child 4718 a6c15bc68c47
Now hashing blobs instead of ASCII keys. Old tests run, yay.
src/message_api.c
src/message_api.h
src/pEpEngine.c
src/pEp_internal.h
src/pgp_sequoia.c
     1.1 --- a/src/message_api.c	Wed May 13 19:14:18 2020 +0200
     1.2 +++ b/src/message_api.c	Thu May 28 20:17:10 2020 +0200
     1.3 @@ -16,6 +16,7 @@
     1.4  #include <assert.h>
     1.5  #include <string.h>
     1.6  #include <stdlib.h>
     1.7 +#include <stdint.h>
     1.8  #include <math.h>
     1.9  
    1.10  
    1.11 @@ -1515,7 +1516,9 @@
    1.12  bool import_attached_keys(
    1.13          PEP_SESSION session,
    1.14          message *msg,
    1.15 -        identity_list **private_idents
    1.16 +        identity_list **private_idents, 
    1.17 +        stringlist_t** imported_keys,
    1.18 +        uint64_t* changed_keys
    1.19      )
    1.20  {
    1.21      assert(session);
    1.22 @@ -1575,13 +1578,11 @@
    1.23                      continue;
    1.24                  }
    1.25              }
    1.26 -            
    1.27 -            // FIXME: free these guys if we choke, yeah?
    1.28              identity_list *local_private_idents = NULL;
    1.29 -            stringlist_t* imported_keylist = NULL;
    1.30 -            uint64_t changed_keys = 0;
    1.31 -            PEP_STATUS import_status = import_key(session, blob_value, blob_size, &local_private_idents,
    1.32 -                                                  &imported_keylist, &changed_keys);
    1.33 +            PEP_STATUS import_status = import_key(session, blob_value, blob_size, 
    1.34 +                                                  &local_private_idents,
    1.35 +                                                  imported_keys,
    1.36 +                                                  changed_keys);
    1.37              bloblist_t* to_delete = NULL;
    1.38              switch (import_status) {
    1.39                  case PEP_NO_KEY_IMPORTED:
    1.40 @@ -3082,11 +3083,14 @@
    1.41      return status;
    1.42  }
    1.43  
    1.44 -static PEP_STATUS import_priv_keys_from_decrypted_msg(PEP_SESSION session,
    1.45 +// This is misleading - this imports ALL the keys!
    1.46 +static PEP_STATUS import_keys_from_decrypted_msg(PEP_SESSION session,
    1.47                                                        message* msg,
    1.48                                                        bool* imported_keys,
    1.49                                                        bool* imported_private,
    1.50 -                                                      identity_list** private_il)
    1.51 +                                                      identity_list** private_il,
    1.52 +                                                      stringlist_t** keylist,
    1.53 +                                                      uint64_t* changed_keys)
    1.54  {
    1.55      assert(msg && imported_keys && imported_private);
    1.56      if (!(msg && imported_keys && imported_private))
    1.57 @@ -3101,7 +3105,7 @@
    1.58      // check for private key in decrypted message attachment while importing
    1.59      identity_list *_private_il = NULL;
    1.60  
    1.61 -    bool _imported_keys = import_attached_keys(session, msg, &_private_il);
    1.62 +    bool _imported_keys = import_attached_keys(session, msg, &_private_il, keylist, changed_keys);
    1.63      bool _imported_private = false;
    1.64      if (_private_il && _private_il->ident && _private_il->ident->address)
    1.65          _imported_private = true;
    1.66 @@ -3398,7 +3402,7 @@
    1.67      return NULL;
    1.68  }
    1.69  
    1.70 -static bool import_header_keys(PEP_SESSION session, message* src) {
    1.71 +static bool import_header_keys(PEP_SESSION session, message* src, stringlist_t** imported_keys, uint64_t* changed_keys) {
    1.72      stringpair_list_t* header_keys = stringpair_list_find(src->opt_fields, "Autocrypt"); 
    1.73      if (!header_keys || !header_keys->value)
    1.74          return false;
    1.75 @@ -3413,10 +3417,7 @@
    1.76      bloblist_t* the_key = base64_str_to_binary_blob(start_key, length);
    1.77      if (!the_key)
    1.78          return false;
    1.79 -    stringlist_t* imported_keys = NULL;
    1.80 -    uint64_t changed_keys = 0;    
    1.81 -    PEP_STATUS status = import_key(session, the_key->value, the_key->size, NULL,
    1.82 -                                   &imported_keys, &changed_keys);
    1.83 +    PEP_STATUS status = import_key(session, the_key->value, the_key->size, NULL, imported_keys, changed_keys);
    1.84      free_bloblist(the_key);
    1.85      if (status == PEP_STATUS_OK || status == PEP_KEY_IMPORTED)
    1.86          return true;
    1.87 @@ -3593,6 +3594,9 @@
    1.88      unsigned int major_ver = 0;
    1.89      unsigned int minor_ver = 0;
    1.90      
    1.91 +    stringlist_t* import_keylist = NULL;
    1.92 +    uint64_t changed_keys = 0;
    1.93 +    
    1.94      stringpair_list_t* revoke_replace_pairs = NULL;
    1.95      
    1.96      // Grab input flags
    1.97 @@ -3647,9 +3651,9 @@
    1.98      bool imported_keys = false;
    1.99      PEP_cryptotech enc_type = determine_encryption_format(src);
   1.100      if (enc_type != PEP_crypt_OpenPGP || !(src->enc_format == PEP_enc_PGP_MIME || src->enc_format == PEP_enc_PGP_MIME_Outlook1))
   1.101 -        imported_keys = import_attached_keys(session, src, NULL);
   1.102 -            
   1.103 -    import_header_keys(session, src);
   1.104 +        imported_keys = import_attached_keys(session, src, NULL, &import_keylist, &changed_keys);
   1.105 +    
   1.106 +    import_header_keys(session, src, &import_keylist, &changed_keys);
   1.107      
   1.108      // FIXME: is this really necessary here?
   1.109      // if (src->from) {
   1.110 @@ -3738,10 +3742,12 @@
   1.111                  //
   1.112                  // We are importing from the decrypted outermost message now.
   1.113                  //
   1.114 -                status = import_priv_keys_from_decrypted_msg(session, msg,
   1.115 +                status = import_keys_from_decrypted_msg(session, msg,
   1.116                                                               &imported_keys,
   1.117                                                               &imported_private_key_address,
   1.118 -                                                             private_il);
   1.119 +                                                             private_il,
   1.120 +                                                             &import_keylist,
   1.121 +                                                             &changed_keys);
   1.122                  if (status != PEP_STATUS_OK)
   1.123                      goto pEp_error;            
   1.124  
   1.125 @@ -3981,10 +3987,12 @@
   1.126                              private_il = NULL;
   1.127                              
   1.128                              // import keys from decrypted INNER source
   1.129 -                            status = import_priv_keys_from_decrypted_msg(session, inner_message,
   1.130 +                            status = import_keys_from_decrypted_msg(session, inner_message,
   1.131                                                                           &imported_keys,
   1.132                                                                           &imported_private_key_address,
   1.133 -                                                                         private_il);
   1.134 +                                                                         private_il,
   1.135 +                                                                         &import_keylist,
   1.136 +                                                                         &changed_keys);
   1.137                              if (status != PEP_STATUS_OK)
   1.138                                  goto pEp_error;            
   1.139                          }
     2.1 --- a/src/message_api.h	Wed May 13 19:14:18 2020 +0200
     2.2 +++ b/src/message_api.h	Thu May 28 20:17:10 2020 +0200
     2.3 @@ -13,9 +13,11 @@
     2.4  #endif
     2.5  
     2.6  bool import_attached_keys(
     2.7 -        PEP_SESSION session, 
     2.8 +        PEP_SESSION session,
     2.9          message *msg,
    2.10 -        identity_list **private_idents
    2.11 +        identity_list **private_idents, 
    2.12 +        stringlist_t** imported_keys,
    2.13 +        uint64_t* changed_keys
    2.14      );
    2.15  
    2.16  void attach_own_key(PEP_SESSION session, message *msg);
     3.1 --- a/src/pEpEngine.c	Wed May 13 19:14:18 2020 +0200
     3.2 +++ b/src/pEpEngine.c	Thu May 28 20:17:10 2020 +0200
     3.3 @@ -4863,6 +4863,9 @@
     3.4  
     3.5      if (!(session && key_data))
     3.6          return PEP_ILLEGAL_VALUE;
     3.7 +        
     3.8 +    if (imported_keys && !*imported_keys && changed_key_index)
     3.9 +        *changed_key_index = 0;
    3.10  
    3.11      return session->cryptotech[PEP_crypt_OpenPGP].import_key(session, key_data,
    3.12              size, private_keys, imported_keys, changed_key_index);
     4.1 --- a/src/pEp_internal.h	Wed May 13 19:14:18 2020 +0200
     4.2 +++ b/src/pEp_internal.h	Thu May 28 20:17:10 2020 +0200
     4.3 @@ -135,8 +135,9 @@
     4.4          sqlite3_stmt *cert_save_insert_userids;
     4.5          sqlite3_stmt *delete_keypair;
     4.6          // engine convenience hacks
     4.7 -        sqlite3_stmt *insert_ascii_import_hash;
     4.8 -        sqlite3_stmt *get_ascii_import_hash_for_fpr;
     4.9 +        sqlite3_stmt *insert_blob_import_hash;
    4.10 +        sqlite3_stmt *get_blob_import_hash_for_fpr;
    4.11 +        sqlite3_stmt *delete_blob_import_hash_for_fpr;        
    4.12      } sq_sql;
    4.13  
    4.14      pgp_policy_t policy;
     5.1 --- a/src/pgp_sequoia.c	Wed May 13 19:14:18 2020 +0200
     5.2 +++ b/src/pgp_sequoia.c	Thu May 28 20:17:10 2020 +0200
     5.3 @@ -368,7 +368,7 @@
     5.4                    sqlite3_errmsg(session->key_db));
     5.5  
     5.6      sqlite_result = sqlite3_exec(session->key_db,
     5.7 -                                 "CREATE TABLE IF NOT EXISTS ascii_import_hashes (\n"
     5.8 +                                 "CREATE TABLE IF NOT EXISTS blob_import_hashes (\n"
     5.9                                   "   fpr TEXT PRIMARY KEY NOT NULL,\n"
    5.10                                   "   last_import_hash INT,\n"
    5.11                                   "   UNIQUE(fpr, last_import_hash)\n"
    5.12 @@ -377,7 +377,7 @@
    5.13                                   
    5.14      if (sqlite_result != SQLITE_OK)
    5.15          ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
    5.16 -                  "creating ascii import hashes table: %s",
    5.17 +                  "creating blob import hashes table: %s",
    5.18                    sqlite3_errmsg(session->key_db));
    5.19  
    5.20      sqlite_result
    5.21 @@ -501,17 +501,17 @@
    5.22      // possibly-changed key *files*. This is ONLY a heuristic
    5.23      sqlite_result
    5.24          = sqlite3_prepare_v2(session->key_db,
    5.25 -                             "INSERT OR REPLACE INTO ascii_import_hashes"
    5.26 +                             "INSERT OR REPLACE INTO blob_import_hashes"
    5.27                               "    (fpr, last_import_hash)"
    5.28                               " VALUES (?, ?)",
    5.29 -                             -1, &session->sq_sql.insert_ascii_import_hash, NULL);
    5.30 +                             -1, &session->sq_sql.insert_blob_import_hash, NULL);
    5.31      assert(sqlite_result == SQLITE_OK);
    5.32  
    5.33      sqlite_result
    5.34          = sqlite3_prepare_v2(session->key_db,
    5.35 -                             "select last_import_hash from ascii_import_hashes"
    5.36 +                             "select last_import_hash from blob_import_hashes"
    5.37                               "    where fpr = ?1",
    5.38 -                             -1, &session->sq_sql.get_ascii_import_hash_for_fpr, NULL);
    5.39 +                             -1, &session->sq_sql.get_blob_import_hash_for_fpr, NULL);
    5.40      assert(sqlite_result == SQLITE_OK);
    5.41      
    5.42      session->policy = pgp_null_policy ();
    5.43 @@ -793,13 +793,110 @@
    5.44  }
    5.45  
    5.46  
    5.47 +// Fun stuff for import key and heuristics follows...
    5.48 +//
    5.49 +
    5.50 +// start detect possibly changed key stuff
    5.51 +
    5.52 +// NON-CRYPTOGRAPHIC HASH: take note - this is djb2 with XOR and is NEVER used 
    5.53 +// by sequoia or for any cryptographic purpose. We are using this to determine 
    5.54 +// if two ascii-armored keys for a given primary key are different, and that 
    5.55 +// is all. This is engine internal and our way of heuristically and cheaply 
    5.56 +// determining if a given key has *possibly* changed - we don't mind false 
    5.57 +// positives.
    5.58 +static uint32_t _non_crypto_hash_djb2(const char* str, const char* end) {
    5.59 +    uint32_t hash = 5381; // will also be returned if !str. That's OK.
    5.60 +    int c;
    5.61 +
    5.62 +    while ((str < end) && (c = *str++))
    5.63 +        hash = ((hash << 5) + hash) ^ c; // hash(i - 1) * 33 ^ str[i]
    5.64 +
    5.65 +    return hash;
    5.66 +} 
    5.67 +
    5.68 +static PEP_STATUS _has_hash_changed(PEP_SESSION session,
    5.69 +                                    uint32_t test_hash, 
    5.70 +                                    const char* fpr, 
    5.71 +                                    bool* differs) {
    5.72 +
    5.73 +    bool changed = false;
    5.74 +    sqlite3_stmt* stmt = session->sq_sql.get_blob_import_hash_for_fpr;
    5.75 +    sqlite3_bind_text(stmt, 1, 
    5.76 +                      fpr, -1, SQLITE_STATIC);
    5.77 +    int sqlite_result = sqlite3_step(stmt);                  
    5.78 +
    5.79 +    uint32_t old_hash = 0;
    5.80 +    
    5.81 +    PEP_STATUS status = PEP_STATUS_OK;
    5.82 +    
    5.83 +    switch (sqlite_result) {
    5.84 +        case SQLITE_ROW:
    5.85 +            old_hash = (uint32_t) sqlite3_column_int(stmt, 0);
    5.86 +            changed = (old_hash != test_hash);
    5.87 +            break;
    5.88 +        case SQLITE_DONE:
    5.89 +            // Not yet in database. Whee!
    5.90 +            changed = true;
    5.91 +            break;
    5.92 +        default:
    5.93 +           ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
    5.94 +                     "stepping: %s", sqlite3_errmsg(session->key_db));
    5.95 +    }
    5.96 +                    
    5.97 +    *differs = changed;
    5.98 +                                        
    5.99 +out:
   5.100 +    sqlite3_reset(stmt);
   5.101 +    T(" -> %s", pEp_status_to_string(status));
   5.102 +    return status;
   5.103 +}
   5.104 +
   5.105 +static PEP_STATUS _update_blob_hash_for_key(PEP_SESSION session, 
   5.106 +                                             uint32_t new_hash, 
   5.107 +                                             const char* fpr) {
   5.108 +    PEP_STATUS status = PEP_STATUS_OK;
   5.109 +    sqlite3_stmt* stmt = session->sq_sql.insert_blob_import_hash;
   5.110 +    sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
   5.111 +    sqlite3_bind_int(stmt, 2, new_hash);                      
   5.112 +    int sqlite_result = sqlite3_step(stmt);
   5.113 +    sqlite3_reset(stmt);    
   5.114 +    if (sqlite_result != SQLITE_DONE)
   5.115 +        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
   5.116 +                  "Updating hash: %s", sqlite3_errmsg(session->key_db));
   5.117 +
   5.118 +out:
   5.119 +    T(" -> %s", pEp_status_to_string(status));
   5.120 +    return status;    
   5.121 +}
   5.122 +
   5.123 +static PEP_STATUS _detect_and_update_changed_key_blob(PEP_SESSION session,
   5.124 +                                                      const char* keydata_begin,
   5.125 +                                                      size_t keydata_len,
   5.126 +                                                      const char* fpr,
   5.127 +                                                      bool* changed) {
   5.128 +    if (!changed)
   5.129 +        return PEP_ILLEGAL_VALUE;
   5.130 +    
   5.131 +    uint32_t hashval = _non_crypto_hash_djb2(keydata_begin, keydata_begin + keydata_len);    
   5.132 +    PEP_STATUS status = _has_hash_changed(session, hashval, fpr, changed);
   5.133 +
   5.134 +    if (status != PEP_STATUS_OK)
   5.135 +        return status;
   5.136 +    
   5.137 +    if (*changed)
   5.138 +        status = _update_blob_hash_for_key(session, hashval, fpr);    
   5.139 +    
   5.140 +    return status;    
   5.141 +}
   5.142 +// end detect possibly changed key stuff
   5.143 +
   5.144  // Saves the specified certificates.
   5.145  //
   5.146  // This function takes ownership of CERT.
   5.147 -static PEP_STATUS cert_save(PEP_SESSION, pgp_cert_t, identity_list **)
   5.148 +static PEP_STATUS cert_save(PEP_SESSION, pgp_cert_t, identity_list **, bool* changed_ptr)
   5.149      __attribute__((nonnull(1, 2)));
   5.150  static PEP_STATUS cert_save(PEP_SESSION session, pgp_cert_t cert,
   5.151 -                           identity_list **private_idents)
   5.152 +                           identity_list **private_idents, bool* changed_ptr)
   5.153  {
   5.154      PEP_STATUS status = PEP_STATUS_OK;
   5.155      pgp_error_t err = NULL;
   5.156 @@ -812,6 +909,8 @@
   5.157      pgp_user_id_bundle_iter_t user_id_iter = NULL;
   5.158      char *email = NULL;
   5.159      char *name = NULL;
   5.160 +    
   5.161 +    bool _changed = false;    
   5.162  
   5.163      sqlite3_stmt *stmt = session->sq_sql.begin_transaction;
   5.164      int sqlite_result = sqlite3_step(stmt);
   5.165 @@ -841,6 +940,9 @@
   5.166      int is_tsk = pgp_cert_is_tsk(cert);
   5.167  
   5.168      // Serialize it.
   5.169 +    // NOTE: Just because it's called tsk in tsk_buffer does NOT mean it necessarily 
   5.170 +    //       has secret key material; it is just that is could. is_tsk is the 
   5.171 +    //       part that asks whether or not it contains such.
   5.172      pgp_writer_t writer = pgp_writer_alloc(&tsk_buffer, &tsk_buffer_len);
   5.173      if (! writer)
   5.174          ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
   5.175 @@ -853,7 +955,16 @@
   5.176      if (pgp_status != 0)
   5.177          ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Serializing certificates");
   5.178  
   5.179 -
   5.180 +    // Before we do anything else, if we need to know if things MAY have changed, 
   5.181 +    // we check the key blob (this is not comprehensive and can generate false 
   5.182 +    // positives)
   5.183 +    //
   5.184 +    if (changed_ptr) {
   5.185 +        status = _detect_and_update_changed_key_blob(session, tsk_buffer, tsk_buffer_len, fpr, &_changed);
   5.186 +        if (status != PEP_STATUS_OK)
   5.187 +            ERROR_OUT(NULL, status, "Checking/updating hash for tsk for: %s", fpr);
   5.188 +    }
   5.189 +                    
   5.190      // Insert the TSK into the DB.
   5.191      stmt = session->sq_sql.cert_save_insert_primary;
   5.192      sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
   5.193 @@ -918,7 +1029,7 @@
   5.194          free(email);
   5.195          email = NULL;
   5.196  
   5.197 -        pgp_packet_t userid = pgp_user_id_new (user_id_value);
   5.198 +        pgp_packet_t userid = pgp_user_id_new(user_id_value);
   5.199          pgp_user_id_name(NULL, userid, &name);
   5.200          // Try to get the normalized address.
   5.201          pgp_user_id_email_normalized(NULL, userid, &email);
   5.202 @@ -980,12 +1091,15 @@
   5.203  
   5.204      T("(%s) -> %s", fpr, pEp_status_to_string(status));
   5.205  
   5.206 +    if (changed_ptr && status == PEP_STATUS_OK)
   5.207 +        *changed_ptr = _changed;
   5.208 +
   5.209      free(email);
   5.210      free(name);
   5.211      pgp_user_id_bundle_iter_free(user_id_iter);
   5.212      pgp_cert_key_iter_free(key_iter);
   5.213      if (stmt)
   5.214 -      sqlite3_reset(stmt);
   5.215 +        sqlite3_reset(stmt);
   5.216      free(tsk_buffer);
   5.217      pgp_cert_free(cert);
   5.218      free(fpr);
   5.219 @@ -2206,7 +2320,7 @@
   5.220      pgp_fpr = pgp_cert_fingerprint(cert);
   5.221      fpr = pgp_fingerprint_to_hex(pgp_fpr);
   5.222  
   5.223 -    status = cert_save(session, cert, NULL);
   5.224 +    status = cert_save(session, cert, NULL, NULL);
   5.225      cert = NULL;
   5.226      if (status != 0)
   5.227          ERROR_OUT(NULL, PEP_CANNOT_CREATE_KEY, "saving TSK");
   5.228 @@ -2264,135 +2378,6 @@
   5.229      return status;
   5.230  }
   5.231  
   5.232 -// Fun stuff for import key and heuristics follows...
   5.233 -//
   5.234 -
   5.235 -// start detect possibly changed key stuff
   5.236 -
   5.237 -// NON-CRYPTOGRAPHIC HASH: take note - this is djb2 with XOR and is NEVER used 
   5.238 -// by sequoia or for any cryptographic purpose. We are using this to determine 
   5.239 -// if two ascii-armored keys for a given primary key are different, and that 
   5.240 -// is all. This is engine internal and our way of heuristically and cheaply 
   5.241 -// determining if a given key has *possibly* changed - we don't mind false 
   5.242 -// positives.
   5.243 -static uint32_t _non_crypto_hash_djb2(const char* str, const char* end) {
   5.244 -    uint32_t hash = 5381; // will also be returned if !str. That's OK.
   5.245 -    int c;
   5.246 -
   5.247 -    while ((str < end) && (c = *str++))
   5.248 -        hash = ((hash << 5) + hash) ^ c; // hash(i - 1) * 33 ^ str[i]
   5.249 -
   5.250 -    return hash;
   5.251 -} 
   5.252 -
   5.253 -static void _trim_ascii_key(const char* keystring, const char** start, 
   5.254 -                            const char** stop) {
   5.255 -    *start = NULL;
   5.256 -    *stop = NULL;
   5.257 -    
   5.258 -    const char* header = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
   5.259 -    const char* footer = "-----END PGP PUBLIC KEY BLOCK-----";    
   5.260 -    const int _KEY_HEADER_LEN = 36; // strlen(header)
   5.261 -    const char* begin = strstr(keystring, header);
   5.262 -    if (!begin)
   5.263 -        return;
   5.264 -    begin = header + _KEY_HEADER_LEN;
   5.265 -    const char* end = strstr(begin, footer);
   5.266 -    if (!end)
   5.267 -        return;
   5.268 -    while (isspace(*begin) && begin < end) {
   5.269 -        begin++;
   5.270 -    }
   5.271 -    if (begin == end)
   5.272 -        return;
   5.273 -    while (isspace(*end) && end > begin) {
   5.274 -        end--;
   5.275 -    }
   5.276 -    if (end == begin)
   5.277 -        return;
   5.278 -    *start = begin;
   5.279 -    *stop = end + 1;        
   5.280 -}
   5.281 -
   5.282 -static PEP_STATUS _has_hash_changed(PEP_SESSION session,
   5.283 -                                    uint32_t test_hash, 
   5.284 -                                    const char* fpr, 
   5.285 -                                    bool* differs) {
   5.286 -
   5.287 -    bool changed = false;
   5.288 -    sqlite3_stmt* stmt = session->sq_sql.get_ascii_import_hash_for_fpr;
   5.289 -    sqlite3_bind_text(stmt, 1, 
   5.290 -                      fpr, -1, SQLITE_STATIC);
   5.291 -    int sqlite_result = sqlite3_step(stmt);                  
   5.292 -
   5.293 -    uint32_t old_hash = 0;
   5.294 -    
   5.295 -    PEP_STATUS status = PEP_STATUS_OK;
   5.296 -    
   5.297 -    switch (sqlite_result) {
   5.298 -        case SQLITE_ROW:
   5.299 -            old_hash = (uint32_t) sqlite3_column_int(stmt, 0);
   5.300 -            changed = (old_hash != test_hash);
   5.301 -            break;
   5.302 -        case SQLITE_DONE:
   5.303 -            // Not yet in database. Whee!
   5.304 -            changed = true;
   5.305 -            break;
   5.306 -        default:
   5.307 -           ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
   5.308 -                     "stepping: %s", sqlite3_errmsg(session->key_db));
   5.309 -    }
   5.310 -                    
   5.311 -    *differs = changed;
   5.312 -                                        
   5.313 -out:
   5.314 -    sqlite3_reset(stmt);
   5.315 -    T(" -> %s", pEp_status_to_string(status));
   5.316 -    return status;
   5.317 -}
   5.318 -
   5.319 -static PEP_STATUS _update_ascii_hash_for_key(PEP_SESSION session, 
   5.320 -                                             uint32_t new_hash, 
   5.321 -                                             const char* fpr) {
   5.322 -    PEP_STATUS status = PEP_STATUS_OK;
   5.323 -    sqlite3_stmt* stmt = session->sq_sql.insert_ascii_import_hash;
   5.324 -    sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
   5.325 -    sqlite3_bind_int(stmt, 2, new_hash);                      
   5.326 -    int sqlite_result = sqlite3_step(stmt);
   5.327 -    sqlite3_reset(stmt);    
   5.328 -    if (sqlite_result != SQLITE_DONE)
   5.329 -        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
   5.330 -                  "Updating hash: %s", sqlite3_errmsg(session->key_db));
   5.331 -
   5.332 -out:
   5.333 -    T(" -> %s", pEp_status_to_string(status));
   5.334 -    return status;    
   5.335 -}
   5.336 -
   5.337 -static PEP_STATUS _detect_and_update_changed_ascii_key(PEP_SESSION session,
   5.338 -                                                       const char* keydata,
   5.339 -                                                       const char* fpr,
   5.340 -                                                       bool* changed) {
   5.341 -    const char* keydata_begin = NULL;
   5.342 -    const char* keydata_end = NULL;
   5.343 -    
   5.344 -    _trim_ascii_key(keydata, &keydata_begin, &keydata_end);
   5.345 -    if (!(keydata_begin && keydata_end))
   5.346 -        return PEP_ILLEGAL_VALUE;
   5.347 -    
   5.348 -    uint32_t hashval = _non_crypto_hash_djb2(keydata_begin, keydata_end);    
   5.349 -    PEP_STATUS status = _has_hash_changed(session, hashval, fpr, changed);
   5.350 -
   5.351 -    if (status != PEP_STATUS_OK)
   5.352 -        return status;
   5.353 -    
   5.354 -    if (*changed)
   5.355 -        status = _update_ascii_hash_for_key(session, hashval, fpr);    
   5.356 -    
   5.357 -    return status;    
   5.358 -}
   5.359 -// end detect possibly changed key stuff
   5.360 -
   5.361  static unsigned int count_keydata_parts(const char* key_data, size_t size) {
   5.362      unsigned int retval = 0;
   5.363  
   5.364 @@ -2417,22 +2402,22 @@
   5.365  PEP_STATUS _pgp_import_keydata(PEP_SESSION session, const char *key_data,
   5.366                                 size_t size, identity_list **private_idents,
   5.367                                 stringlist_t** imported_keys,
   5.368 -                               bool* changed)
   5.369 +                               uint64_t* changed_bitvec)
   5.370  {
   5.371      PEP_STATUS status = PEP_NO_KEY_IMPORTED;
   5.372      pgp_error_t err;
   5.373      pgp_cert_parser_t parser = NULL;
   5.374 -    stringlist_t* keylist = NULL;
   5.375 -    *changed = false;
   5.376 +    char* issuer_fpr_hex = NULL;
   5.377 +    char* cert_fpr_hex = NULL;
   5.378      
   5.379 -    if (imported_keys) {
   5.380 -        keylist = ((*imported_keys) ? *imported_keys : new_stringlist(NULL));
   5.381 -        if (!keylist)
   5.382 -            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "setting retval keylist");
   5.383 -    }    
   5.384 +    if (changed_bitvec && !imported_keys)
   5.385 +        return PEP_ILLEGAL_VALUE;    
   5.386  
   5.387      if (private_idents)
   5.388          *private_idents = NULL;
   5.389 +
   5.390 +    stringlist_t* _import_keylist = imported_keys ? *imported_keys : NULL;    
   5.391 +    int _import_keylist_len = stringlist_length(_import_keylist);    
   5.392          
   5.393      T("parsing %zd bytes", size);
   5.394  
   5.395 @@ -2460,8 +2445,11 @@
   5.396          pgp_cert_t cert = NULL;
   5.397  
   5.398          pgp_fingerprint_t issuer_fpr = pgp_signature_issuer_fingerprint(sig);
   5.399 +        
   5.400 +        char* issuer_fpr_hex = NULL;
   5.401 +
   5.402          if (issuer_fpr) {
   5.403 -            char *issuer_fpr_hex = pgp_fingerprint_to_hex(issuer_fpr);
   5.404 +            issuer_fpr_hex = pgp_fingerprint_to_hex(issuer_fpr);
   5.405              T("Importing a signature issued by %s", issuer_fpr_hex);
   5.406  
   5.407              status = cert_find_by_fpr_hex(session, issuer_fpr_hex,
   5.408 @@ -2469,22 +2457,20 @@
   5.409              if (status && status != PEP_KEY_NOT_FOUND)
   5.410                  DUMP_ERR(NULL, status, "Looking up %s", issuer_fpr_hex);
   5.411  
   5.412 -            free(issuer_fpr_hex);
   5.413              pgp_fingerprint_free(issuer_fpr);
   5.414          }
   5.415  
   5.416          if (! cert) {
   5.417              pgp_keyid_t issuer = pgp_signature_issuer(sig);
   5.418              if (issuer) {
   5.419 -                char *issuer_hex = pgp_keyid_to_hex(issuer);
   5.420 -                T("Importing a signature issued by %s", issuer_hex);
   5.421 -
   5.422 -                status = cert_find_by_keyid_hex(session, issuer_hex,
   5.423 +                issuer_fpr_hex = pgp_keyid_to_hex(issuer);
   5.424 +                T("Importing a signature issued by %s", issuer_fpr_hex);
   5.425 +
   5.426 +                status = cert_find_by_keyid_hex(session, issuer_fpr_hex,
   5.427                                                 false, &cert, NULL);
   5.428                  if (status && status != PEP_KEY_NOT_FOUND)
   5.429 -                    DUMP_ERR(NULL, status, "Looking up %s", issuer_hex);
   5.430 -
   5.431 -                free(issuer_hex);
   5.432 +                    DUMP_ERR(NULL, status, "Looking up %s", issuer_fpr_hex);
   5.433 +
   5.434                  pgp_keyid_free(issuer);
   5.435              }
   5.436          }
   5.437 @@ -2500,7 +2486,18 @@
   5.438              if (! cert)
   5.439                  ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Merging signature");
   5.440  
   5.441 -            status = cert_save(session, cert, NULL);
   5.442 +            bool changed = false;  
   5.443 +              
   5.444 +            status = cert_save(session, cert, NULL, changed_bitvec ? &changed : NULL);
   5.445 +            if (imported_keys) {
   5.446 +                if (_import_keylist)
   5.447 +                    stringlist_add(_import_keylist, issuer_fpr_hex);
   5.448 +                else 
   5.449 +                    _import_keylist = new_stringlist(issuer_fpr_hex);
   5.450 +                
   5.451 +                if (changed_bitvec && changed)
   5.452 +                    *changed_bitvec |= 1 << _import_keylist_len;
   5.453 +            }
   5.454              if (status)
   5.455                  ERROR_OUT(NULL, status, "saving merged CERT");
   5.456              status = PEP_KEY_IMPORTED;
   5.457 @@ -2513,33 +2510,38 @@
   5.458          pgp_cert_t cert;
   5.459          int count = 0;
   5.460          err = NULL;
   5.461 +        
   5.462          while ((cert = pgp_cert_parser_next(&err, parser))) {
   5.463              count ++;
   5.464  
   5.465 +            char* cert_fpr_hex = pgp_fingerprint_to_hex(pgp_cert_fingerprint(cert)); 
   5.466              T("#%d. CERT for %s, %s",
   5.467                count, pgp_cert_primary_user_id(cert, session->policy, 0),
   5.468 -              pgp_fingerprint_to_hex(pgp_cert_fingerprint(cert)));
   5.469 +              cert_fpr_hex);
   5.470  
   5.471              // If private_idents is not NULL and there is any private key
   5.472 -            // material, it will be saved.
   5.473 -            status = cert_save(session, cert, private_idents);
   5.474 +            // material, then we'll put an entry for it into private_idents 
   5.475 +            bool changed = false;
   5.476 +            status = cert_save(session, cert, private_idents, changed_bitvec ? &changed : NULL);
   5.477              if (status == PEP_STATUS_OK) {
   5.478                  status = PEP_KEY_IMPORTED;
   5.479                  if (imported_keys) {
   5.480 -                    char* fpr = pgp_fingerprint_to_hex(pgp_cert_fingerprint(cert));
   5.481 -                    if (!fpr) {
   5.482 -                        status = PEP_UNKNOWN_ERROR; // KB: Should we do this?
   5.483 -                        ERROR_OUT(NULL, status, "getting cert fingerprint hex");                    
   5.484 -                    }
   5.485 -                    stringlist_add(keylist, fpr); 
   5.486 -                    
   5.487 -                    status = _detect_and_update_changed_ascii_key(session, key_data, fpr, changed);  
   5.488 -                    if (status != PEP_STATUS_OK)
   5.489 -                        ERROR_OUT(NULL, status, "detecting / changing ascii key in DB");                    
   5.490 +                    if (_import_keylist)
   5.491 +                        stringlist_add(_import_keylist, cert_fpr_hex);
   5.492 +                    else
   5.493 +                        _import_keylist = new_stringlist(cert_fpr_hex);
   5.494 +                        
   5.495 +                    if (_import_keylist_len < 64 && changed) {
   5.496 +                        *changed_bitvec |= 1 << _import_keylist_len;
   5.497 +                    }   
   5.498 +                    _import_keylist_len++;
   5.499                  }    
   5.500              }    
   5.501              else
   5.502                  ERROR_OUT(NULL, status, "saving certificate");
   5.503 +            
   5.504 +            free(cert_fpr_hex);
   5.505 +            cert_fpr_hex = NULL;
   5.506          }
   5.507          if (err || count == 0)
   5.508              ERROR_OUT(err, PEP_UNKNOWN_ERROR, "parsing key data");
   5.509 @@ -2565,6 +2567,12 @@
   5.510   out:
   5.511      pgp_cert_parser_free(parser);
   5.512  
   5.513 +    if (imported_keys && status == PEP_KEY_IMPORTED)
   5.514 +        *imported_keys = _import_keylist;
   5.515 +    
   5.516 +    free(issuer_fpr_hex);
   5.517 +    free(cert_fpr_hex);    
   5.518 +        
   5.519      T("-> %s", pEp_status_to_string(status));
   5.520      return status;
   5.521  }
   5.522 @@ -2574,8 +2582,9 @@
   5.523                                stringlist_t** imported_keys,
   5.524                                uint64_t* changed_key_index)
   5.525  {
   5.526 -    *changed_key_index = 0;
   5.527 -    
   5.528 +    if (imported_keys && !changed_key_index)
   5.529 +        return PEP_ILLEGAL_VALUE;
   5.530 +        
   5.531      const char* pgp_begin = "-----BEGIN PGP";
   5.532      size_t prefix_len = strlen(pgp_begin);
   5.533      
   5.534 @@ -2594,12 +2603,8 @@
   5.535  
   5.536      unsigned int keycount = count_keydata_parts(key_data, size);
   5.537      if (keycount < 2) {
   5.538 -        bool changed = false;
   5.539          retval = _pgp_import_keydata(session, key_data, size, private_idents,
   5.540 -                                     imported_keys, &changed);
   5.541 -        if (retval == PEP_KEY_IMPORTED)
   5.542 -            *changed_key_index = 1; // only one key 
   5.543 -        
   5.544 +                                     imported_keys, changed_key_index);        
   5.545          return retval;    
   5.546      }        
   5.547  
   5.548 @@ -2610,8 +2615,6 @@
   5.549      identity_list* collected_idents = NULL;
   5.550  
   5.551      retval = PEP_KEY_IMPORTED;
   5.552 -    
   5.553 -    int imported_key_index = 0;
   5.554              
   5.555      for (i = 0, curr_begin = key_data; i < keycount; i++) {
   5.556          const char* next_begin = NULL;
   5.557 @@ -2627,13 +2630,12 @@
   5.558          else
   5.559              curr_size = (key_data + size) - curr_begin;
   5.560  
   5.561 -        bool changed = false;    
   5.562          PEP_STATUS curr_status = _pgp_import_keydata(session, 
   5.563                                                       curr_begin, 
   5.564                                                       curr_size, 
   5.565                                                       private_idents,
   5.566                                                       imported_keys,
   5.567 -                                                     &changed);
   5.568 +                                                     changed_key_index);
   5.569          if (private_idents && *private_idents) {
   5.570              if (!collected_idents)
   5.571                  collected_idents = *private_idents;
   5.572 @@ -2642,10 +2644,6 @@
   5.573              *private_idents = NULL;
   5.574          }
   5.575  
   5.576 -        // Set changed bit for appropriately-indexed key
   5.577 -        if (changed && curr_status == PEP_KEY_IMPORTED)
   5.578 -            *changed_key_index |= (1 << imported_key_index++);
   5.579 -
   5.580          if (curr_status != retval) {
   5.581              switch (curr_status) {
   5.582                  case PEP_NO_KEY_IMPORTED:
   5.583 @@ -3081,7 +3079,7 @@
   5.584      if (! cert)
   5.585          ERROR_OUT(err, PEP_UNKNOWN_ERROR, "setting expiration (updating cert)");
   5.586  
   5.587 -    status = cert_save(session, cert, NULL);
   5.588 +    status = cert_save(session, cert, NULL, NULL);
   5.589      cert = NULL;
   5.590      ERROR_OUT(NULL, status, "Saving %s", fpr);
   5.591  
   5.592 @@ -3159,7 +3157,7 @@
   5.593      assert(pgp_revocation_status_variant(pgp_cert_revoked(cert, session->policy, 0))
   5.594             == PGP_REVOCATION_STATUS_REVOKED);
   5.595  
   5.596 -    status = cert_save(session, cert, NULL);
   5.597 +    status = cert_save(session, cert, NULL, NULL);
   5.598      cert = NULL;
   5.599      ERROR_OUT(NULL, status, "Saving %s", fpr);
   5.600