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