src/key_reset.c
author Krista 'DarthMama' Bennett <krista@pep.foundation>
Thu, 04 Jun 2020 11:18:45 +0200
changeset 4729 3df9a2a67597
parent 4677 00432c431c53
child 4792 7056435ab9e7
child 4801 fa3ab5c2f6df
permissions -rw-r--r--
forgot test files
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 #include "pEp_internal.h"
     5 #include "dynamic_api.h"
     6 #include "message_api.h"
     7 #include "key_reset.h"
     8 #include "distribution_codec.h"
     9 #include "map_asn1.h"
    10 #include "keymanagement.h"
    11 #include "baseprotocol.h"
    12 #include "../asn.1/Distribution.h"
    13 #include "Sync_impl.h" // this seems... bad
    14 
    15 #include <string.h>
    16 #include <stdlib.h>
    17 
    18 // FIXME: these should be taken from sync/Distribution.fsm
    19 
    20 #define KEY_RESET_MAJOR_VERSION 1L
    21 #define KEY_RESET_MINOR_VERSION 0L
    22 
    23 static void _add_auto_consume(message* msg) {
    24     add_opt_field(msg, "pEp-auto-consume", "yes");
    25     msg->in_reply_to = stringlist_add(msg->in_reply_to, "pEp-auto-consume@pEp.foundation");
    26 }
    27 
    28 static PEP_STATUS _generate_reset_structs(PEP_SESSION session,
    29                                           const pEp_identity* reset_ident,
    30                                           const char* old_fpr,
    31                                           const char* new_fpr,
    32                                           bloblist_t** key_attachments,
    33                                           keyreset_command_list** command_list,
    34                                           bool include_secret) {
    35 
    36     if (!session || !reset_ident || EMPTYSTR(old_fpr) || EMPTYSTR(new_fpr) ||
    37         !key_attachments || !command_list)
    38         return PEP_ILLEGAL_VALUE;
    39     
    40     // Ok, generate payload here...
    41     pEp_identity* outgoing_ident = identity_dup(reset_ident);
    42     if (!outgoing_ident)
    43         return PEP_OUT_OF_MEMORY;
    44     free(outgoing_ident->fpr);
    45     outgoing_ident->fpr = strdup(old_fpr);
    46     if (!outgoing_ident->fpr)
    47         return PEP_OUT_OF_MEMORY;
    48         
    49     keyreset_command* kr_command = new_keyreset_command(outgoing_ident, new_fpr);
    50     if (!kr_command)
    51         return PEP_OUT_OF_MEMORY;
    52     if (!*command_list)
    53         *command_list = new_keyreset_command_list(kr_command);
    54     else
    55         if (keyreset_command_list_add(*command_list, kr_command) == NULL)
    56             return PEP_OUT_OF_MEMORY;
    57     
    58     bloblist_t* keys = NULL;
    59     
    60     char* key_material_old = NULL;
    61     char* key_material_new = NULL;   
    62     char* key_material_priv = NULL;
    63      
    64     size_t datasize = 0;
    65     
    66     PEP_STATUS status = PEP_STATUS_OK;
    67     
    68     if (!include_secret) { // This isn't to own recips, so shipping the rev'd key is OK. Own keys are revoked on each device.
    69         status = export_key(session, old_fpr, &key_material_old, &datasize);
    70         if (datasize > 0 && key_material_old) {         
    71             if (status != PEP_STATUS_OK)
    72                 return status;
    73 
    74             if (!keys)
    75                 keys = new_bloblist(key_material_old, datasize, 
    76                                                 "application/pgp-keys",
    77                                                 "file://pEpkey_old.asc");
    78             else                                    
    79                 bloblist_add(keys, key_material_old, datasize, "application/pgp-keys",
    80                                                                        "file://pEpkey_old.asc");
    81         }
    82         datasize = 0;
    83     }                                                                  
    84     status = export_key(session, new_fpr, &key_material_new, &datasize);
    85 
    86     if (datasize > 0 && key_material_new) {         
    87         if (status != PEP_STATUS_OK)
    88             return status;
    89 
    90         if (!keys)
    91             keys = new_bloblist(key_material_new, datasize, 
    92                                             "application/pgp-keys",
    93                                             "file://pEpkey_new_pub.asc");
    94         else                                    
    95             bloblist_add(keys, key_material_new, datasize, "application/pgp-keys", "file://pEpkey_new_pub.asc");
    96                         
    97         datasize = 0;    
    98         if (include_secret) {
    99             status = export_secret_key(session, new_fpr, &key_material_priv, &datasize);    
   100             if (status != PEP_STATUS_OK)
   101                 return status;
   102             if (datasize > 0 && key_material_priv) {
   103                 bloblist_add(keys, key_material_priv, datasize, "application/pgp-keys",
   104                                                                             "file://pEpkey_priv.asc");
   105             }                                                      
   106         }    
   107     }
   108     if (keys) {
   109         if (*key_attachments)
   110             bloblist_join(*key_attachments, keys);
   111         else
   112             *key_attachments = keys;
   113     }        
   114     return status;
   115 }
   116 
   117 // For multiple idents under a single key
   118 // idents contain new fprs
   119 static PEP_STATUS _generate_own_commandlist_msg(PEP_SESSION session,
   120                                                 identity_list* from_idents,
   121                                                 const char* old_fpr,
   122                                                 message** dst) {                                                
   123     PEP_STATUS status = PEP_STATUS_OK;
   124     message* msg = NULL;                                                
   125     identity_list* list_curr = from_idents;
   126     keyreset_command_list* kr_commands = NULL;
   127     bloblist_t* key_attachments = NULL;
   128     
   129     for ( ; list_curr && list_curr->ident; list_curr = list_curr->next) {
   130         pEp_identity* curr_ident = list_curr->ident;
   131         
   132         if (curr_ident->flags & PEP_idf_devicegroup) {                
   133         
   134             PEP_STATUS status = _generate_reset_structs(session,
   135                                                         curr_ident,
   136                                                         old_fpr,
   137                                                         curr_ident->fpr,
   138                                                         &key_attachments,
   139                                                         &kr_commands,
   140                                                         true);
   141             if (status != PEP_STATUS_OK)
   142                 return status; // FIXME
   143             if (!key_attachments || !kr_commands)
   144                 return PEP_UNKNOWN_ERROR;
   145         }        
   146     }
   147     
   148     if (!kr_commands) {
   149         // There was nothing for us to send to self - we could be ungrouped,
   150         // etc
   151         return PEP_STATUS_OK;
   152     }    
   153     char* payload = NULL;
   154     size_t size = 0;
   155     status = key_reset_commands_to_PER(kr_commands, &payload, &size);
   156     if (status != PEP_STATUS_OK)
   157         return status;
   158         
   159     // From and to our first ident - this only goes to us.
   160     pEp_identity* from = identity_dup(from_idents->ident);
   161     pEp_identity* to = identity_dup(from);    
   162     status = base_prepare_message(session, from, to,
   163                                   BASE_KEYRESET, payload, size, NULL,
   164                                   &msg);
   165 
   166     if (status != PEP_STATUS_OK) {
   167         free(msg);
   168         return status;
   169     }    
   170     if (!msg)
   171         return PEP_OUT_OF_MEMORY;
   172     if (!msg->attachments)
   173         return PEP_UNKNOWN_ERROR;
   174     
   175     if (!bloblist_join(msg->attachments, key_attachments))
   176         return PEP_UNKNOWN_ERROR;
   177 
   178     if (msg)
   179         *dst = msg;
   180 
   181     free_keyreset_command_list(kr_commands);
   182         
   183     return status;
   184 
   185 }
   186 
   187 static PEP_STATUS _generate_keyreset_command_message(PEP_SESSION session,
   188                                                      const pEp_identity* from_ident,
   189                                                      const pEp_identity* to_ident,
   190                                                      const char* old_fpr,
   191                                                      const char* new_fpr,
   192                                                      bool is_private,
   193                                                      message** dst) {
   194                                                                                                                   
   195     if (!session || !from_ident || !old_fpr || !new_fpr || !dst)
   196         return PEP_ILLEGAL_VALUE;
   197 
   198     // safe cast
   199     if (!is_me(session, (pEp_identity*)from_ident))
   200         return PEP_ILLEGAL_VALUE;
   201 
   202     PEP_STATUS status = PEP_STATUS_OK;
   203         
   204     *dst = NULL;
   205         
   206     message* msg = NULL;
   207     
   208     // Ok, generate payload here...
   209     pEp_identity* outgoing_ident = identity_dup(from_ident);
   210     if (!outgoing_ident)
   211         return PEP_OUT_OF_MEMORY;
   212     free(outgoing_ident->fpr);
   213     outgoing_ident->fpr = strdup(old_fpr);
   214     if (!outgoing_ident->fpr)
   215         return PEP_OUT_OF_MEMORY;
   216         
   217     keyreset_command_list* kr_list = NULL;
   218     bloblist_t* key_attachments = NULL;
   219             
   220     // Check memory        
   221     status = _generate_reset_structs(session,
   222                                      outgoing_ident,
   223                                      old_fpr,
   224                                      new_fpr,
   225                                      &key_attachments,
   226                                      &kr_list,
   227                                      is_private);
   228     if (status != PEP_STATUS_OK)
   229         return status; // FIXME
   230     if (!key_attachments || !kr_list)
   231         return PEP_UNKNOWN_ERROR;
   232         
   233     char* payload = NULL;
   234     size_t size = 0;
   235     status = key_reset_commands_to_PER(kr_list, &payload, &size);
   236     status = base_prepare_message(session, outgoing_ident, to_ident,
   237                                   BASE_KEYRESET, payload, size, NULL,
   238                                   &msg);
   239     if (status) {
   240         free(msg);
   241         return status;
   242     }    
   243     if (!msg)
   244         return PEP_OUT_OF_MEMORY;
   245     if (!msg->attachments)
   246         return PEP_UNKNOWN_ERROR;
   247     
   248     if (msg)
   249         *dst = msg;
   250     return status;
   251 }
   252 
   253 PEP_STATUS has_key_reset_been_sent(
   254         PEP_SESSION session, 
   255         const char* from_addr,
   256         const char* user_id, 
   257         const char* revoked_fpr,
   258         bool* contacted)
   259 {
   260     assert(session);
   261     assert(contacted);
   262     assert(user_id);
   263     assert(revoked_fpr);
   264     assert(!EMPTYSTR(user_id));
   265 
   266     if (!session || !contacted || EMPTYSTR(from_addr) || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
   267         return PEP_ILLEGAL_VALUE;
   268     
   269     *contacted = false;
   270                     
   271     char* alias_default = NULL;
   272     
   273     PEP_STATUS status = get_userid_alias_default(session, user_id, &alias_default);
   274     
   275     if (status == PEP_CANNOT_FIND_ALIAS || EMPTYSTR(alias_default)) {
   276         free(alias_default);
   277         alias_default = strdup(user_id);
   278     }
   279     
   280     sqlite3_reset(session->was_id_for_revoke_contacted);
   281     sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
   282             SQLITE_STATIC);
   283     sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, from_addr, -1,
   284             SQLITE_STATIC);        
   285     sqlite3_bind_text(session->was_id_for_revoke_contacted, 3, user_id, -1,
   286             SQLITE_STATIC);        
   287     int result = sqlite3_step(session->was_id_for_revoke_contacted);
   288     switch (result) {
   289         case SQLITE_ROW: {
   290             *contacted = (sqlite3_column_int(session->was_id_for_revoke_contacted, 0) != 0);
   291             break;
   292         }
   293         default:
   294             sqlite3_reset(session->was_id_for_revoke_contacted);
   295             free(alias_default);
   296             return PEP_UNKNOWN_DB_ERROR;
   297     }
   298 
   299     sqlite3_reset(session->was_id_for_revoke_contacted);
   300     return PEP_STATUS_OK;
   301 }
   302 
   303 PEP_STATUS set_reset_contact_notified(
   304         PEP_SESSION session,
   305         const char* own_address,
   306         const char* revoke_fpr,
   307         const char* contact_id
   308     )
   309 {
   310     PEP_STATUS status = PEP_STATUS_OK;
   311     
   312     assert(session && !EMPTYSTR(own_address) && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
   313     
   314     if (!session || EMPTYSTR(own_address) || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
   315         return PEP_ILLEGAL_VALUE;
   316     
   317     sqlite3_reset(session->set_revoke_contact_as_notified);
   318     sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1, 
   319             SQLITE_STATIC);
   320     sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, own_address, -1, 
   321             SQLITE_STATIC);            
   322     sqlite3_bind_text(session->set_revoke_contact_as_notified, 3, contact_id, -1,
   323             SQLITE_STATIC);
   324 
   325     int result;
   326     
   327     result = sqlite3_step(session->set_revoke_contact_as_notified);
   328     switch (result) {
   329         case SQLITE_DONE:
   330             status = PEP_STATUS_OK;
   331             break;
   332             
   333         default:
   334             status = PEP_UNKNOWN_DB_ERROR;
   335     }
   336     
   337     sqlite3_reset(session->set_revoke_contact_as_notified);
   338     return status;    
   339 }
   340 
   341 // FIXME: fpr ownership
   342 PEP_STATUS receive_key_reset(PEP_SESSION session,
   343                              message* reset_msg) {
   344 
   345     if (!session || !reset_msg || !reset_msg->_sender_fpr)
   346         return PEP_ILLEGAL_VALUE;
   347 
   348     PEP_STATUS status = PEP_STATUS_OK;
   349 
   350     stringlist_t* keylist = NULL;
   351     
   352     char* sender_fpr = reset_msg->_sender_fpr;
   353 
   354     bool revoked = false;
   355 
   356     // Check to see if sender fpr is revoked already - if this was 
   357     // from us, we won't have done it yet for obvious reasons (i.e. 
   358     // we need to verify it's from us before we accept someone telling
   359     // us to reset our private key), and if this was from someone else,
   360     // a key reset message will be signed by their new key, because 
   361     // we presume the old one was compromised (and we remove trust from 
   362     // the replacement key until verified)
   363     status = key_revoked(session, sender_fpr, &revoked); 
   364     
   365     if (status != PEP_STATUS_OK)
   366         return status;
   367 
   368     // Bail if revoked
   369     if (revoked) {
   370         return PEP_ILLEGAL_VALUE; // could be an attack            
   371     }
   372     // Otherwise, bail
   373     else {
   374         bool mistrusted = false;
   375         status = is_mistrusted_key(session, sender_fpr, &mistrusted);
   376         
   377         if (status != PEP_STATUS_OK)
   378             return status;
   379         
   380         if (mistrusted)
   381             return PEP_ILLEGAL_VALUE;
   382     }
   383 
   384     
   385     // Parse reset message
   386     
   387     pEp_identity* sender_id = reset_msg->from;
   388                             
   389     if (!sender_id)
   390         return PEP_MALFORMED_KEY_RESET_MSG;
   391 
   392     if (is_me(session, sender_id)) {
   393         // first off, we need to make sure we're up-to-date
   394         status = myself(session, sender_id);        
   395     }
   396     else {    
   397         status = update_identity(session, sender_id);
   398         if (!sender_id->user_id)
   399             return PEP_UNKNOWN_ERROR;
   400     }
   401     
   402     bool sender_own_key = false;
   403     bool from_me = is_me(session, sender_id);
   404     
   405     if (is_me(session, sender_id)) {
   406         // Do own-reset-checks
   407         status = is_own_key(session, sender_fpr, &sender_own_key);
   408         
   409         if (status != PEP_STATUS_OK)
   410             return status;
   411         
   412         // Should we mistrust the sender_fpr here??
   413         if (!sender_own_key) 
   414             return PEP_ILLEGAL_VALUE; // actually, this is an attack                
   415         
   416         // Make sure it's a TRUSTED own key
   417         char* keyholder = sender_id->fpr;
   418         
   419         sender_id->fpr = sender_fpr;                     
   420         status = get_trust(session, sender_id);
   421         sender_id->fpr = keyholder;
   422             
   423         if (sender_id->comm_type < PEP_ct_pEp)
   424             return PEP_ILLEGAL_VALUE;
   425     }
   426         
   427     status = PEP_STATUS_OK;
   428     char* old_fpr = NULL;
   429     char* new_fpr = NULL;
   430     
   431     size_t size = 0;
   432     const char* payload = NULL;
   433 
   434     char* not_used_fpr = NULL;
   435     status = base_extract_message(session,
   436                                   reset_msg,
   437                                   BASE_KEYRESET,
   438                                   &size,
   439                                   &payload,
   440                                   &not_used_fpr);
   441                                   
   442     if (status != PEP_STATUS_OK)
   443         return status;
   444         
   445     if (!payload || size == 0)
   446         return PEP_MALFORMED_KEY_RESET_MSG;
   447         
   448     keyreset_command_list* resets = NULL; 
   449     
   450     status = PER_to_key_reset_commands(payload, size, &resets);
   451 
   452     if (status != PEP_STATUS_OK)
   453         return status;
   454         
   455     if (!resets)
   456         return PEP_MALFORMED_KEY_RESET_MSG;
   457 
   458     keyreset_command_list* curr_cl = resets;
   459 
   460     stringpair_list_t* rev_pairs = NULL;
   461     
   462     // Ok, go through the list of reset commands. Right now, this 
   463     // is actually only one, but could be more later.
   464     for ( ; curr_cl && curr_cl->command; curr_cl = curr_cl->next) {    
   465         keyreset_command* curr_cmd = curr_cl->command;
   466         if (!curr_cmd || !curr_cmd->ident || !curr_cmd->ident->fpr ||
   467             !curr_cmd->ident->address) {
   468             return PEP_MALFORMED_KEY_RESET_MSG;        
   469         }
   470         pEp_identity* curr_ident = curr_cmd->ident;
   471         
   472         old_fpr = curr_ident->fpr;
   473         new_fpr = strdup(curr_cmd->new_key);
   474         
   475         bool is_old_own = false;
   476         // if it's our key and the old one is revoked, we skip it.
   477         // Sorry, them's the rules/
   478         if (sender_own_key) {
   479             status = is_own_key(session, old_fpr, &is_old_own);
   480             if (is_old_own) {
   481                 bool old_revoked = false;
   482                 status = key_revoked(session, old_fpr, &old_revoked);
   483                 if (old_revoked)
   484                     continue;
   485             }
   486         }
   487 
   488         // Make sure that this key is at least one we associate 
   489         // with the sender. FIXME: check key election interaction
   490         // N.B. If we ever allow ourselves to send resets to ourselves
   491         // for not-own stuff, this will have to be revised
   492         
   493         status = find_keys(session, new_fpr, &keylist);
   494         if (status != PEP_STATUS_OK)
   495             goto pEp_free;
   496         if (!keylist) {
   497             status = PEP_MALFORMED_KEY_RESET_MSG;
   498             goto pEp_free;
   499         }
   500         
   501         // We need to update the identity to get the user_id
   502         curr_ident->fpr = NULL; // ensure old_fpr is preserved
   503         free(curr_ident->user_id);
   504         curr_ident->user_id = NULL;
   505         status = update_identity(session, curr_ident);
   506         
   507         // Ok, now check the old fpr to see if we have an entry for it
   508         // temp fpr set for function call
   509         curr_ident->fpr = old_fpr;
   510         status = get_trust(session, curr_ident);
   511         if (status != PEP_STATUS_OK)
   512             return status;
   513         
   514         PEP_comm_type ct_result = curr_ident->comm_type;
   515 
   516         // Basically, see if fpr is even in the database
   517         // for this user - we'll get PEP_ct_unknown if it isn't
   518         if (ct_result == PEP_ct_unknown)
   519             return PEP_KEY_NOT_RESET;
   520         
   521         // Alright, so we have a key to reset. Good.
   522         
   523         // If this is a non-own user, for NOW, we presume key reset 
   524         // by email for non-own keys is ONLY in case of revoke-and-replace. 
   525         // This means we have, at a *minimum*, an object that once 
   526         // required the initial private key in order to replace that key 
   527         // with another.
   528         //
   529         // The limitations on what this guarantees are known - this does 
   530         // not prevent, for example, replay attacks from someone with 
   531         // access to the original revocation cert are possible if they 
   532         // get to us before we receive this object from the original sender.
   533         // The best we can do in this case is to NOT trust the new key.
   534         // It will be used by default, but if the original was trusted,
   535         // the rating will visibly change for the sender, and even if it was 
   536         // not, if we do use it, the sender can report unreadable mails to us 
   537         // and detect it that way. FIXME: We may need to have some kind 
   538         // of even alert the user when such a change occurs for their contacts
   539         //
   540         // If this is from US, we already made sure that the sender_fpr 
   541         // was a valid own key, so we don't consider it here.
   542         if (!from_me) {
   543             revoked = false;
   544             status = key_revoked(session, old_fpr, &revoked); 
   545 
   546             if (!revoked)
   547                 return PEP_KEY_NOT_RESET;            
   548 
   549             // Also don't let someone change the replacement fpr 
   550             // if the replacement fpr was also revoked - we really need 
   551             // to detect that something fishy is going on at this point
   552             // FIXME: ensure that PEP_KEY_NOT_RESET responses to 
   553             // automated key reset functions are propagated upward - 
   554             // app should be made aware if someone is trying to reset someone 
   555             // else's key and it's failing for some reason.
   556             revoked = false;
   557             status = key_revoked(session, new_fpr, &revoked); 
   558 
   559             if (revoked)
   560                 return PEP_KEY_NOT_RESET;                        
   561         }
   562         
   563         // Hooray! We apparently now are dealing with keys 
   564         // belonging to the user from a message at least marginally
   565         // from the user
   566         if (!sender_own_key) {
   567             // Clear all info (ALSO REMOVES OLD KEY RIGHT NOW!!!)            
   568             status = key_reset(session, old_fpr, curr_ident);
   569             if (status != PEP_STATUS_OK)
   570                 return status;
   571                                 
   572             // Make new key the default    
   573             curr_ident->fpr = new_fpr;
   574     
   575             // Whether new_key is NULL or not, if this key is equal to the current user default, we 
   576             // replace it.
   577             status = replace_main_user_fpr_if_equal(session, curr_ident->user_id, 
   578                                                     new_fpr, old_fpr);                    
   579 
   580             // This only sets as the default, does NOT TRUST IN ANY WAY
   581             PEP_comm_type new_key_rating = PEP_ct_unknown;
   582             
   583             // No key is ever returned as "confirmed" from here - it's based on raw key
   584             status = get_key_rating(session, new_fpr, &new_key_rating);
   585             if (status != PEP_STATUS_OK)
   586                 return status;
   587 
   588             if (new_key_rating >= PEP_ct_strong_but_unconfirmed) {
   589                 bool is_pEp = false;
   590                 status = is_pEp_user(session, curr_ident, &is_pEp);
   591                 if (is_pEp)
   592                     curr_ident->comm_type = PEP_ct_pEp_unconfirmed;
   593                 else    
   594                     curr_ident->comm_type = new_key_rating & (~PEP_ct_confirmed);
   595             }
   596             else
   597                 curr_ident->comm_type = new_key_rating;
   598                 
   599             status = set_identity(session, curr_ident);  
   600             if (status != PEP_STATUS_OK)
   601                 goto pEp_free; 
   602         }    
   603         else {
   604             // set new key as the default for this identity
   605             // N.B. If for some reason this is only a pubkey,
   606             // then so be it - but we need to double-check to 
   607             // ensure that in this case, we end up with a private one,
   608             // so talk to vb about this.
   609             
   610             // Make new key the default    
   611             
   612             // This is REQUIRED for set_own_key (see doc)
   613             curr_ident->fpr = NULL;
   614             
   615             status = set_own_key(session, curr_ident, new_fpr);
   616             
   617             if (status != PEP_STATUS_OK)
   618                 return status;
   619 
   620             // Whether new_key is NULL or not, if this key is equal to the current user default, we 
   621             // replace it.
   622             status = replace_main_user_fpr_if_equal(session, curr_ident->user_id, 
   623                                                     new_fpr, old_fpr);                    
   624 
   625             if (status != PEP_STATUS_OK)
   626                 return status;            
   627                 
   628             status = myself(session, curr_ident);
   629 
   630             if (status != PEP_STATUS_OK)
   631                 return status;            
   632 
   633             char* old_copy = NULL;
   634             char* new_copy = NULL;
   635             old_copy = strdup(old_fpr);
   636             new_copy = strdup(new_fpr);
   637             if (!old_copy || !new_copy)
   638                 return PEP_OUT_OF_MEMORY;
   639                                 
   640 
   641             stringpair_t* revp = new_stringpair(old_copy, new_copy);                
   642             if (!rev_pairs) {
   643                 rev_pairs = new_stringpair_list(revp);
   644                 if (!rev_pairs)
   645                     return PEP_OUT_OF_MEMORY;
   646             }
   647             else    
   648                 stringpair_list_add(rev_pairs, revp);
   649                             
   650         }    
   651         
   652         old_fpr = NULL;
   653         free(new_fpr);
   654         new_fpr = NULL;    
   655     }
   656 
   657     // actually revoke - list only exists with own keys
   658     stringpair_list_t* curr_rev_pair = rev_pairs;
   659     while (curr_rev_pair && curr_rev_pair->value) {
   660         char* rev_key = curr_rev_pair->value->key;
   661         char* new_key = curr_rev_pair->value->value;
   662             
   663         if (EMPTYSTR(rev_key) || EMPTYSTR(new_key))
   664             return PEP_UNKNOWN_ERROR;
   665         bool revoked = false;
   666         status = key_revoked(session, rev_key, &revoked);
   667         if (!revoked) {
   668             // key reset on old key
   669             status = revoke_key(session, rev_key, NULL);
   670 
   671             if (status != PEP_STATUS_OK)
   672                 goto pEp_free;    
   673         }
   674         // N.B. This sort of sucks because we overwrite this every time.
   675         // But this case is infrequent and we don't rely on the binding.
   676 
   677         if (status == PEP_STATUS_OK) 
   678             status = set_revoked(session, rev_key, new_key, time(NULL));            
   679 
   680         if (status != PEP_STATUS_OK)
   681             goto pEp_free;      
   682                   
   683         curr_rev_pair = curr_rev_pair->next;    
   684     }
   685 
   686 
   687 pEp_free:    
   688     free_stringlist(keylist);    
   689     free_stringpair_list(rev_pairs);
   690     free(old_fpr);
   691     free(new_fpr);
   692     return status;
   693 }
   694 
   695 PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
   696                                                message** dst, 
   697                                                pEp_identity* own_identity,
   698                                                pEp_identity* recip,
   699                                                const char* old_fpr,
   700                                                const char* new_fpr) {
   701                                                    
   702     if (!dst || !own_identity || EMPTYSTR(own_identity->address) 
   703              || !recip || EMPTYSTR(recip->user_id) 
   704              || EMPTYSTR(recip->address))
   705         return PEP_ILLEGAL_VALUE;
   706 
   707     if (EMPTYSTR(old_fpr) || EMPTYSTR(new_fpr))
   708         return PEP_ILLEGAL_VALUE;
   709         
   710     *dst = NULL;
   711     
   712     message* reset_msg = NULL;
   713     
   714     PEP_STATUS status = _generate_keyreset_command_message(session, own_identity,
   715                                                            recip,
   716                                                            old_fpr, new_fpr, false,
   717                                                            &reset_msg);
   718                             
   719     if (status != PEP_STATUS_OK)
   720         goto pEp_free;
   721     
   722     if (!reset_msg)
   723         return PEP_ILLEGAL_VALUE;
   724                                                                          
   725     if (!reset_msg->attachments)
   726         return PEP_UNKNOWN_ERROR;
   727     
   728     message* output_msg = NULL;
   729     
   730     status = encrypt_message(session, reset_msg, NULL,
   731                              &output_msg, PEP_enc_PGP_MIME,
   732                              PEP_encrypt_flag_key_reset_only);
   733 
   734     if (status == PEP_STATUS_OK)
   735         *dst = output_msg;
   736         
   737 pEp_free:
   738         
   739     free_message(reset_msg);    
   740     return status;
   741 }
   742 
   743 PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
   744                                      pEp_identity* from_ident,
   745                                      const char* old_fpr, 
   746                                      const char* new_fpr) {
   747     assert(old_fpr);
   748     assert(new_fpr);
   749     assert(session);
   750 //    assert(session->messageToSend); NO. Don't assert this, FFS.
   751     
   752     if (!session || !old_fpr || !new_fpr)
   753         return PEP_ILLEGAL_VALUE;
   754 
   755     messageToSend_t send_cb = session->messageToSend;
   756     if (!send_cb)
   757         return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
   758         
   759     identity_list* recent_contacts = NULL;
   760     message* reset_msg = NULL;
   761 
   762     PEP_STATUS status = get_last_contacted(session, &recent_contacts);
   763     
   764     if (status != PEP_STATUS_OK)
   765         goto pEp_free;
   766                     
   767     identity_list* curr_id_ptr = recent_contacts;
   768 
   769     for (curr_id_ptr = recent_contacts; curr_id_ptr; curr_id_ptr = curr_id_ptr->next) {
   770         pEp_identity* curr_id = curr_id_ptr->ident;
   771         
   772         if (!curr_id)
   773             break;
   774     
   775         const char* user_id = curr_id->user_id;
   776         
   777         // Should be impossible, but?
   778         if (!user_id)
   779             continue;
   780         
   781         // Check if it's us - if so, pointless...
   782         if (is_me(session, curr_id))
   783             continue;
   784             
   785         // Also, don't bother to send it to non-pEp-users 
   786         bool pEp_user = false;
   787         status = is_pEp_user(session, curr_id, &pEp_user);
   788 
   789         if (status != PEP_STATUS_OK)
   790             goto pEp_free;
   791 
   792         if (!pEp_user)
   793             continue;
   794             
   795         // Check if they've already been told - this shouldn't be the case, but...
   796         bool contacted = false;
   797         status = has_key_reset_been_sent(session, from_ident->address, user_id, old_fpr, &contacted);
   798         if (status != PEP_STATUS_OK)
   799             goto pEp_free;
   800     
   801         if (contacted)
   802             continue;
   803             
   804         // Make sure they've ever *contacted* this address    
   805         bool in_contact_w_this_address = false;
   806         status = has_partner_contacted_address(session, curr_id->user_id, from_ident->address,  
   807                                                &in_contact_w_this_address);
   808         
   809         if (!in_contact_w_this_address)
   810             continue;
   811             
   812         // if not, make em a message    
   813         reset_msg = NULL;
   814         
   815         status = create_standalone_key_reset_message(session,
   816                                                      &reset_msg,
   817                                                      from_ident,
   818                                                      curr_id,
   819                                                      old_fpr,
   820                                                      new_fpr);
   821 
   822         if (status == PEP_CANNOT_FIND_IDENTITY) { // this is ok, just means we never mailed them 
   823             status = PEP_STATUS_OK;
   824             continue; 
   825         }
   826             
   827         if (status != PEP_STATUS_OK) {
   828             free(reset_msg);
   829             goto pEp_free;
   830         }
   831 
   832         _add_auto_consume(reset_msg);        
   833         // insert into queue
   834         status = send_cb(reset_msg);
   835 
   836         if (status != PEP_STATUS_OK) {
   837             free(reset_msg);
   838             goto pEp_free;            
   839         }
   840             
   841         // Put into notified DB
   842         status = set_reset_contact_notified(session, from_ident->address, old_fpr, user_id);
   843         if (status != PEP_STATUS_OK)
   844             goto pEp_free;            
   845     }
   846     
   847 pEp_free:
   848     free_identity_list(recent_contacts);
   849     return status;
   850 }
   851 
   852 DYNAMIC_API PEP_STATUS key_reset_identity(
   853         PEP_SESSION session,
   854         pEp_identity* ident,
   855         const char* fpr        
   856     )
   857 {
   858     if (!session || !ident || (ident && (EMPTYSTR(ident->user_id) || EMPTYSTR(ident->address))))
   859         return PEP_ILLEGAL_VALUE;
   860     
   861     return key_reset(session, fpr, ident);    
   862 }
   863 
   864 DYNAMIC_API PEP_STATUS key_reset_user(
   865         PEP_SESSION session,
   866         const char* user_id,
   867         const char* fpr        
   868     )
   869 {
   870     if (!session || EMPTYSTR(user_id))
   871         return PEP_ILLEGAL_VALUE;
   872 
   873     pEp_identity* input_ident = new_identity(NULL, NULL, user_id, NULL);
   874     if (!input_ident)
   875         return PEP_OUT_OF_MEMORY;
   876         
   877     if (is_me(session, input_ident) && EMPTYSTR(fpr))
   878         return PEP_ILLEGAL_VALUE;
   879         
   880     PEP_STATUS status = key_reset(session, fpr, input_ident);
   881     free_identity(input_ident);
   882     return status;
   883 }
   884 
   885 DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
   886     return key_reset(session, NULL, NULL);
   887 }
   888 
   889 static PEP_STATUS _dup_grouped_only(identity_list* idents, identity_list** filtered) {
   890     if (!idents)
   891         return PEP_STATUS_OK;
   892         
   893     identity_list* id_node;
   894     pEp_identity* curr_ident = NULL;
   895     
   896     identity_list* ret_list = NULL;
   897     identity_list** ret_list_pp = &ret_list;
   898     
   899     for (id_node = idents; id_node && id_node->ident; id_node = id_node->next) {
   900         curr_ident = id_node->ident;
   901         if (curr_ident->flags & PEP_idf_devicegroup) {
   902             pEp_identity* new_ident = identity_dup(curr_ident);
   903             if (!new_ident) {
   904                 free_identity_list(ret_list);
   905                 return PEP_OUT_OF_MEMORY;
   906             }
   907             identity_list* new_ident_il = new_identity_list(new_ident);
   908             if (!new_ident_il) {
   909                 free(new_ident);
   910                 free_identity_list(ret_list);
   911                 return PEP_OUT_OF_MEMORY;
   912             }
   913                 
   914             *ret_list_pp = new_ident_il;
   915             ret_list_pp = &(new_ident_il->next);                
   916         }
   917     }
   918     *filtered = ret_list;
   919     return PEP_STATUS_OK;    
   920 }
   921 
   922 static PEP_STATUS _key_reset_device_group_for_shared_key(PEP_SESSION session, 
   923                                                          identity_list* key_idents, 
   924                                                          const char* old_key,
   925                                                          bool grouped_only) {
   926     assert(session);
   927     assert(key_idents);
   928     assert(old_key);
   929     
   930     if (!session || !key_idents || EMPTYSTR(old_key))
   931         return PEP_ILLEGAL_VALUE;
   932         
   933     messageToSend_t send_cb = session->messageToSend;
   934     if (!send_cb)
   935         return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
   936         
   937     PEP_STATUS status = PEP_STATUS_OK;
   938         
   939     // if we only want grouped identities, we do this:
   940     if (grouped_only) {
   941         identity_list* new_list = NULL;        
   942         status = _dup_grouped_only(key_idents, &new_list);
   943         if (status != PEP_STATUS_OK)
   944             return status;
   945         key_idents = new_list; // local var change, won't impact caller    
   946     }
   947     
   948     if (!key_idents)
   949         return PEP_STATUS_OK;
   950         
   951     // each of these has the same key and needs a new one.
   952     identity_list* curr_ident;
   953     for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
   954         pEp_identity* ident = curr_ident->ident;
   955         free(ident->fpr);
   956         ident->fpr = NULL;
   957         status = _generate_keypair(session, ident, true);
   958         if (status != PEP_STATUS_OK)
   959             return status;            
   960     }
   961         
   962     // Ok, everyone's got a new keypair. Hoorah! 
   963     // generate, sign, and push messages into queue
   964     message* outmsg = NULL;
   965     status = _generate_own_commandlist_msg(session,
   966                                            key_idents,
   967                                            old_key,
   968                                            &outmsg);
   969                                            
   970     // Following will only be true if some idents were grouped,
   971     // and will only include grouped idents!                                       
   972     if (outmsg) {    
   973         message* enc_msg = NULL;
   974         
   975         // encrypt this baby and get out
   976         // extra keys???
   977         status = encrypt_message(session, outmsg, NULL, &enc_msg, PEP_enc_PGP_MIME, PEP_encrypt_flag_key_reset_only);
   978         
   979         if (status != PEP_STATUS_OK) {
   980             goto pEp_free;
   981         }
   982         _add_auto_consume(enc_msg);
   983         
   984         // insert into queue
   985         status = send_cb(enc_msg);
   986 
   987         if (status != PEP_STATUS_OK) {
   988             free(enc_msg);
   989             goto pEp_free;            
   990         }                         
   991     }
   992     
   993     // Ok, we've signed everything we need to with the old key,
   994     // Revoke that baby.
   995     status = revoke_key(session, old_key, NULL);
   996 
   997     if (status != PEP_STATUS_OK)
   998         goto pEp_free;
   999         
  1000     for (curr_ident = key_idents; curr_ident && curr_ident->ident; curr_ident = curr_ident->next) {
  1001         if (curr_ident->ident->flags & PEP_idf_devicegroup) {
  1002             pEp_identity* ident = curr_ident->ident;
  1003             
  1004             // set own key, you fool.
  1005             // Grab ownership first.
  1006             char* new_key = ident->fpr;
  1007             ident->fpr = NULL;
  1008             status = set_own_key(session, ident, new_key);
  1009             if (status != PEP_STATUS_OK) {
  1010                 // scream loudly and cry, then hang head in shame
  1011                 return status;
  1012             }
  1013             free(ident->fpr);
  1014 
  1015             // release ownership to the struct again
  1016             ident->fpr = new_key;
  1017                 
  1018             // N.B. This sort of sucks because we overwrite this every time.
  1019             // But this case is infrequent and we don't rely on the binding.
  1020             if (status == PEP_STATUS_OK) 
  1021                 status = set_revoked(session, old_key, new_key, time(NULL));            
  1022 
  1023             if (status != PEP_STATUS_OK)
  1024                 goto pEp_free;
  1025 
  1026             // Whether new_key is NULL or not, if this key is equal to the current user default, we 
  1027             // replace it.
  1028             status = replace_main_user_fpr_if_equal(session, 
  1029                                                     ident->user_id, 
  1030                                                     new_key, 
  1031                                                     old_key);                    
  1032             
  1033             if (status != PEP_STATUS_OK)
  1034                 goto pEp_free;
  1035                 
  1036             pEp_identity* tmp_ident = identity_dup(ident);
  1037             if (!tmp_ident) {
  1038                 status = PEP_OUT_OF_MEMORY;
  1039                 goto pEp_free;
  1040             }    
  1041             free(tmp_ident->fpr);    
  1042             
  1043             // for all active communication partners:
  1044             //      active_send revocation            
  1045             tmp_ident->fpr = strdup(old_key); // freed in free_identity
  1046             if (status == PEP_STATUS_OK)
  1047                 status = send_key_reset_to_recents(session, tmp_ident, old_key, ident->fpr);        
  1048             free_identity(tmp_ident);
  1049         }    
  1050     }    
  1051     
  1052     if (status == PEP_STATUS_OK)
  1053         // cascade that mistrust for anyone using this key
  1054         status = mark_as_compromised(session, old_key);
  1055         
  1056     if (status == PEP_STATUS_OK)
  1057         status = remove_fpr_as_default(session, old_key);
  1058     if (status == PEP_STATUS_OK)
  1059         status = add_mistrusted_key(session, old_key);
  1060     
  1061 pEp_free:
  1062     return status;
  1063 }
  1064 
  1065 
  1066 DYNAMIC_API PEP_STATUS key_reset_own_grouped_keys(PEP_SESSION session) {
  1067     assert(session);
  1068     
  1069     if (!session)
  1070         return PEP_ILLEGAL_VALUE;
  1071 
  1072     stringlist_t* keys = NULL;
  1073     char* user_id = NULL;    
  1074     PEP_STATUS status = get_default_own_userid(session, &user_id);
  1075 
  1076     if (status != PEP_STATUS_OK || !user_id)
  1077         goto pEp_free;                    
  1078 
  1079     
  1080     status = get_all_keys_for_user(session, user_id, &keys);
  1081 
  1082     // TODO: free
  1083     if (status == PEP_STATUS_OK) {
  1084         stringlist_t* curr_key;
  1085         
  1086         for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
  1087             identity_list* key_idents = NULL;
  1088             const char* own_key = curr_key->value;
  1089             status = get_identities_by_main_key_id(session, own_key, &key_idents);
  1090             
  1091             if (status == PEP_CANNOT_FIND_IDENTITY) {
  1092                 status = PEP_STATUS_OK;
  1093                 continue;
  1094             }    
  1095             else if (status == PEP_STATUS_OK)    
  1096                 status = _key_reset_device_group_for_shared_key(session, key_idents, own_key, true);            
  1097             else 
  1098                 goto pEp_free;
  1099             
  1100             free_identity_list(key_idents);    
  1101         }
  1102     }
  1103     goto pEp_free;
  1104 
  1105 pEp_free:
  1106     free_stringlist(keys);
  1107     free(user_id);
  1108     return status;
  1109 }
  1110 
  1111 // Notes to integrate into header:
  1112 // IF there is an ident, it must have a user_id.
  1113 PEP_STATUS key_reset(
  1114         PEP_SESSION session,
  1115         const char* key_id,
  1116         pEp_identity* ident
  1117     )
  1118 {
  1119     if (!session || (ident && EMPTYSTR(ident->user_id)))
  1120         return PEP_ILLEGAL_VALUE;
  1121         
  1122     PEP_STATUS status = PEP_STATUS_OK;
  1123         
  1124     char* fpr_copy = NULL;
  1125     char* own_id = NULL;
  1126     char* user_id = NULL;
  1127     char* new_key = NULL;
  1128     pEp_identity* tmp_ident = NULL;
  1129     identity_list* key_idents = NULL;
  1130     stringlist_t* keys = NULL;
  1131     
  1132     if (!EMPTYSTR(key_id)) {
  1133         fpr_copy = strdup(key_id);
  1134         if (!fpr_copy)
  1135             return PEP_OUT_OF_MEMORY;
  1136     }
  1137 
  1138     // This is true when we don't have a user_id and address and the fpr isn't specified
  1139     bool reset_all_for_user = !fpr_copy && (!ident || EMPTYSTR(ident->address));
  1140 
  1141     // FIXME: does this need to be done everywhere?> I think not.
  1142     if (ident) {
  1143         user_id = strdup(ident->user_id);
  1144         if (!user_id) {
  1145             status = PEP_OUT_OF_MEMORY;
  1146             goto pEp_free;
  1147         }
  1148     }
  1149     else {
  1150         status = get_default_own_userid(session, &user_id);
  1151         if (status != PEP_STATUS_OK || !user_id)
  1152             goto pEp_free;                    
  1153     }
  1154     
  1155     // FIXME: Make sure this can't result in a double-free in recursive calls
  1156     tmp_ident = (ident ? identity_dup(ident) : new_identity(NULL, NULL, user_id, NULL));
  1157     
  1158     if (reset_all_for_user) {
  1159         status = get_all_keys_for_user(session, user_id, &keys);
  1160         // TODO: free
  1161         if (status == PEP_STATUS_OK) {
  1162             stringlist_t* curr_key;
  1163             
  1164             for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
  1165                 // FIXME: Is the ident really necessary?
  1166                 status = key_reset(session, curr_key->value, tmp_ident);
  1167                 if (status != PEP_STATUS_OK && status != PEP_CANNOT_FIND_IDENTITY)
  1168                     break;
  1169                 else 
  1170                     status = PEP_STATUS_OK;
  1171             }
  1172         }
  1173         goto pEp_free;
  1174     }                   
  1175     else {
  1176         // tmp_ident => tmp_ident->user_id (was checked)
  1177         //
  1178         // !(EMPTYSTR(fpr) && (!tmp_ident || EMPTYSTR(tmp_ident->address)))
  1179         // => fpr || (tmp_ident && tmp_ident->address)
  1180         //
  1181         // so: We have an fpr or we have an ident with user_id and address
  1182         //     or both
  1183         if (!fpr_copy) {
  1184             // We are guaranteed to have an ident w/ id + addr here.
  1185             // Get the default key.
  1186             pEp_identity* stored_ident = NULL;
  1187             status = get_identity(session, tmp_ident->address, 
  1188                                   tmp_ident->user_id, &stored_ident);
  1189 
  1190             // FIXME FIXME FIXME
  1191             if (status == PEP_STATUS_OK) {
  1192                 // transfer ownership
  1193                 fpr_copy = stored_ident->fpr;
  1194                 stored_ident->fpr = NULL;
  1195                 free_identity(stored_ident);                
  1196             }
  1197             
  1198             if (!fpr_copy || status == PEP_CANNOT_FIND_IDENTITY) {
  1199                 // There's no identity default. Try resetting user default
  1200                 status = get_user_default_key(session, tmp_ident->user_id, &fpr_copy);
  1201             }            
  1202                         
  1203             if (!fpr_copy || status != PEP_STATUS_OK) // No default to free. We're done here.
  1204                 goto pEp_free;            
  1205         }
  1206         
  1207         // Ok - now we have at least an ident with user_id and an fpr.
  1208         // Now it matters if we're talking about ourselves or a partner.
  1209         bool is_own_private = false;
  1210         if (is_me(session, tmp_ident)) {
  1211             bool own_key = false;            
  1212             status = is_own_key(session, fpr_copy, &own_key);
  1213 
  1214             if (status != PEP_STATUS_OK)
  1215                 goto pEp_free;
  1216             if (!own_key) {
  1217                 status = PEP_ILLEGAL_VALUE;
  1218                 goto pEp_free;
  1219             }
  1220 
  1221             status = contains_priv_key(session, fpr_copy, &is_own_private);
  1222             if (status != PEP_STATUS_OK && status != PEP_KEY_NOT_FOUND)
  1223                 goto pEp_free;
  1224         }
  1225         
  1226         // Up to this point, we haven't cared about whether or not we 
  1227         // had a full identity. Now we have to deal with that in the 
  1228         // case of own identities with private keys.
  1229         
  1230         if (is_own_private) {
  1231             
  1232             // This is now the "is_own" base case - we don't do this 
  1233             // per-identity, because all identities using this key will 
  1234             // need new ones. That said, this is really only a problem 
  1235             // with manual key management, something which we only support 
  1236             // to a limited extent in any event.
  1237             
  1238             bool is_grouped = false;
  1239             status = deviceGrouped(session, &is_grouped);
  1240              
  1241             // Regardless of the single identity this is for, for own keys, we do this 
  1242             // for all keys associated with the identity.
  1243             status = get_identities_by_main_key_id(session, fpr_copy, &key_idents);
  1244             
  1245             if (status != PEP_CANNOT_FIND_IDENTITY) {
  1246                 
  1247                 // N.B. Possible user default key replacement will happen inside
  1248                 //      _key_reset_device_group_for_shared_key in the first case.
  1249                 //      We handle the reassignment for the second case in the block here.
  1250                 if (is_grouped) 
  1251                     status = _key_reset_device_group_for_shared_key(session, key_idents, fpr_copy, false);
  1252                 else if (status == PEP_STATUS_OK) {
  1253                     // now have ident list, or should
  1254                     identity_list* curr_ident;
  1255 
  1256                     for (curr_ident = key_idents; curr_ident && curr_ident->ident; 
  1257                                                     curr_ident = curr_ident->next) {
  1258                         
  1259                         pEp_identity* this_ident = curr_ident->ident;
  1260                         // Do the full reset on this identity        
  1261                         // Base case for is_own_private starts here
  1262                         // Note that we reset this key for ANY own ident that has it. And if 
  1263                         // tmp_ident did NOT have this key, it won't matter. We will reset the 
  1264                         // key for all idents for this user.
  1265                         status = revoke_key(session, fpr_copy, NULL);
  1266                         
  1267                         // If we have a full identity, we have some cleanup and generation tasks here
  1268                         if (!EMPTYSTR(this_ident->address)) {
  1269                             // generate new key
  1270                             if (status == PEP_STATUS_OK) {
  1271                                 this_ident->fpr = NULL;
  1272                                 status = myself(session, this_ident);
  1273                             }
  1274                             if (status == PEP_STATUS_OK && this_ident->fpr && strcmp(fpr_copy, this_ident->fpr) != 0)
  1275                                 new_key = strdup(this_ident->fpr);
  1276                             // Error handling?    
  1277                             
  1278                             // mistrust fpr from trust
  1279                             this_ident->fpr = fpr_copy;
  1280                                                             
  1281                             this_ident->comm_type = PEP_ct_mistrusted;
  1282                             status = set_trust(session, this_ident);
  1283                             this_ident->fpr = NULL;
  1284                             
  1285                             // Done with old use of ident.
  1286                             if (status == PEP_STATUS_OK) {
  1287                                 // Generate new key
  1288                                 status = myself(session, this_ident);
  1289                             }
  1290                         }    
  1291                         
  1292                         if (status == PEP_STATUS_OK)
  1293                             // cascade that mistrust for anyone using this key
  1294                             status = mark_as_compromised(session, fpr_copy);
  1295                             
  1296                         if (status == PEP_STATUS_OK)
  1297                             status = remove_fpr_as_default(session, fpr_copy);
  1298                         if (status == PEP_STATUS_OK)
  1299                             status = add_mistrusted_key(session, fpr_copy);
  1300 
  1301                         // If there's a new key, do the DB linkage with the revoked one, and 
  1302                         // send the key reset mail opportunistically to recently contacted
  1303                         // partners
  1304                         if (new_key) {
  1305                             // add to revocation list 
  1306                             if (status == PEP_STATUS_OK) 
  1307                                 status = set_revoked(session, fpr_copy, new_key, time(NULL));            
  1308                             // for all active communication partners:
  1309                             //      active_send revocation
  1310                             
  1311                             tmp_ident->fpr = fpr_copy;
  1312                             if (status == PEP_STATUS_OK)
  1313                                 status = send_key_reset_to_recents(session, this_ident, fpr_copy, new_key);        
  1314                             tmp_ident->fpr = NULL;    
  1315                         }
  1316                         
  1317                         // Whether new_key is NULL or not, if this key is equal to the current user default, we 
  1318                         // replace it.
  1319                         status = replace_main_user_fpr_if_equal(session, this_ident->user_id, new_key, fpr_copy);                    
  1320                     }  // Ident list gets freed below, do not free here!
  1321                 }
  1322                 // Ok, we've either now reset for each own identity with this key, or 
  1323                 // we got an error and want to bail anyway.
  1324                 goto pEp_free;
  1325             }
  1326             else 
  1327                 return PEP_CANNOT_FIND_IDENTITY;
  1328         } // end is_own_private
  1329         else {
  1330             // if it's mistrusted, make it not be so.
  1331             bool mistrusted_key = false;
  1332             is_mistrusted_key(session, fpr_copy, &mistrusted_key);
  1333 
  1334             if (mistrusted_key)
  1335                 delete_mistrusted_key(session, fpr_copy);
  1336             
  1337             if (tmp_ident->user_id)
  1338                 status = clear_trust_info(session, tmp_ident->user_id, fpr_copy);
  1339 
  1340             // This is a public key (or a private key that isn't ours, which means
  1341             // we want it gone anyway)
  1342             //
  1343             // Delete this key from the keyring.
  1344             // FIXME: when key election disappears, so should this!
  1345             status = delete_keypair(session, fpr_copy);
  1346         }
  1347 
  1348         // REGARDLESS OF WHO OWNS THE KEY, WE NOW NEED TO REMOVE IT AS A DEFAULT.
  1349         PEP_STATUS cached_status = status;
  1350         // remove fpr from all identities
  1351         // remove fpr from all users
  1352         status = remove_fpr_as_default(session, fpr_copy);
  1353         // delete key from DB - this does NOT touch the keyring!
  1354         // Note: for own priv keys, we cannot do this. But we'll never encrypt to/from it.
  1355         if (status == PEP_STATUS_OK && !is_own_private) {
  1356             status = remove_key(session, fpr_copy);
  1357         }
  1358         if (status == PEP_STATUS_OK)
  1359             status = cached_status;
  1360     }           
  1361         
  1362 pEp_free:
  1363     if (!ident)
  1364         free_identity(tmp_ident);
  1365     free(fpr_copy);
  1366     free(own_id);
  1367     free_identity_list(key_idents);
  1368     free_stringlist(keys);
  1369     free(new_key);    
  1370     return status;
  1371 }
  1372 
  1373 Distribution_t *Distribution_from_keyreset_command_list(
  1374         const keyreset_command_list *command_list,
  1375         Distribution_t *dist
  1376     )
  1377 {
  1378     bool allocated = !dist;
  1379 
  1380     assert(command_list);
  1381     if (!command_list)
  1382         return NULL;
  1383 
  1384     if (allocated)
  1385         dist = (Distribution_t *) calloc(1, sizeof(Distribution_t));
  1386 
  1387     assert(dist);
  1388     if (!dist)
  1389         goto enomem;
  1390 
  1391     dist->present = Distribution_PR_keyreset;
  1392     dist->choice.keyreset.present = KeyReset_PR_commands;
  1393 
  1394     long *major = malloc(sizeof(long));
  1395     assert(major);
  1396     if (!major)
  1397         goto enomem;
  1398     *major = KEY_RESET_MAJOR_VERSION;
  1399     dist->choice.keyreset.choice.commands.version.major = major;
  1400 
  1401     long *minor = malloc(sizeof(long));
  1402     assert(minor);
  1403     if (!minor)
  1404         goto enomem;
  1405     *minor = KEY_RESET_MINOR_VERSION;
  1406     dist->choice.keyreset.choice.commands.version.minor = minor;
  1407 
  1408     for (const keyreset_command_list *cl = command_list; cl && cl->command; cl = cl->next) {
  1409         Command_t *c = (Command_t *) calloc(1, sizeof(Command_t));
  1410         assert(c);
  1411         if (!c)
  1412             goto enomem;
  1413 
  1414         if (!Identity_from_Struct(cl->command->ident, &c->ident)) {
  1415             free(c);
  1416             goto enomem;
  1417         }
  1418 
  1419         if (OCTET_STRING_fromString(&c->newkey, cl->command->new_key)) {
  1420             ASN_STRUCT_FREE(asn_DEF_Command, c);
  1421             goto enomem;
  1422         }
  1423 
  1424         if (ASN_SEQUENCE_ADD(&dist->choice.keyreset.choice.commands.commandlist, c)) {
  1425             ASN_STRUCT_FREE(asn_DEF_Command, c);
  1426             goto enomem;
  1427         }
  1428     }
  1429 
  1430     return dist;
  1431 
  1432 enomem:
  1433     ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
  1434     return NULL;
  1435 }
  1436 
  1437 
  1438 PEP_STATUS key_reset_commands_to_PER(const keyreset_command_list *command_list, char **cmds, size_t *size)
  1439 {
  1440     PEP_STATUS status = PEP_STATUS_OK;
  1441 
  1442     assert(command_list && cmds);
  1443     if (!(command_list && cmds))
  1444         return PEP_ILLEGAL_VALUE;
  1445 
  1446     *cmds = NULL;
  1447     *size = 0;
  1448 
  1449     // convert from pEp engine struct
  1450 
  1451     Distribution_t *dist = Distribution_from_keyreset_command_list(command_list, NULL);
  1452     assert(dist);
  1453     if (!dist)
  1454         goto enomem;
  1455 
  1456     // encode
  1457 
  1458     char *_cmds;
  1459     size_t _size;
  1460     status = encode_Distribution_message(dist, &_cmds, &_size);
  1461     if (status)
  1462         goto the_end;
  1463 
  1464     // return result
  1465 
  1466     *cmds = _cmds;
  1467     *size = _size;
  1468     goto the_end;
  1469 
  1470 enomem:
  1471     status = PEP_OUT_OF_MEMORY;
  1472 
  1473 the_end:
  1474     ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
  1475     return status;
  1476 }
  1477 
  1478 keyreset_command_list * Distribution_to_keyreset_command_list(
  1479         Distribution_t *dist,
  1480         keyreset_command_list *command_list
  1481     )
  1482 {
  1483     bool allocated = !command_list;
  1484 
  1485     assert(dist);
  1486     if (!dist)
  1487         return NULL;
  1488 
  1489     if (allocated)
  1490         command_list = new_keyreset_command_list(NULL);
  1491     if (!command_list)
  1492         goto enomem;
  1493 
  1494     struct Commands__commandlist *cl = &dist->choice.keyreset.choice.commands.commandlist;
  1495     keyreset_command_list *_result = command_list;
  1496     for (int i=0; i<cl->list.count; i++) {
  1497         pEp_identity *ident = Identity_to_Struct(&cl->list.array[i]->ident, NULL);
  1498         if (!ident)
  1499             goto enomem;
  1500 
  1501         const char *new_key = (const char *) cl->list.array[i]->newkey.buf;
  1502 
  1503         keyreset_command *command = new_keyreset_command(ident, new_key);
  1504         if (!command) {
  1505             free_identity(ident);
  1506             goto enomem;
  1507         }
  1508 
  1509         _result = keyreset_command_list_add(_result, command);
  1510         free_identity(ident);
  1511         if (!_result)
  1512             goto enomem;
  1513     }
  1514 
  1515     return command_list;
  1516 
  1517 enomem:
  1518     if (allocated)
  1519         free_keyreset_command_list(command_list);
  1520     return NULL;
  1521 }
  1522 
  1523 PEP_STATUS PER_to_key_reset_commands(const char *cmds, size_t size, keyreset_command_list **command_list)
  1524 {
  1525     assert(command_list && cmds);
  1526     if (!(command_list && cmds))
  1527         return PEP_ILLEGAL_VALUE;
  1528 
  1529     *command_list = NULL;
  1530 
  1531     // decode
  1532 
  1533     Distribution_t *dist = NULL;
  1534     PEP_STATUS status = decode_Distribution_message(cmds, size, &dist);
  1535     if (status)
  1536         goto the_end;
  1537 
  1538     // check if these are key reset commands or not
  1539 
  1540     assert(dist && dist->present == Distribution_PR_keyreset
  1541             && dist->choice.keyreset.present == KeyReset_PR_commands);
  1542 
  1543     if (!(dist && dist->present == Distribution_PR_keyreset
  1544             && dist->choice.keyreset.present == KeyReset_PR_commands)) {
  1545         status = PEP_ILLEGAL_VALUE;
  1546         goto the_end;
  1547     }
  1548 
  1549     // convert to pEp engine struct
  1550 
  1551     keyreset_command_list *result = Distribution_to_keyreset_command_list(dist, NULL);
  1552     if (!result)
  1553         goto enomem;
  1554 
  1555     // return result
  1556 
  1557     *command_list = result;
  1558     goto the_end;
  1559 
  1560 enomem:
  1561     status = PEP_OUT_OF_MEMORY;
  1562     free_keyreset_command_list(result);
  1563 
  1564 the_end:
  1565     ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
  1566     return status;
  1567 }