(************************************************************************) (* This file is part of SKS. SKS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (***********************************************************************) (** Logic for merging PGP keys with the same public key *) open StdLabels open MoreLabels open Printf open Common open Packet module Set = PSet.Set module Map = PMap.Map exception Unparseable_packet_sequence (** This is my understanding of the grammar of allowable public keys: {[ ATOMS = v3_pubkey v4_pubkey signature pubsubkey uid e]} The above correspond to packet types, except for e, which corresponds to the empty string. Here's the grammar: {[ KEY := V3 | V4 V3 := v3_pubkey SIGLIST UIDLIST V4 := v4_pubkey SIGLIST UIDLIST SUBKEYLIST SIGLIST := e | signature SIGLIST UIDLIST := e | UID UIDLIST UID := uid SIGLIST | uid SUBKEYLIST := e | SUBKEY SUBKEYLIST SUBKEY := subkey SIGLIST ]} (shouldn't the last one be: {[SUBKEY := subkey signature SIGLIST]} since there must be at least one signature?) My only purpose in doing this parsing is to allow for the proper merging of two public keys. To merge two keys, I join the SIGLISTs and UIDLISTs and SUBKEYLISTs. + Merging SIGLISTs is straightforward: just concatentate the lists and drop duplicates. + Merging UIDLISTs and SUBKEYLISTs is somewhat more complicated. I join siglists corresponding to the same UID. The current implementation explicitly distinguishes between v3 and v4 keys, which really it doesn't need to do as it presently stands. But if a fuller handler of revocation becomes necessary, then distinguishing between the two may be necessary. There is no special handling of revocations --- I don't check if they're valid, and multiple revocations can pop up. *) (*******************************************************************) (* Types for representing the structure of a key *) type sigpair = packet * packet list type pkey = { key : packet; selfsigs: packet list; (* revocations only in v3 keys *) uids: sigpair list; subkeys: sigpair list; } let packets_equal p1 p2 = p1 = p2 (*******************************************************************) (** Code for flattening out the above structure back to the original key *) let rec flatten_sigpair_list list = match list with [] -> [] | (pack,sigs)::tl -> pack :: (sigs @ flatten_sigpair_list tl) let flatten key = key.key :: List.concat [ key.selfsigs; flatten_sigpair_list key.uids; flatten_sigpair_list key.subkeys ] (************************************************************) let print_pkey key = printf "%d selfsigs, %d uids, %d subkeys\n" (List.length key.selfsigs) (List.length key.uids) (List.length key.subkeys) (*******************************************************************) let get_version packet = match packet.packet_type with Public_Key_Packet -> int_of_char packet.packet_body.[0] | Signature_Packet -> int_of_char packet.packet_body.[0] | _ -> raise Not_found let key_to_stream key = let ptype_list = List.map ~f:(fun pack -> (pack.packet_type,pack)) key in Stream.of_list ptype_list (*******************************************************************) (*** Key Parsing ***************************************************) (*******************************************************************) let rec parse_keystr = parser | [< '(Public_Key_Packet,p) ; s >] -> match get_version p with | 4 -> (match s with parser [< selfsigs = siglist; uids = uidlist; subkeys = subkeylist; >] -> { key = p; selfsigs = selfsigs; uids = uids; subkeys = subkeys; }) | 2 | 3 -> (match s with parser [< revocations = siglist; uids = uidlist; >] -> { key = p ; selfsigs = revocations; uids = uids; subkeys = []; }) | _ -> failwith "Unexpected key packet version number" and siglist = parser | [< '(Signature_Packet,p); tl = siglist >] -> p::tl | [< >] -> [] and uidlist = parser | [< '(User_ID_Packet,p); sigs = siglist; tl = uidlist >] -> (p,sigs)::tl | [< '(User_Attribute_Packet,p); sigs = siglist; tl = uidlist >] -> (p,sigs)::tl (* (p,sigs)::(match s with parser | [< '(User_ID_Packet,p); sigs = siglist; tl = uidlist >] -> (p,sigs)::tl | [< >] -> []) *) | [< >] -> [] and subkeylist = parser | [< '(Public_Subkey_Packet,p); sigs = siglist; tl = subkeylist >] -> (p,sigs)::tl | [< >] -> [] (*******************************************************************) (*** Key Merging Code *********************************************) (*******************************************************************) let set_of_list list = List.fold_left ~init:Set.empty list ~f:(fun set x -> Set.add x set) let merge_sigpairs pairs = let map = List.fold_left pairs ~f:(fun map (pack,sigs) -> try let old_sigs = Map.find pack map in (* If front packet is already there, add in new sigs, discarding duplicates *) Map.add ~key:pack ~data:(Utils.dedup (old_sigs @ sigs)) map with (* otherwise, add in data by itself *) Not_found -> Map.add ~key:pack ~data:sigs map) ~init:Map.empty in Map.fold ~f:(fun ~key:pack ~data:sigs list -> (pack,sigs)::list) map ~init:[] let merge_sigpair_lists l1 l2 = merge_sigpairs (l1 @ l2) (*******************************************************************) let merge_pkeys key1 key2 = if not (packets_equal key1.key key2.key) then None (* merge can only work if keys are the same *) else Some { key = key1.key; selfsigs = Utils.dedup (key1.selfsigs @ key2.selfsigs); (* this might be wrong. Must the revocations be separated out to go before the other self signatures? *) uids = merge_sigpair_lists key1.uids key2.uids; subkeys = merge_sigpair_lists key1.subkeys key2.subkeys; } (*******************************************************************) (*******************************************************************) (*******************************************************************) let key_to_pkey key = try let keystream = key_to_stream key in let pkey = parse_keystr keystream in Stream.empty keystream; pkey with Stream.Failure | Stream.Error _ -> raise Unparseable_packet_sequence let merge key1 key2 = try let pkey1 = key_to_pkey key1 and pkey2 = key_to_pkey key2 in let mkey = merge_pkeys pkey1 pkey2 in apply_opt ~f:flatten mkey with Unparseable_packet_sequence -> None let dedup_sigpairs pairs = let map = List.fold_left pairs ~init:Map.empty ~f:(fun map (pack,sigs) -> try let old_sigs = Map.find pack map in Map.add ~key:pack ~data:(Utils.dedup (sigs @ old_sigs)) map with Not_found -> Map.add ~key:pack ~data:sigs map ) in Map.to_alist map let dedup_pkey pkey = { pkey with selfsigs = Utils.dedup pkey.selfsigs; uids = dedup_sigpairs pkey.uids; subkeys = dedup_sigpairs pkey.subkeys; } let dedup_key key = flatten (dedup_pkey (key_to_pkey key)) let parseable key = try ignore (key_to_pkey key); true with Unparseable_packet_sequence -> false