2 * merge.c - Routines to merge OpenPGP public keys.
4 * Jonathan McDowell <noodles@earth.li>
6 * Copyright 2002 Project Purple
14 #include "keystructs.h"
20 * compare_packets - Check to see if 2 OpenPGP packets are the same.
21 * @a: The first key to compare.
22 * @b: The second key to compare.
24 * Takes 2 keys and returns true if they are the same and false otherwise.
26 bool compare_packets(struct openpgp_packet *a, struct openpgp_packet *b)
28 return (a->tag == b->tag && a->length == b->length &&
29 !memcmp(a->data, b->data, b->length));
33 * find_packet - Checks to see if an OpenPGP packet exists in a list.
34 * @packet_list: The list of packets to look in.
35 * @packet: The packet to look for.
37 * Walks through the packet_list checking to see if the packet given is
38 * present in it. Returns true if it is.
40 bool find_packet(struct openpgp_packet_list *packet_list,
41 struct openpgp_packet *packet)
45 while (!found && packet_list != NULL) {
46 if (compare_packets(packet_list->packet, packet)) {
49 packet_list = packet_list -> next;
56 * get_signed_packet - Gets a signed packet from a list.
57 * @packet_list: The list of packets to look in.
58 * @packet: The packet to look for.
60 * Walks through the signedpacket_list looking for the supplied packet and
61 * returns it if found. Otherwise returns NULL.
63 struct openpgp_signedpacket_list *find_signed_packet(
64 struct openpgp_signedpacket_list *packet_list,
65 struct openpgp_packet *packet)
67 struct openpgp_signedpacket_list *found = NULL;
69 while (found == NULL && packet_list != NULL) {
70 if (compare_packets(packet_list->packet, packet)) {
73 packet_list = packet_list -> next;
80 * remove_signed_packet - Removes a signed packet from a list.
81 * @packet_list: The list of packets to look in.
82 * @packet: The packet to remove.
84 * Walks through the signedpacket_list looking for the supplied packet and
85 * removes it if found. Assumes the packet can only exist a maximum of
88 bool remove_signed_packet(struct openpgp_signedpacket_list **packet_list,
89 struct openpgp_signedpacket_list **list_end,
90 struct openpgp_packet *packet)
92 struct openpgp_signedpacket_list *cur = NULL;
93 struct openpgp_signedpacket_list *prev = NULL;
96 for (cur = *packet_list; !found && (cur != NULL); cur = cur->next) {
97 if (compare_packets(cur->packet, packet)) {
100 *packet_list = cur->next;
102 prev->next = cur->next;
104 if (cur->next == NULL) {
114 * merge_packet_sigs - Takes 2 signed packets and merges their sigs.
115 * @old: The old signed packet.
116 * @new: The new signed packet.
118 * Takes 2 signed packet list structures and the sigs of the packets on
119 * the head of these structures. These packets must both be the same and
120 * the fully merged structure is returned in old and the minimal
121 * difference to get from old to new in new.
123 int merge_packet_sigs(struct openpgp_signedpacket_list *old,
124 struct openpgp_signedpacket_list *new)
126 struct openpgp_packet_list *lastpacket = NULL;
127 struct openpgp_packet_list *curpacket = NULL;
128 struct openpgp_packet_list *nextpacket = NULL;
130 assert(compare_packets(old->packet, new->packet));
132 curpacket = new->sigs;
133 while (curpacket != NULL) {
134 nextpacket = curpacket->next;
135 if (find_packet(old->sigs, curpacket->packet)) {
137 * We already have this sig, remove it from the
138 * difference list and free the memory allocated for
141 if (lastpacket != NULL) {
142 lastpacket->next = curpacket->next;
144 assert(curpacket == new->sigs);
145 new->sigs = curpacket->next;
147 curpacket->next = NULL;
148 free_packet_list(curpacket);
150 lastpacket = curpacket;
152 curpacket = nextpacket;
154 new->last_sig = lastpacket;
157 * What's left on new->sigs now are the new signatures, so add them to
160 packet_list_add(&old->sigs, &old->last_sig, new->sigs);
166 * merge_signed_packets - Takes 2 lists of signed packets and merges them.
167 * @old: The old signed packet list.
168 * @new: The new signed packet list.
170 * Takes 2 lists of signed packets and merges them. The complete list of
171 * signed packets & sigs is returned in old and the minimal set of
172 * differences required to get from old to new in new.
174 int merge_signed_packets(struct openpgp_signedpacket_list **old,
175 struct openpgp_signedpacket_list **old_end,
176 struct openpgp_signedpacket_list **new,
177 struct openpgp_signedpacket_list **new_end)
179 struct openpgp_signedpacket_list *curelem = NULL;
180 struct openpgp_signedpacket_list *newelem = NULL;
182 for (curelem = *old; curelem != NULL; curelem = curelem->next) {
183 newelem = find_signed_packet(*new, curelem->packet);
184 if (newelem != NULL) {
185 merge_packet_sigs(curelem, newelem);
188 * If there are no sigs left on the new signed packet
189 * then remove it from the list.
191 if (newelem->sigs == NULL) {
192 remove_signed_packet(new,
200 * If *new != NULL now then there are UIDs on the new key that weren't
201 * on the old key. Add them.
203 for (curelem = *new; curelem != NULL;
204 curelem = curelem->next) {
205 ADD_PACKET_TO_LIST((*old_end),
206 packet_dup(curelem->packet));
210 packet_list_add(&(*old_end)->sigs,
211 &(*old_end)->last_sig,
219 * merge_keys - Takes 2 public keys and merges them.
220 * @a: The old key. The merged key is returned in this structure.
221 * @b: The new key. The changed from old to new keys are returned in this
224 * This function takes 2 keys and merges them. It then returns the merged
225 * key in a and the difference between this new key and the original a
226 * in b (ie newb contains the minimum amount of detail necessary to
227 * convert olda to newa). The intention is that olda is provided from
228 * internal storage and oldb from the remote user. newa is then stored in
229 * internal storage and newb is sent to all our keysync peers.
231 int merge_keys(struct openpgp_publickey *a, struct openpgp_publickey *b)
233 int rc = 0; /* Return code */
234 struct openpgp_packet_list *curpacket = NULL;
235 struct openpgp_packet_list *lastpacket = NULL;
236 struct openpgp_packet_list *nextpacket = NULL;
238 if (a == NULL || b == NULL) {
243 } else if (get_keyid(a) != get_keyid(b)) {
245 * Key IDs are different.
250 * Key IDs are the same, so I guess we have to merge them.
252 curpacket = b->revocations;
253 while (curpacket != NULL) {
254 nextpacket = curpacket->next;
255 if (find_packet(a->revocations, curpacket->packet)) {
257 * We already have this revocation, remove it
258 * from the difference list and free the memory
262 if (lastpacket != NULL) {
263 lastpacket->next = curpacket->next;
265 assert(curpacket == b->revocations);
266 b->revocations = curpacket->next;
268 curpacket->next = NULL;
269 free_packet_list(curpacket);
272 lastpacket = curpacket;
274 curpacket = nextpacket;
276 b->last_revocation = lastpacket;
279 * Anything left on b->revocations doesn't exist on
280 * a->revocations, so add them to the list.
282 packet_list_add(&a->revocations,
287 * Merge uids (signed list).
288 * Merge subkeys (signed list).
290 merge_signed_packets(&a->uids, &a->last_uid,
291 &b->uids, &b->last_uid);
292 merge_signed_packets(&a->subkeys, &a->last_uid,
293 &b->subkeys, &b->last_subkey);