diff options
author | joe <joe@jerkface.net> | 2013-11-09 18:58:23 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2013-11-09 18:58:23 -0500 |
commit | 1a61b1e32ba8931e7919642376198f813c962a77 (patch) | |
tree | 9cbadf253833ac221620971c5dfc6fae7674f480 | |
parent | fddfa5141f9926afc23e3ac971a4ef7224720a07 (diff) |
Merging functionality (mergesecrets command)
-rw-r--r-- | kiki.hs | 190 |
1 files changed, 189 insertions, 1 deletions
@@ -44,7 +44,7 @@ import System.Exit | |||
44 | import ControlMaybe | 44 | import ControlMaybe |
45 | import Data.Char | 45 | import Data.Char |
46 | import Control.Arrow (second) | 46 | import Control.Arrow (second) |
47 | import Data.Traversable | 47 | import Data.Traversable hiding (mapM) |
48 | import System.Console.CmdArgs | 48 | import System.Console.CmdArgs |
49 | -- import System.Posix.Time | 49 | -- import System.Posix.Time |
50 | import Data.Time.Clock.POSIX | 50 | import Data.Time.Clock.POSIX |
@@ -52,6 +52,7 @@ import System.Posix.IO (fdToHandle,fdRead) | |||
52 | import System.Posix.Files | 52 | import System.Posix.Files |
53 | import Data.Monoid ((<>)) | 53 | import Data.Monoid ((<>)) |
54 | -- import Data.X509 | 54 | -- import Data.X509 |
55 | import qualified Data.Map as Map | ||
55 | 56 | ||
56 | unprefix c spec = if null (snd p) then swap p else (fst p, tail (snd p)) | 57 | unprefix 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 | ||
812 | scanPackets [] = [] | ||
813 | scanPackets (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 | |||
824 | type KeyKey = [Char8.ByteString] | ||
825 | data SubKey = SubKey Packet [Packet] | ||
826 | data KeyData = KeyData Packet -- main key | ||
827 | [Packet] -- sigs on main key | ||
828 | (Map.Map String [Packet]) -- uids | ||
829 | (Map.Map KeyKey SubKey) -- subkeys | ||
830 | |||
831 | type KeyDB = Map.Map KeyKey KeyData | ||
832 | |||
833 | keykey key = fingerprint_material key -- TODO: smaller key? | ||
834 | uidkey (UserIDPacket str) = str | ||
835 | |||
836 | -- Compare master keys, LT is prefered for merging | ||
837 | keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT | ||
838 | keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT | ||
839 | keycomp a b | a==b = EQ | ||
840 | keycomp 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 | ||
848 | subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT | ||
849 | subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT | ||
850 | subcomp a b | a==b = EQ | ||
851 | subcomp a b = error $ unlines ["Unable to merge subs:" | ||
852 | , fingerprint a | ||
853 | , PP.ppShow a | ||
854 | , fingerprint b | ||
855 | , PP.ppShow b | ||
856 | ] | ||
857 | |||
858 | merge :: Map.Map KeyKey KeyData -> Message -> Map.Map KeyKey KeyData | ||
859 | merge 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 | |||
931 | flattenKeys :: Map.Map KeyKey KeyData -> Message | ||
932 | flattenKeys 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 | {- | ||
943 | merge 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 | |||
805 | main = do | 966 | main = 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 |