2  * merge.c - Routines to merge OpenPGP public keys.
 
   4  * Jonathan McDowell <noodles@earth.li>
 
   6  * Copyright 2002 Project Purple
 
   8  * $Id: merge.c,v 1.11 2003/06/07 13:45:35 noodles Exp $
 
  16 #include "decodekey.h"
 
  19 #include "keystructs.h"
 
  26  *      compare_packets - Check to see if 2 OpenPGP packets are the same.
 
  27  *      @a: The first packet to compare.
 
  28  *      @b: The second packet to compare.
 
  30  *      Takes 2 packets and returns true if they are the same and false
 
  33 bool compare_packets(struct openpgp_packet *a, struct openpgp_packet *b)
 
  35         return (a->tag == b->tag && a->length == b->length &&
 
  36                 !memcmp(a->data, b->data, b->length));
 
  40  *      compare_signatures - Check to see if 2 OpenPGP signatures are the same.
 
  41  *      @a: The first signature to compare.
 
  42  *      @b: The second signature to compare.
 
  44  *      Takes 2 signature packets and returns true if they are the same and
 
  47 bool compare_signatures(struct openpgp_packet *a, struct openpgp_packet *b)
 
  49         return (sig_keyid(a) == sig_keyid(b));
 
  53  *      find_packet - Checks to see if an OpenPGP packet exists in a list.
 
  54  *      @packet_list: The list of packets to look in.
 
  55  *      @packet: The packet to look for.
 
  57  *      Walks through the packet_list checking to see if the packet given is
 
  58  *      present in it. Returns true if it is.
 
  60 bool find_packet(struct openpgp_packet_list *packet_list,
 
  61                         struct openpgp_packet *packet)
 
  65         while (!found && packet_list != NULL) {
 
  66                 if (compare_packets(packet_list->packet, packet)) {
 
  69                 packet_list = packet_list -> next;
 
  76  *      find_signature - Checks to see if an OpenPGP signature exists in a list.
 
  77  *      @packet_list: The list of packets to look in.
 
  78  *      @packet: The signature to look for.
 
  80  *      Walks through the packet_list checking to see if the signature given is
 
  81  *      present in it. Returns a pointer to it if it is, NULL otherwise.
 
  84 struct openpgp_packet_list *find_signature(
 
  85                         struct openpgp_packet_list *packet_list,
 
  86                         struct openpgp_packet *packet)
 
  88         struct openpgp_packet_list *found = NULL;
 
  90         while (!found && packet_list != NULL) {
 
  91                 if (compare_signatures(packet_list->packet, packet)) {
 
  94                 packet_list = packet_list -> next;
 
 101  *      get_signed_packet - Gets a signed packet from a list.
 
 102  *      @packet_list: The list of packets to look in.
 
 103  *      @packet: The packet to look for.
 
 105  *      Walks through the signedpacket_list looking for the supplied packet and
 
 106  *      returns it if found. Otherwise returns NULL.
 
 108 struct openpgp_signedpacket_list *find_signed_packet(
 
 109                 struct openpgp_signedpacket_list *packet_list,
 
 110                 struct openpgp_packet *packet)
 
 112         struct openpgp_signedpacket_list *found = NULL;
 
 114         while (found == NULL && packet_list != NULL) {
 
 115                 if (compare_packets(packet_list->packet, packet)) {
 
 118                 packet_list = packet_list -> next;
 
 125  *      remove_signed_packet - Removes a signed packet from a list.
 
 126  *      @packet_list: The list of packets to look in.
 
 127  *      @packet: The packet to remove.
 
 129  *      Walks through the signedpacket_list looking for the supplied packet and
 
 130  *      removes it if found. Assumes the packet can only exist a maximum of
 
 133 bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list,
 
 134                 struct openpgp_signedpacket_list **list_end,
 
 135                 struct openpgp_packet *packet)
 
 137         struct openpgp_signedpacket_list *cur = NULL;
 
 138         struct openpgp_signedpacket_list *prev = NULL;
 
 141         for (cur = *packet_list; !found && (cur != NULL); cur = cur->next) {
 
 142                 if (compare_packets(cur->packet, packet)) {
 
 145                                 *packet_list = cur->next;
 
 147                                 prev->next = cur->next;
 
 149                         if (cur->next == NULL) {
 
 153                          * TODO: Free the removed signed packet...
 
 163  *      merge_packet_sigs - Takes 2 signed packets and merges their sigs.
 
 164  *      @old: The old signed packet.
 
 165  *      @new: The new signed packet.
 
 167  *      Takes 2 signed packet list structures and the sigs of the packets on
 
 168  *      the head of these structures. These packets must both be the same and
 
 169  *      the fully merged structure is returned in old and the minimal
 
 170  *      difference to get from old to new in new.
 
 172 int merge_packet_sigs(struct openpgp_signedpacket_list *old,
 
 173                         struct openpgp_signedpacket_list *new)
 
 175         struct openpgp_packet_list      *lastpacket = NULL;
 
 176         struct openpgp_packet_list      *curpacket = NULL;
 
 177         struct openpgp_packet_list      *nextpacket = NULL;
 
 179         assert(compare_packets(old->packet, new->packet));
 
 181         curpacket = new->sigs;
 
 182         while (curpacket != NULL) {
 
 183                 nextpacket = curpacket->next;
 
 185                  * TODO: We should be checking the signature and then
 
 186                  * potentially merging/replacing it depending on the subpackets
 
 187                  * really. For now this stops us adding the same one twice
 
 190                 if (find_signature(old->sigs, curpacket->packet)) {
 
 192                          * We already have this sig, remove it from the
 
 193                          * difference list and free the memory allocated for
 
 196                         if (lastpacket != NULL) {
 
 197                                 lastpacket->next = curpacket->next;
 
 199                                 assert(curpacket == new->sigs);
 
 200                                 new->sigs = curpacket->next;
 
 202                         curpacket->next = NULL;
 
 203                         free_packet_list(curpacket);
 
 205                         lastpacket = curpacket;
 
 207                 curpacket = nextpacket;
 
 209         new->last_sig = lastpacket;
 
 212          * What's left on new->sigs now are the new signatures, so add them to
 
 215         packet_list_add(&old->sigs, &old->last_sig, new->sigs);
 
 221  *      merge_signed_packets - Takes 2 lists of signed packets and merges them.
 
 222  *      @old: The old signed packet list.
 
 223  *      @new: The new signed packet list.
 
 225  *      Takes 2 lists of signed packets and merges them. The complete list of
 
 226  *      signed packets & sigs is returned in old and the minimal set of
 
 227  *      differences required to get from old to new in new.
 
 229 int merge_signed_packets(struct openpgp_signedpacket_list **old,
 
 230                         struct openpgp_signedpacket_list **old_end,
 
 231                         struct openpgp_signedpacket_list **new,
 
 232                         struct openpgp_signedpacket_list **new_end)
 
 234         struct openpgp_signedpacket_list *curelem = NULL;
 
 235         struct openpgp_signedpacket_list *newelem = NULL;
 
 237         for (curelem = *old; curelem != NULL; curelem = curelem->next) {
 
 238                 newelem = find_signed_packet(*new, curelem->packet);
 
 239                 if (newelem != NULL) {
 
 240                         merge_packet_sigs(curelem, newelem);
 
 243                          * If there are no sigs left on the new signed packet
 
 244                          * then remove it from the list.
 
 246                         if (newelem->sigs == NULL) {
 
 247                                 remove_signed_packet(new,
 
 255          * If *new != NULL now then there might be UIDs on the new key that
 
 256          * weren't on the old key. Walk through them, checking if the UID is
 
 257          * on the old key and if not adding them to it.
 
 259         for (curelem = *new; curelem != NULL;
 
 260                         curelem = curelem->next) {
 
 262                 if (find_signed_packet(*old, curelem->packet) == NULL) {
 
 263                         ADD_PACKET_TO_LIST((*old_end),
 
 264                                 packet_dup(curelem->packet));
 
 268                         packet_list_add(&(*old_end)->sigs,
 
 269                                 &(*old_end)->last_sig,
 
 278  *      merge_keys - Takes 2 public keys and merges them.
 
 279  *      @a: The old key. The merged key is returned in this structure.
 
 280  *      @b: The new key. The changed from old to new keys are returned in this
 
 283  *      This function takes 2 keys and merges them. It then returns the merged
 
 284  *      key in a and the difference between this new key and the original a
 
 285  *      in b (ie newb contains the minimum amount of detail necessary to
 
 286  *      convert olda to newa). The intention is that olda is provided from
 
 287  *      internal storage and oldb from the remote user. newa is then stored in
 
 288  *      internal storage and newb is sent to all our keysync peers.
 
 290 int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
 
 292         int rc = 0; /* Return code */
 
 293         struct openpgp_packet_list      *curpacket = NULL; 
 
 294         struct openpgp_packet_list      *lastpacket = NULL;
 
 295         struct openpgp_packet_list      *nextpacket = NULL;
 
 297         if (a == NULL || b == NULL) {
 
 302         } else if (get_keyid(a) != get_keyid(b)) {
 
 304                  * Key IDs are different.
 
 309                  * Key IDs are the same, so I guess we have to merge them.
 
 311                 curpacket = b->revocations;
 
 312                 while (curpacket != NULL) {
 
 313                         nextpacket = curpacket->next;
 
 314                         if (find_packet(a->revocations, curpacket->packet)) {
 
 316                                  * We already have this revocation, remove it
 
 317                                  * from the difference list and free the memory
 
 321                                 if (lastpacket != NULL) {
 
 322                                         lastpacket->next = curpacket->next;
 
 324                                         assert(curpacket == b->revocations);
 
 325                                         b->revocations = curpacket->next;
 
 327                                 curpacket->next = NULL;
 
 328                                 free_packet_list(curpacket);
 
 331                                 lastpacket = curpacket;
 
 333                         curpacket = nextpacket;
 
 335                 b->last_revocation = lastpacket;
 
 338                  * Anything left on b->revocations doesn't exist on
 
 339                  * a->revocations, so add them to the list.
 
 341                 packet_list_add(&a->revocations,
 
 346                  * Merge uids (signed list).
 
 347                  * Merge subkeys (signed list).
 
 349                 merge_signed_packets(&a->uids, &a->last_uid, 
 
 350                                 &b->uids, &b->last_uid);
 
 351                 merge_signed_packets(&a->subkeys, &a->last_subkey,
 
 352                                 &b->subkeys, &b->last_subkey);
 
 360  *      update_keys - Takes a list of public keys and updates them in the DB.
 
 361  *      @keys: The keys to update in the DB.
 
 363  *      Takes a list of keys and adds them to the database, merging them with
 
 364  *      the key in the database if it's already present there. The key list is
 
 365  *      update to contain the minimum set of updates required to get from what
 
 366  *      we had before to what we have now (ie the set of data that was added to
 
 367  *      the DB). Returns the number of entirely new keys added.
 
 369 int update_keys(struct openpgp_publickey **keys)
 
 371         struct openpgp_publickey *curkey = NULL;
 
 372         struct openpgp_publickey *oldkey = NULL;
 
 373         struct openpgp_publickey *prev = NULL;
 
 377         for (curkey = *keys; curkey != NULL; curkey = curkey->next) {
 
 378                 intrans = starttrans();
 
 379                 logthing(LOGTHING_INFO,
 
 380                         "Fetching key 0x%llX, result: %d",
 
 382                         fetch_key(get_keyid(curkey), &oldkey, intrans));
 
 385                  * If we already have the key stored in the DB then merge it
 
 386                  * with the new one that's been supplied. Otherwise the key
 
 387                  * we've just got is the one that goes in the DB and also the
 
 388                  * one that we send out.
 
 390                 if (oldkey != NULL) {
 
 391                         merge_keys(oldkey, curkey);
 
 392                         if (curkey->revocations == NULL &&
 
 393                                         curkey->uids == NULL &&
 
 394                                         curkey->subkeys == NULL) {
 
 396                                         *keys = curkey->next;
 
 398                                         prev->next = curkey->next;
 
 400                                         free_publickey(curkey);
 
 405                                 logthing(LOGTHING_INFO,
 
 406                                         "Merged key; storing updated key.");
 
 407                                 store_key(oldkey, intrans, true);
 
 409                         free_publickey(oldkey);
 
 412                         logthing(LOGTHING_INFO,
 
 413                                 "Storing completely new key.");
 
 414                         store_key(curkey, intrans, false);