summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2013-11-09 18:58:23 -0500
committerjoe <joe@jerkface.net>2013-11-09 18:58:23 -0500
commit1a61b1e32ba8931e7919642376198f813c962a77 (patch)
tree9cbadf253833ac221620971c5dfc6fae7674f480
parentfddfa5141f9926afc23e3ac971a4ef7224720a07 (diff)
Merging functionality (mergesecrets command)
-rw-r--r--kiki.hs190
1 files changed, 189 insertions, 1 deletions
diff --git a/kiki.hs b/kiki.hs
index 33f8428..b55510f 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -44,7 +44,7 @@ import System.Exit
44import ControlMaybe 44import ControlMaybe
45import Data.Char 45import Data.Char
46import Control.Arrow (second) 46import Control.Arrow (second)
47import Data.Traversable 47import Data.Traversable hiding (mapM)
48import System.Console.CmdArgs 48import System.Console.CmdArgs
49-- import System.Posix.Time 49-- import System.Posix.Time
50import Data.Time.Clock.POSIX 50import Data.Time.Clock.POSIX
@@ -52,6 +52,7 @@ import System.Posix.IO (fdToHandle,fdRead)
52import System.Posix.Files 52import System.Posix.Files
53import Data.Monoid ((<>)) 53import Data.Monoid ((<>))
54-- import Data.X509 54-- import Data.X509
55import qualified Data.Map as Map
55 56
56unprefix c spec = if null (snd p) then swap p else (fst p, tail (snd p)) 57unprefix c spec = if null (snd p) then swap p else (fst p, tail (snd p))
57 where p = break (==c) spec 58 where p = break (==c) spec
@@ -773,6 +774,12 @@ data Arguments =
773 , seckey :: String } 774 , seckey :: String }
774 | CatPub { homedir :: Maybe FilePath 775 | CatPub { homedir :: Maybe FilePath
775 , catpub_args :: [String] } 776 , catpub_args :: [String] }
777 | MergeSecrets
778 { homedir :: Maybe FilePath
779 , files :: [FilePath]
780 }
781 | DumpPackets { homedir :: Maybe FilePath
782 , marshal_test :: String }
776 {- 783 {-
777 | Decrypt { homedir :: Maybe FilePath 784 | Decrypt { homedir :: Maybe FilePath
778 , passphrase_fd :: Maybe Int 785 , passphrase_fd :: Maybe Int
@@ -802,6 +809,160 @@ is40digitHex xs = ys == xs && length ys==40
802 | 'a' <= c && c <= 'f' = True 809 | 'a' <= c && c <= 'f' = True
803 ishex c = False 810 ishex c = False
804 811
812scanPackets [] = []
813scanPackets (p:ps) = scanl doit (doit (MarkerPacket,MarkerPacket,MarkerPacket) p) ps
814 where
815 doit (top,sub,_) p =
816 case p of
817 _ | isKey p && not (is_subkey p) -> (p,MarkerPacket,p)
818 _ | isKey p && is_subkey p -> (top,p,p)
819 _ | isUserID p -> (top,p,p)
820 _ | otherwise -> (top,sub,p)
821
822
823
824type KeyKey = [Char8.ByteString]
825data SubKey = SubKey Packet [Packet]
826data KeyData = KeyData Packet -- main key
827 [Packet] -- sigs on main key
828 (Map.Map String [Packet]) -- uids
829 (Map.Map KeyKey SubKey) -- subkeys
830
831type KeyDB = Map.Map KeyKey KeyData
832
833keykey key = fingerprint_material key -- TODO: smaller key?
834uidkey (UserIDPacket str) = str
835
836-- Compare master keys, LT is prefered for merging
837keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
838keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
839keycomp a b | a==b = EQ
840keycomp a b = error $ unlines ["Unable to merge keys:"
841 , fingerprint a
842 , PP.ppShow a
843 , fingerprint b
844 , PP.ppShow b
845 ]
846
847-- Compare subkeys, LT is prefered for merging
848subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
849subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
850subcomp a b | a==b = EQ
851subcomp a b = error $ unlines ["Unable to merge subs:"
852 , fingerprint a
853 , PP.ppShow a
854 , fingerprint b
855 , PP.ppShow b
856 ]
857
858merge :: Map.Map KeyKey KeyData -> Message -> Map.Map KeyKey KeyData
859merge db (Message ps) = foldl mergeit db qs
860 where
861 qs = scanPackets ps
862 mergeit db (_,_,TrustPacket {}) = db -- Filter TrustPackets
863 mergeit db (top,sub,p) | isKey top = Map.alter update (keykey top) db
864 where
865 update v | isKey p && not (is_subkey p)
866 = case v of
867 Nothing -> Just $ KeyData p [] Map.empty Map.empty
868 Just (KeyData key sigs uids subkeys) | keykey key == keykey p
869 -> Just $ KeyData (minimumBy keycomp [key,p]) sigs uids subkeys
870 _ -> error . concat $ ["Unexpected master key merge error: "
871 ,show (fingerprint top, fingerprint p)]
872 update (Just (KeyData key sigs uids subkeys)) | isKey p && is_subkey p
873 = Just $ KeyData key sigs uids (Map.alter (mergeSubkey p) (keykey p) subkeys)
874 update (Just (KeyData key sigs uids subkeys)) | isUserID p
875 = Just $ KeyData key sigs (Map.alter (mergeUid p) (uidkey p) uids) subkeys
876 update (Just (KeyData key sigs uids subkeys))
877 = case sub of
878 MarkerPacket -> Just $ KeyData key (mergeSig p sigs) uids subkeys
879 UserIDPacket {} -> Just $ KeyData key
880 sigs
881 (Map.alter (mergeUidSig p) (uidkey sub) uids)
882 subkeys
883 _ | isKey sub -> Just $ KeyData key
884 sigs
885 uids
886 (Map.alter (mergeSubSig p) (keykey sub) subkeys)
887 _ -> error $ "Unexpected PGP packet 1: "++(words (show p) >>= take 1)
888 update _ = error $ "Unexpected PGP packet 2: "++(words (show p) >>= take 1)
889
890 mergeit _ (_,_,p) = error $ "Unexpected PGP packet 3: "++whatP p
891
892 mergeSubkey p Nothing = Just $ SubKey p []
893 mergeSubkey p (Just (SubKey key sigs)) = Just $
894 SubKey (minimumBy subcomp [key,p]) sigs
895
896 mergeUid (UserIDPacket s) Nothing = Just []
897 mergeUid (UserIDPacket s) (Just sigs) = Just sigs
898 mergeUid p _ = error $ "Unable to merge into UID record: " ++whatP p
899
900 whatP = concat . take 1 . words . show
901
902
903 mergeSig sig sigs =
904 let (xs,ys) = break (isSameSig sig) sigs
905 in if null ys
906 then sigs++[sig]
907 else let y:ys'=ys
908 in xs ++ (mergeSameSig sig y : ys')
909
910
911 isSameSig a b | isSignaturePacket a && isSignaturePacket b =
912 a { unhashed_subpackets=[] } == b { unhashed_subpackets = [] }
913 isSameSig a b = a==b
914
915 mergeSameSig a b | isSignaturePacket a && isSignaturePacket b =
916 b { unhashed_subpackets =
917 foldl mergeItem (unhashed_subpackets b) (unhashed_subpackets a)
918 }
919 where
920 mergeItem ys x = if x `elem` ys then ys else ys++[x]
921
922 mergeSameSig a b = trace ("discarding dup "++show a) b
923
924 mergeUidSig sig (Just sigs) = Just $ mergeSig sig sigs
925 mergeUidSig sig Nothing = Just [sig]
926
927 mergeSubSig sig (Just (SubKey key sigs)) = Just $ SubKey key (mergeSig sig sigs)
928 mergeSubSig sig Nothing = error $
929 "Unable to merge subkey signature: "++(words (show sig) >>= take 1)
930
931flattenKeys :: Map.Map KeyKey KeyData -> Message
932flattenKeys db = Message $ concatMap flattenTop (Map.assocs db)
933 where
934 flattenTop (_,(KeyData key sigs uids subkeys)) =
935 key : ( concatMap flattenUid (Map.assocs uids)
936 ++ concatMap flattenSub (Map.assocs subkeys))
937
938 flattenUid (str,sigs) = UserIDPacket str : sigs
939
940 flattenSub (_,SubKey key sigs) = key:sigs
941
942{-
943merge db (Message ps) = scanl mergeit db qs
944 where
945 qs = scanPackets ps
946 mergeit db (top,sub,p) = todo
947 where
948 k = keykey top
949 v = maybe (merge1 (newrec top)) (merge2 sub p) $ Map.lookup k db
950
951 keykey key = fingerprint_material key -- TODO: smaller key?
952
953 newrec key | isKey key && not (is_subkey key) =
954 KeyData key [] Map.empty Map.empty
955 newrec _ = error "Unexpected packet."
956
957 merge1 rec db = Map.insert k rec db
958
959 merge2 sub p (KeyData mk sigs uids subkeys)
960 | isKey sub =
961 maybe todo todo $ Map.lookup skey subkeys
962 where
963 skey = keykey sub
964-}
965
805main = do 966main = do
806 args <- cmdArgs $ modes 967 args <- cmdArgs $ modes
807 [ List HOMEOPTION 968 [ List HOMEOPTION
@@ -838,6 +999,12 @@ main = do
838 , CatPub HOMEOPTION 999 , CatPub HOMEOPTION
839 (def &= args &= typ "KEYSPEC FILES") 1000 (def &= args &= typ "KEYSPEC FILES")
840 &= help "Extract a public subkey to stdout." 1001 &= help "Extract a public subkey to stdout."
1002 , MergeSecrets HOMEOPTION
1003 (def &= args &= typFile)
1004 &= help "Merge multiple keyrings to stdout."
1005 , DumpPackets HOMEOPTION
1006 (def &= opt ("n" ::String))
1007 &= help "Output secret ring packets in ascii format for debugging."
841 , Add HOMEOPTION 1008 , Add HOMEOPTION
842 (def &= opt ("passphrase"::String) 1009 (def &= opt ("passphrase"::String)
843 &= typ "FD" 1010 &= typ "FD"
@@ -1083,6 +1250,27 @@ main = do
1083 -} 1250 -}
1084 -} 1251 -}
1085 1252
1253 doCmd cmd@(DumpPackets {}) = do
1254 ( homedir -- e3ozbhvej4jvlu43.onion/gpg/gnupghome
1255 , sec -- e3ozbhvej4jvlu43.onion/gpg/gnupghome/secring.gpg
1256 , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321"
1257 ) <- getPGPEnviron cmd
1258 if map toLower (marshal_test cmd) `elem` ["y","yes"]
1259 then L.putStr $ encode (Message sec)
1260 else putStrLn $ PP.ppShow sec
1261
1262 doCmd cmd@(MergeSecrets {}) = do
1263 ( homedir -- e3ozbhvej4jvlu43.onion/gpg/gnupghome
1264 , sec -- e3ozbhvej4jvlu43.onion/gpg/gnupghome/secring.gpg
1265 , grip -- Just "AD1CA892FCF4ED9829C762269BDEA5B4D5643321"
1266 ) <- getPGPEnviron cmd
1267 let db = merge Map.empty (Message sec)
1268 ms <- mapM readPacketsFromFile (files cmd)
1269 let db' = foldl' merge db ms
1270 m = flattenKeys db'
1271 L.putStr (encode m)
1272 return ()
1273
1086 doCmd cmd@(CatPub {}) = do 1274 doCmd cmd@(CatPub {}) = do
1087 let spec:files = catpub_args cmd 1275 let spec:files = catpub_args cmd
1088 let (topspec,subspec) = unprefix '/' spec 1276 let (topspec,subspec) = unprefix '/' spec