src/key_reset.c
author Volker Birk <vb@pep-project.org>
Thu, 12 Dec 2019 06:31:35 +0100
branchsync
changeset 4265 441c82c3d526
parent 4264 d709b7af2f6c
child 4266 b7012c29e885
permissions -rw-r--r--
...
     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 "../asn.1/Distribution.h"
    11 
    12 #include <string.h>
    13 #include <stdlib.h>
    14 
    15 PEP_STATUS has_key_reset_been_sent(
    16         PEP_SESSION session, 
    17         const char* user_id, 
    18         const char* revoked_fpr,
    19         bool* contacted)
    20 {
    21     assert(session);
    22     assert(contacted);
    23     assert(user_id);
    24     assert(revoked_fpr);
    25     assert(!EMPTYSTR(user_id));
    26 
    27     if (!session || !contacted || EMPTYSTR(revoked_fpr) || EMPTYSTR(user_id))
    28         return PEP_ILLEGAL_VALUE;
    29     
    30     *contacted = false;
    31                     
    32     char* alias_default = NULL;
    33     
    34     PEP_STATUS status = get_userid_alias_default(session, user_id, &alias_default);
    35     
    36     if (status == PEP_CANNOT_FIND_ALIAS || EMPTYSTR(alias_default)) {
    37         free(alias_default);
    38         alias_default = strdup(user_id);
    39     }
    40     
    41     sqlite3_reset(session->was_id_for_revoke_contacted);
    42     sqlite3_bind_text(session->was_id_for_revoke_contacted, 1, revoked_fpr, -1,
    43             SQLITE_STATIC);
    44     sqlite3_bind_text(session->was_id_for_revoke_contacted, 2, user_id, -1,
    45             SQLITE_STATIC);        
    46     int result = sqlite3_step(session->was_id_for_revoke_contacted);
    47     switch (result) {
    48         case SQLITE_ROW: {
    49             *contacted = (sqlite3_column_int(session->was_id_for_revoke_contacted, 0) != 0);
    50             break;
    51         }
    52         default:
    53             sqlite3_reset(session->was_id_for_revoke_contacted);
    54             free(alias_default);
    55             return PEP_UNKNOWN_DB_ERROR;
    56     }
    57 
    58     sqlite3_reset(session->was_id_for_revoke_contacted);
    59     return PEP_STATUS_OK;
    60 }
    61 
    62 //static const char *sql_set_revoke_contact_as_notified =
    63 //    "insert or replace into revocation_contact_list(fpr, contact_id) values (?1, ?2) ;";
    64 
    65 PEP_STATUS set_reset_contact_notified(
    66         PEP_SESSION session,
    67         const char* revoke_fpr,
    68         const char* contact_id
    69     )
    70 {
    71     PEP_STATUS status = PEP_STATUS_OK;
    72     
    73     assert(session && !EMPTYSTR(revoke_fpr) && !EMPTYSTR(contact_id));
    74     
    75     if (!session || EMPTYSTR(revoke_fpr) || EMPTYSTR(contact_id))
    76         return PEP_ILLEGAL_VALUE;
    77     
    78     sqlite3_reset(session->set_revoke_contact_as_notified);
    79     sqlite3_bind_text(session->set_revoke_contact_as_notified, 1, revoke_fpr, -1, 
    80             SQLITE_STATIC);
    81     sqlite3_bind_text(session->set_revoke_contact_as_notified, 2, contact_id, -1,
    82             SQLITE_STATIC);
    83 
    84     int result;
    85     
    86     result = sqlite3_step(session->set_revoke_contact_as_notified);
    87     switch (result) {
    88         case SQLITE_DONE:
    89             status = PEP_STATUS_OK;
    90             break;
    91             
    92         default:
    93             status = PEP_UNKNOWN_DB_ERROR;
    94     }
    95     
    96     sqlite3_reset(session->set_revoke_contact_as_notified);
    97     return status;    
    98 }
    99 
   100 
   101 PEP_STATUS receive_key_reset(PEP_SESSION session,
   102                              message* reset_msg) {
   103 
   104     if (!session || !reset_msg)
   105         return PEP_ILLEGAL_VALUE;
   106 
   107     pEp_identity* sender_id = reset_msg->from;
   108                 
   109     if (!sender_id)
   110         return PEP_MALFORMED_KEY_RESET_MSG;
   111         
   112     PEP_STATUS status = update_identity(session, sender_id);
   113     if (!sender_id->user_id)
   114         return PEP_UNKNOWN_ERROR;
   115         
   116     if (is_me(session, sender_id))
   117         return PEP_ILLEGAL_VALUE;    
   118         
   119     if (!reset_msg->longmsg || strncmp(reset_msg->longmsg, "OLD: ", 5) != 0) 
   120         return PEP_MALFORMED_KEY_RESET_MSG;
   121 
   122     status = PEP_STATUS_OK;
   123     char* old_fpr = NULL;
   124     char* new_fpr = NULL;
   125     
   126     stringlist_t* keylist = NULL;
   127     pEp_identity* temp_ident = identity_dup(sender_id);
   128     if (!temp_ident) {
   129         status = PEP_OUT_OF_MEMORY;
   130         goto pEp_free;
   131     }        
   132             
   133     char* rest = NULL;
   134     char* p = strtok_r(reset_msg->longmsg, "\r\n", &rest);
   135     if (!EMPTYSTR(p + 5))
   136         old_fpr = strdup(p + 5);
   137     else {
   138         status = PEP_MALFORMED_KEY_RESET_MSG;
   139         goto pEp_free;
   140     }
   141     
   142     bool own_key = false;
   143     status = is_own_key(session, old_fpr, &own_key);
   144     
   145     if (own_key) {
   146         // Nope, no one can make us our own default. If we want to do that,
   147         // that's keysync, NOT key reset.
   148         status = PEP_ILLEGAL_VALUE;
   149         goto pEp_free;
   150     }
   151             
   152     p = strtok_r(NULL, "\r\n", &rest); 
   153     if (strncmp(p, "NEW: ", 5) != 0  || EMPTYSTR(p + 5)) {
   154         status = PEP_MALFORMED_KEY_RESET_MSG;
   155         goto pEp_free;
   156     }
   157 
   158     new_fpr = strdup(p + 5);
   159         
   160     // Reset the original key
   161     status = key_reset(session, old_fpr, temp_ident, NULL, NULL);
   162     if (status != PEP_STATUS_OK)
   163         goto pEp_free;
   164         
   165     status = find_keys(session, new_fpr, &keylist);
   166     if (status != PEP_STATUS_OK)
   167         goto pEp_free;
   168         
   169     if (!keylist) {
   170         status = PEP_KEY_NOT_FOUND;
   171         goto pEp_free;
   172     }
   173 
   174     // alright, we've checked as best we can. Let's set that baby.
   175     sender_id->fpr = new_fpr;
   176     
   177     // This only sets as the default, does NOT TRUST IN ANY WAY
   178     sender_id->comm_type = sender_id->comm_type & (~PEP_ct_confirmed);
   179     status = set_identity(session, sender_id);
   180     
   181     sender_id->fpr = NULL; // ownership for free
   182 pEp_free:    
   183     free_stringlist(keylist);    
   184     free(old_fpr);
   185     free(new_fpr);
   186     free_identity(temp_ident);
   187     return status;
   188 }
   189 
   190 PEP_STATUS create_standalone_key_reset_message(PEP_SESSION session,
   191                                                message** dst, 
   192                                                pEp_identity* recip,
   193                                                const char* old_fpr,
   194                                                const char* new_fpr) {
   195                                                    
   196     if (!dst || !recip->user_id || !recip->address)
   197         return PEP_ILLEGAL_VALUE;
   198 
   199     if (!old_fpr || !new_fpr)
   200         return PEP_ILLEGAL_VALUE;
   201         
   202     *dst = NULL;
   203     // Get own identity user has corresponded with
   204     pEp_identity* own_identity = NULL;
   205     
   206     PEP_STATUS status = get_own_ident_for_contact_id(session,
   207                                                      recip,
   208                                                      &own_identity);                                                       
   209     if (status != PEP_STATUS_OK)
   210         return status;
   211         
   212     message* reset_message = new_message(PEP_dir_outgoing);
   213     reset_message->from = own_identity;
   214     reset_message->to = new_identity_list(identity_dup(recip)); // ?
   215     
   216     const char* oldtag = "OLD: ";
   217     const char* newtag = "\nNEW: ";
   218     const size_t taglens = 11;
   219     size_t full_len = taglens + strlen(old_fpr) + strlen(new_fpr) + 2; // \n and \0
   220     char* longmsg = calloc(full_len, 1);
   221     strlcpy(longmsg, oldtag, full_len);
   222     strlcat(longmsg, old_fpr, full_len);
   223     strlcat(longmsg, newtag, full_len);
   224     strlcat(longmsg, new_fpr, full_len);
   225     strlcat(longmsg, "\n", full_len);
   226     reset_message->longmsg = longmsg; 
   227     reset_message->shortmsg = strdup("Key reset");    
   228     
   229     message* output_msg = NULL;
   230     
   231     status = encrypt_message(session, reset_message, NULL,
   232                              &output_msg, PEP_enc_PGP_MIME,
   233                              PEP_encrypt_flag_key_reset_only);
   234 
   235     if (status == PEP_STATUS_OK)
   236         *dst = output_msg;
   237         
   238     free_message(reset_message);
   239     return status;
   240 }
   241 
   242 PEP_STATUS send_key_reset_to_recents(PEP_SESSION session,
   243                                      const char* old_fpr, 
   244                                      const char* new_fpr) {
   245     assert(old_fpr);
   246     assert(new_fpr);
   247     assert(session);
   248     assert(session->messageToSend);
   249     
   250     if (!session || !old_fpr || !new_fpr)
   251         return PEP_ILLEGAL_VALUE;
   252 
   253     messageToSend_t send_cb = session->messageToSend;
   254     if (!send_cb)
   255         return PEP_SYNC_NO_MESSAGE_SEND_CALLBACK;
   256         
   257     identity_list* recent_contacts = NULL;
   258     message* reset_msg = NULL;
   259 
   260     PEP_STATUS status = get_last_contacted(session, &recent_contacts);
   261     
   262     if (status != PEP_STATUS_OK)
   263         goto pEp_free;
   264                     
   265     identity_list* curr_id_ptr = recent_contacts;
   266 
   267     for (curr_id_ptr = recent_contacts; curr_id_ptr; curr_id_ptr = curr_id_ptr->next) {
   268         pEp_identity* curr_id = curr_id_ptr->ident;
   269         
   270         if (!curr_id)
   271             break;
   272     
   273         const char* user_id = curr_id->user_id;
   274         
   275         // Should be impossible, but?
   276         if (!user_id)
   277             continue;
   278         
   279         // Check if it's us - if so, pointless...
   280         if (is_me(session, curr_id))
   281             continue;
   282             
   283         // Check if they've already been told - this shouldn't be the case, but...
   284         bool contacted = false;
   285         status = has_key_reset_been_sent(session, user_id, old_fpr, &contacted);
   286         if (status != PEP_STATUS_OK)
   287             goto pEp_free;
   288     
   289         if (contacted)
   290             continue;
   291             
   292         // if not, make em a message    
   293         reset_msg = NULL;
   294         
   295         status = create_standalone_key_reset_message(session,
   296                                                      &reset_msg,
   297                                                      curr_id,
   298                                                      old_fpr,
   299                                                      new_fpr);
   300 
   301         if (status == PEP_CANNOT_FIND_IDENTITY) { // this is ok, just means we never mailed them 
   302             status = PEP_STATUS_OK;
   303             continue; 
   304         }
   305             
   306         if (status != PEP_STATUS_OK) {
   307             free(reset_msg);
   308             goto pEp_free;
   309         }
   310         
   311         // insert into queue
   312         status = send_cb(reset_msg);
   313 
   314         if (status != PEP_STATUS_OK) {
   315             free(reset_msg);
   316             goto pEp_free;            
   317         }
   318             
   319         // Put into notified DB
   320         status = set_reset_contact_notified(session, old_fpr, user_id);
   321         if (status != PEP_STATUS_OK)
   322             goto pEp_free;            
   323     }
   324     
   325 pEp_free:
   326     free_identity_list(recent_contacts);
   327     return status;
   328 }
   329 
   330 DYNAMIC_API PEP_STATUS key_reset_identity(
   331         PEP_SESSION session,
   332         pEp_identity* ident,
   333         const char* fpr        
   334     )
   335 {
   336     if (!session || !ident || (ident && (EMPTYSTR(ident->user_id) || EMPTYSTR(ident->address))))
   337         return PEP_ILLEGAL_VALUE;
   338     
   339     return key_reset(session, fpr, ident, NULL, NULL);    
   340 }
   341 
   342 DYNAMIC_API PEP_STATUS key_reset_user(
   343         PEP_SESSION session,
   344         const char* user_id,
   345         const char* fpr        
   346     )
   347 {
   348     if (!session || EMPTYSTR(user_id))
   349         return PEP_ILLEGAL_VALUE;
   350 
   351     pEp_identity* input_ident = new_identity(NULL, NULL, user_id, NULL);
   352     if (!input_ident)
   353         return PEP_OUT_OF_MEMORY;
   354         
   355     if (is_me(session, input_ident) && EMPTYSTR(fpr))
   356         return PEP_ILLEGAL_VALUE;
   357         
   358     PEP_STATUS status = key_reset(session, fpr, input_ident, NULL, NULL);
   359     free_identity(input_ident);
   360     return status;
   361 }
   362 
   363 DYNAMIC_API PEP_STATUS key_reset_all_own_keys(PEP_SESSION session) {
   364     return key_reset(session, NULL, NULL, NULL, NULL);
   365 }
   366 
   367 // Notes to integrate into header:
   368 // IF there is an ident, it must have a user_id.
   369 PEP_STATUS key_reset(
   370         PEP_SESSION session,
   371         const char* key_id,
   372         pEp_identity* ident,
   373         identity_list** own_identities,
   374         stringlist_t** own_revoked_fprs
   375     )
   376 {
   377     if (!session || (ident && EMPTYSTR(ident->user_id)))
   378         return PEP_ILLEGAL_VALUE;
   379         
   380     PEP_STATUS status = PEP_STATUS_OK;
   381         
   382     char* fpr_copy = NULL;
   383     char* own_id = NULL;
   384     char* user_id = NULL;
   385     char* new_key = NULL;
   386     pEp_identity* tmp_ident = NULL;
   387     identity_list* key_idents = NULL;
   388     stringlist_t* keys = NULL;
   389     
   390     if (!EMPTYSTR(key_id)) {
   391         fpr_copy = strdup(key_id);
   392         if (!fpr_copy)
   393             return PEP_OUT_OF_MEMORY;
   394     }
   395 
   396     // This is true when we don't have a user_id and address and the fpr isn't specified
   397     bool reset_all_for_user = !fpr_copy && (!ident || EMPTYSTR(ident->address));
   398 
   399     // FIXME: does this need to be done everywhere?> I think not.
   400     if (ident) {
   401         user_id = strdup(ident->user_id);
   402         if (!user_id) {
   403             status = PEP_OUT_OF_MEMORY;
   404             goto pEp_free;
   405         }
   406     }
   407     else {
   408         status = get_default_own_userid(session, &user_id);
   409         if (status != PEP_STATUS_OK || !user_id)
   410             goto pEp_free;                    
   411     }
   412     
   413     // FIXME: Make sure this can't result in a double-free in recursive calls
   414     tmp_ident = (ident ? identity_dup(ident) : new_identity(NULL, NULL, user_id, NULL));
   415     
   416     if (reset_all_for_user) {
   417         status = get_all_keys_for_user(session, user_id, &keys);
   418         // TODO: free
   419         if (status == PEP_STATUS_OK) {
   420             stringlist_t* curr_key;
   421             
   422             for (curr_key = keys; curr_key && curr_key->value; curr_key = curr_key->next) {
   423                 // FIXME: Is the ident really necessary?
   424                 status = key_reset(session, curr_key->value, tmp_ident, own_identities, own_revoked_fprs);
   425                 if (status != PEP_STATUS_OK)
   426                     break;
   427             }
   428         }
   429         goto pEp_free;
   430     }                   
   431     else {
   432         // tmp_ident => tmp_ident->user_id (was checked)
   433         //
   434         // !(EMPTYSTR(fpr) && (!tmp_ident || EMPTYSTR(tmp_ident->address)))
   435         // => fpr || (tmp_ident && tmp_ident->address)
   436         //
   437         // so: We have an fpr or we have an ident with user_id and address
   438         //     or both
   439         if (!fpr_copy) {
   440             // We are guaranteed to have an ident w/ id + addr here.
   441             // Get the default key.
   442             pEp_identity* stored_ident = NULL;
   443             status = get_identity(session, tmp_ident->address, 
   444                                   tmp_ident->user_id, &stored_ident);
   445 
   446             // FIXME FIXME FIXME
   447             if (status == PEP_STATUS_OK) {
   448                 // transfer ownership
   449                 fpr_copy = stored_ident->fpr;
   450                 stored_ident->fpr = NULL;
   451                 free_identity(stored_ident);                
   452             }
   453             
   454             if (!fpr_copy || status == PEP_CANNOT_FIND_IDENTITY) {
   455                 // There's no identity default. Try resetting user default
   456                 status = get_user_default_key(session, tmp_ident->user_id, &fpr_copy);
   457             }            
   458                         
   459             if (!fpr_copy || status != PEP_STATUS_OK) // No default to free. We're done here.
   460                 goto pEp_free;            
   461         }
   462         
   463         // Ok - now we have at least an ident with user_id and an fpr.
   464         // Now it matters if we're talking about ourselves or a partner.
   465         bool is_own_private = false;
   466         if (is_me(session, tmp_ident)) {
   467             bool own_key = false;            
   468             status = is_own_key(session, fpr_copy, &own_key);
   469 
   470             if (status != PEP_STATUS_OK)
   471                 goto pEp_free;
   472             if (!own_key) {
   473                 status = PEP_ILLEGAL_VALUE;
   474                 goto pEp_free;
   475             }
   476 
   477             status = contains_priv_key(session, fpr_copy, &is_own_private);
   478             if (status != PEP_STATUS_OK && status != PEP_KEY_NOT_FOUND)
   479                 goto pEp_free;
   480         }
   481         
   482         // Up to this point, we haven't cared about whether or not we 
   483         // had a full identity. Now we have to deal with that in the 
   484         // case of own identities with private keys.
   485         
   486         if (is_own_private) {
   487             
   488             // If there's no address, we want to reset this key for every identity 
   489             // it's a part of. Since this means generating new keys, we have to 
   490             // grab all the identities associated with it.
   491             if (EMPTYSTR(tmp_ident->address)) {
   492                 status = get_identities_by_main_key_id(session, fpr_copy, &key_idents);
   493                 
   494                 if (status != PEP_CANNOT_FIND_IDENTITY) {
   495                     if (status == PEP_STATUS_OK) {
   496                         // now have ident list, or should
   497                         identity_list* curr_ident;
   498                         
   499                         for (curr_ident = key_idents; curr_ident && curr_ident->ident; 
   500                                                         curr_ident = curr_ident->next) {
   501                             
   502                             pEp_identity* this_identity = curr_ident->ident;
   503                             // Do the full reset on this identity        
   504                             status = key_reset(session, fpr_copy, this_identity, own_identities, own_revoked_fprs);
   505                             
   506                             // Ident list gets freed below, do not free here!
   507 
   508                             if (status != PEP_STATUS_OK)
   509                                 break;
   510                             
   511                         }
   512                     }
   513                     // Ok, we've either now reset for each own identity with this key, or 
   514                     // we got an error and want to bail anyway.
   515                     goto pEp_free;
   516                 }    
   517             }
   518             
   519             // Base case for is_own_private starts here
   520             
   521             status = revoke_key(session, fpr_copy, NULL);
   522             
   523             // If we have a full identity, we have some cleanup and generation tasks here
   524             if (!EMPTYSTR(tmp_ident->address)) {
   525                 // generate new key
   526                 if (status == PEP_STATUS_OK) {
   527                     tmp_ident->fpr = NULL;
   528                     status = myself(session, tmp_ident);
   529                 }
   530                 if (status == PEP_STATUS_OK && tmp_ident->fpr && strcmp(fpr_copy, tmp_ident->fpr) != 0) {
   531                     new_key = strdup(tmp_ident->fpr);
   532 //                    status = set_own_key(session, tmp_ident, new_key);
   533                 }
   534 
   535                 if (own_revoked_fprs) {
   536                     // We can dedup this later
   537                     if (!(*own_revoked_fprs))
   538                         *own_revoked_fprs = new_stringlist(NULL);
   539                     
   540                     char* revkey = strdup(fpr_copy);
   541                     if (!revkey) {
   542                         status = PEP_OUT_OF_MEMORY;
   543                         goto pEp_free;
   544                     }
   545                     stringlist_add(*own_revoked_fprs, revkey);                
   546                 }
   547                 
   548                 // mistrust fpr from trust
   549                 tmp_ident->fpr = fpr_copy;
   550                                                 
   551                 tmp_ident->comm_type = PEP_ct_mistrusted;
   552                 status = set_trust(session, tmp_ident);
   553                 tmp_ident->fpr = NULL;
   554                 
   555                 // Done with old use of ident.
   556                 if (status == PEP_STATUS_OK) {
   557                     // Update fpr for outgoing
   558                     status = myself(session, tmp_ident);
   559                 }
   560                 
   561                 if (status == PEP_STATUS_OK && own_identities) {
   562                     if (!(*own_identities))
   563                         *own_identities = new_identity_list(NULL);
   564                     
   565                     pEp_identity* new_ident = identity_dup(tmp_ident);
   566                     if (!new_ident) {
   567                         status = PEP_OUT_OF_MEMORY;
   568                         goto pEp_free;
   569                     }
   570                     identity_list_add(*own_identities, new_ident);            
   571                 }    
   572             }    
   573             
   574             if (status == PEP_STATUS_OK)
   575                 // cascade that mistrust for anyone using this key
   576                 status = mark_as_compromised(session, fpr_copy);
   577                 
   578             if (status == PEP_STATUS_OK)
   579                 status = remove_fpr_as_default(session, fpr_copy);
   580             if (status == PEP_STATUS_OK)
   581                 status = add_mistrusted_key(session, fpr_copy);
   582 
   583             // If there's a new key, do the DB linkage with the revoked one, and 
   584             // send the key reset mail opportunistically to recently contacted
   585             // partners
   586             if (new_key) {
   587                 // add to revocation list 
   588                 if (status == PEP_STATUS_OK) 
   589                     status = set_revoked(session, fpr_copy, new_key, time(NULL));            
   590                 // for all active communication partners:
   591                 //      active_send revocation
   592                 if (status == PEP_STATUS_OK)
   593                     status = send_key_reset_to_recents(session, fpr_copy, new_key);        
   594             }        
   595         } // end is_own_private
   596         else {
   597             // if it's mistrusted, make it not be so.
   598             bool mistrusted_key = false;
   599             is_mistrusted_key(session, fpr_copy, &mistrusted_key);
   600 
   601             if (mistrusted_key)
   602                 delete_mistrusted_key(session, fpr_copy);
   603             
   604             if (tmp_ident->user_id)
   605                 status = clear_trust_info(session, tmp_ident->user_id, fpr_copy);
   606 
   607             // This is a public key (or a private key that isn't ours, which means
   608             // we want it gone anyway)
   609             //
   610             // Delete this key from the keyring.
   611             // FIXME: when key election disappears, so should this!
   612             status = delete_keypair(session, fpr_copy);
   613         }
   614 
   615         // REGARDLESS OF WHO OWNS THE KEY, WE NOW NEED TO REMOVE IT AS A DEFAULT.
   616         PEP_STATUS cached_status = status;
   617         // remove fpr from all identities
   618         // remove fpr from all users
   619         status = remove_fpr_as_default(session, fpr_copy);
   620         // delete key from DB - this does NOT touch the keyring!
   621         // Note: for own priv keys, we cannot do this. But we'll never encrypt to/from it.
   622         if (status == PEP_STATUS_OK && !is_own_private) {
   623             status = remove_key(session, fpr_copy);
   624         }
   625         if (status == PEP_STATUS_OK)
   626             status = cached_status;
   627     }           
   628         
   629 pEp_free:
   630     if (!ident)
   631         free_identity(tmp_ident);
   632     free(fpr_copy);
   633     free(own_id);
   634     free_identity_list(key_idents);
   635     free_stringlist(keys);
   636     free(new_key);    
   637     return status;
   638 }
   639 
   640 static stringlist_t* collect_key_material(PEP_SESSION session, stringlist_t* fprs) {
   641     stringlist_t* keydata = NULL;    
   642     stringlist_t* curr_fpr = fprs;    
   643     while (curr_fpr) {
   644         if (curr_fpr->value) {
   645             char* key_material = NULL;
   646             size_t datasize = 0;
   647             PEP_STATUS status = export_key(session, curr_fpr->value, &key_material, &datasize);
   648             if (status) {
   649                 free_stringlist(keydata);
   650                 return NULL;
   651             }
   652             if (datasize > 0 && key_material) {
   653                 if (!(keydata))
   654                     keydata = new_stringlist(NULL);
   655                     
   656                 stringlist_add(keydata, key_material);
   657             }
   658         }
   659         curr_fpr = curr_fpr->next;        
   660     }   
   661     return keydata; 
   662 }
   663 
   664 PEP_STATUS key_reset_own_and_deliver_revocations(PEP_SESSION session, 
   665                                                  identity_list** own_identities, 
   666                                                  stringlist_t** revocations, 
   667                                                  stringlist_t** keys) {
   668 
   669     if (!(session && own_identities && revocations && keys))
   670         return PEP_ILLEGAL_VALUE;
   671         
   672     stringlist_t* revoked_fprs = NULL;
   673     identity_list* affected_idents = NULL;
   674         
   675     PEP_STATUS status = key_reset(session, NULL, NULL, &affected_idents, &revoked_fprs);                                                 
   676 
   677     // FIXME: free things
   678     if (status != PEP_STATUS_OK)
   679         return status;
   680     
   681     dedup_stringlist(revoked_fprs);
   682 
   683     *revocations = collect_key_material(session, revoked_fprs);
   684     stringlist_t* keydata = NULL;
   685     
   686     if (affected_idents) {
   687         keydata = new_stringlist(NULL);
   688         identity_list* curr_ident = affected_idents;
   689         while (curr_ident) {
   690             if (curr_ident->ident && curr_ident->ident->fpr) {
   691                 char* key_material = NULL;
   692                 size_t datasize = 0;
   693                 status = export_key(session, curr_ident->ident->fpr, &key_material, &datasize);
   694                 if (status) {
   695                     free_stringlist(keydata);
   696                     return status;
   697                 }
   698                 if (datasize > 0 && key_material)
   699                     stringlist_add(keydata, key_material);
   700             }
   701             curr_ident = curr_ident->next;
   702         }
   703     }
   704     
   705     *own_identities = affected_idents;
   706     *keys = keydata;
   707     
   708     free(revoked_fprs);
   709     return PEP_STATUS_OK;
   710 }
   711 
   712 PEP_STATUS key_reset_commands_to_PER(const keyreset_command_list *command_list, char **cmds, size_t *size)
   713 {
   714     PEP_STATUS status = PEP_STATUS_OK;
   715 
   716     assert(command_list && cmds);
   717     if (!(command_list && cmds))
   718         return PEP_ILLEGAL_VALUE;
   719 
   720     *cmds = NULL;
   721     *size = 0;
   722 
   723     Distribution_t *dist = (Distribution_t *) calloc(1, sizeof(Distribution_t));
   724     assert(dist);
   725     if (!dist)
   726         goto enomem;
   727 
   728     dist->present = Distribution_PR_keyreset;
   729     dist->choice.keyreset.present = KeyReset_PR_commands;
   730 
   731     // convert to ASN.1 struct
   732 
   733     for (const keyreset_command_list *cl = command_list; cl && cl->command; cl = cl->next) {
   734         Command_t *c = (Command_t *) calloc(1, sizeof(Command_t));
   735         assert(c);
   736         if (!c)
   737             goto enomem;
   738 
   739         if (!Identity_from_Struct(cl->command->ident, &c->ident)) {
   740             free(c);
   741             goto enomem;
   742         }
   743 
   744         if (OCTET_STRING_fromString(&c->newkey, cl->command->new_key)) {
   745             ASN_STRUCT_FREE(asn_DEF_Command, c);
   746             goto enomem;
   747         }
   748 
   749         if (ASN_SEQUENCE_ADD(&dist->choice.keyreset.choice.commands.commandlist, c)) {
   750             ASN_STRUCT_FREE(asn_DEF_Command, c);
   751             goto enomem;
   752         }
   753     }
   754 
   755     // encode
   756 
   757     char *_cmds;
   758     size_t _size;
   759     status = encode_Distribution_message(dist, &_cmds, &_size);
   760     if (status)
   761         goto the_end;
   762 
   763     *cmds = _cmds;
   764     *size = _size;
   765     goto the_end;
   766 
   767 enomem:
   768     status = PEP_OUT_OF_MEMORY;
   769 
   770 the_end:
   771     ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
   772     return status;
   773 }
   774 
   775 PEP_STATUS PER_to_key_reset_commands(const char *cmds, size_t size, keyreset_command_list **command_list)
   776 {
   777     assert(command_list && cmds);
   778     if (!(command_list && cmds))
   779         return PEP_ILLEGAL_VALUE;
   780 
   781     *command_list = NULL;
   782     keyreset_command_list *result = NULL;
   783 
   784     Distribution_t *dist = NULL;
   785     PEP_STATUS status = decode_Distribution_message(cmds, size, &dist);
   786     if (status)
   787         goto the_end;
   788 
   789     assert(dist && dist->present == Distribution_PR_keyreset
   790             && dist->choice.keyreset.present == KeyReset_PR_commands);
   791 
   792     if (!(dist && dist->present == Distribution_PR_keyreset
   793             && dist->choice.keyreset.present == KeyReset_PR_commands)) {
   794         status = PEP_ILLEGAL_VALUE;
   795         goto the_end;
   796     }
   797 
   798     result = new_keyreset_command_list(NULL);
   799     if (!result)
   800         goto enomem;
   801 
   802     struct Commands__commandlist *cl = &dist->choice.keyreset.choice.commands.commandlist;
   803     keyreset_command_list *_result = result;
   804     for (int i=0; i<cl->list.count; i++) {
   805         pEp_identity *ident = Identity_to_Struct(&cl->list.array[i]->ident, NULL);
   806         if (!ident)
   807             goto enomem;
   808 
   809         const char *new_key = (const char *) cl->list.array[i]->newkey.buf;
   810         keyreset_command *command = new_keyreset_command(ident, new_key);
   811         if (!command) {
   812             free_identity(ident);
   813             goto enomem;
   814         }
   815 
   816         _result = keyreset_command_list_add(_result, command);
   817         free_identity(ident);
   818         if (!_result)
   819             goto enomem;
   820     }
   821 
   822     *command_list = result;
   823     goto the_end;
   824 
   825 enomem:
   826     status = PEP_OUT_OF_MEMORY;
   827     free_keyreset_command_list(result);
   828 
   829 the_end:
   830     ASN_STRUCT_FREE(asn_DEF_Distribution, dist);
   831     return status;
   832 }
   833