summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--KeyRing.hs218
-rw-r--r--kiki.hs205
2 files changed, 215 insertions, 208 deletions
diff --git a/KeyRing.hs b/KeyRing.hs
index 7073e43..39bff4c 100644
--- a/KeyRing.hs
+++ b/KeyRing.hs
@@ -8,9 +8,14 @@ import Control.Monad
8import Data.Maybe 8import Data.Maybe
9import Data.Char 9import Data.Char
10import Data.List 10import Data.List
11import Control.Applicative ( (<$>) ) 11import Data.OpenPGP
12import System.Directory ( getHomeDirectory, doesFileExist ) 12import Control.Applicative ( (<$>) )
13import Control.Arrow ( first, second ) 13import System.Directory ( getHomeDirectory, doesFileExist )
14import Control.Arrow ( first, second )
15import Data.OpenPGP.Util ( fingerprint )
16import Data.ByteString.Lazy ( ByteString )
17import Text.Show.Pretty as PP ( ppShow )
18import qualified Data.Map as Map
14 19
15import DotLock 20import DotLock
16 21
@@ -146,3 +151,210 @@ getHomeDir protohome = do
146lookupEnv var = 151lookupEnv var =
147 handleIO_ (return Nothing) $ fmap Just (getEnv var) 152 handleIO_ (return Nothing) $ fmap Just (getEnv var)
148#endif 153#endif
154
155isKey (PublicKeyPacket {}) = True
156isKey (SecretKeyPacket {}) = True
157isKey _ = False
158
159isUserID (UserIDPacket {}) = True
160isUserID _ = False
161
162isTrust (TrustPacket {}) = True
163isTrust _ = False
164
165
166data OriginFlags = OriginFlags {
167 originallyPublic :: Bool,
168 originalNum :: Int
169 }
170 deriving Show
171type OriginMap = Map.Map FilePath OriginFlags
172data MappedPacket = MappedPacket
173 { packet :: Packet
174 , usage_tag :: Maybe String
175 , locations :: OriginMap
176 }
177
178type TrustMap = Map.Map FilePath Packet
179type SigAndTrust = ( MappedPacket
180 , TrustMap ) -- trust packets
181
182type KeyKey = [ByteString]
183data SubKey = SubKey MappedPacket [SigAndTrust]
184data KeyData = KeyData MappedPacket -- main key
185 [SigAndTrust] -- sigs on main key
186 (Map.Map String ([SigAndTrust],OriginMap)) -- uids
187 (Map.Map KeyKey SubKey) -- subkeys
188
189type KeyDB = Map.Map KeyKey KeyData
190
191origin :: Packet -> Int -> OriginFlags
192origin p n = OriginFlags ispub n
193 where
194 ispub = case p of
195 SecretKeyPacket {} -> False
196 _ -> True
197
198mappedPacket filename p = MappedPacket
199 { packet = p
200 , usage_tag = Nothing
201 , locations = Map.singleton filename (origin p (-1))
202 }
203
204keykey key =
205 -- Note: The key's timestamp is included in it's fingerprint.
206 -- Therefore, the same key with a different timestamp is
207 -- considered distinct using this keykey implementation.
208 fingerprint_material (key {timestamp=0}) -- TODO: smaller key?
209
210uidkey (UserIDPacket str) = str
211
212merge :: KeyDB -> FilePath -> Message -> KeyDB
213merge db filename (Message ps) = merge_ db filename qs
214 where
215 qs = scanPackets filename ps
216
217 scanPackets :: FilePath -> [Packet] -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
218 scanPackets filename [] = []
219 scanPackets filename (p:ps) = scanl doit (doit (MarkerPacket,MarkerPacket,ret MarkerPacket) p) ps
220 where
221 ret p = (p,Map.empty)
222 doit (top,sub,prev) p =
223 case p of
224 _ | isKey p && not (is_subkey p) -> (p,MarkerPacket,ret p)
225 _ | isKey p && is_subkey p -> (top,p,ret p)
226 _ | isUserID p -> (top,p,ret p)
227 _ | isTrust p -> (top,sub,updateTrust top sub prev p)
228 _ | otherwise -> (top,sub,ret p)
229
230 updateTrust top (PublicKeyPacket {}) (pre,t) p = (pre,Map.insert filename p t) -- public
231 updateTrust (PublicKeyPacket {}) _ (pre,t) p = (pre,Map.insert filename p t) -- public
232 updateTrust _ _ (pre,t) p = (pre,Map.insert filename p t) -- secret
233
234
235
236
237merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
238 -> KeyDB
239merge_ db filename qs = foldl mergeit db (zip [0..] qs)
240 where
241 keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
242 keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
243 keycomp a b | keykey a==keykey b = EQ
244 keycomp a b = error $ unlines ["Unable to merge keys:"
245 , fingerprint a
246 , PP.ppShow a
247 , fingerprint b
248 , PP.ppShow b
249 ]
250
251 asMapped n p = let m = mappedPacket filename p
252 in m { locations = fmap (\x->x {originalNum=n}) (locations m) }
253 asSigAndTrust n (p,tm) = (asMapped n p,tm)
254 emptyUids = Map.empty
255 -- mergeit db (_,_,TrustPacket {}) = db -- Filter TrustPackets
256 mergeit :: KeyDB -> (Int,(Packet,Packet,(Packet,Map.Map FilePath Packet))) -> KeyDB
257 mergeit db (n,(top,sub,ptt@(p,trustmap))) | isKey top = Map.alter update (keykey top) db
258 where
259 -- NOTE:
260 -- if a keyring file has both a public key packet and a secret key packet
261 -- for the same key, then only one of them will survive, which ever is
262 -- later in the file.
263 --
264 -- This is due to the use of statements like
265 -- (Map.insert filename (origin p n) (locations key))
266 --
267 update v | isKey p && not (is_subkey p)
268 = case v of
269 Nothing -> Just $ KeyData (asMapped n p) [] emptyUids Map.empty
270 Just (KeyData key sigs uids subkeys) | keykey (packet key) == keykey p
271 -> Just $ KeyData ( (asMapped n (minimumBy keycomp [packet key,p]))
272 { locations = Map.insert filename (origin p n) (locations key) } )
273 sigs
274 uids
275 subkeys
276 _ -> error . concat $ ["Unexpected master key merge error: "
277 ,show (fingerprint top, fingerprint p)]
278 update (Just (KeyData key sigs uids subkeys)) | isKey p && is_subkey p
279 = Just $ KeyData key sigs uids (Map.alter (mergeSubkey n p) (keykey p) subkeys)
280 update (Just (KeyData key sigs uids subkeys)) | isUserID p
281 = Just $ KeyData key sigs (Map.alter (mergeUid n ptt) (uidkey p) uids)
282 subkeys
283 update (Just (KeyData key sigs uids subkeys))
284 = case sub of
285 MarkerPacket -> Just $ KeyData key (mergeSig n ptt sigs) uids subkeys
286 UserIDPacket {} -> Just $ KeyData key
287 sigs
288 (Map.alter (mergeUidSig n ptt) (uidkey sub) uids)
289 subkeys
290 _ | isKey sub -> Just $ KeyData key
291 sigs
292 uids
293 (Map.alter (mergeSubSig n ptt) (keykey sub) subkeys)
294 _ -> error $ "Unexpected PGP packet 1: "++(words (show p) >>= take 1)
295 update _ = error $ "Unexpected PGP packet 2: "++(words (show p) >>= take 1)
296
297 mergeit _ (_,(_,_,p)) = error $ "Unexpected PGP packet 3: "++whatP p
298
299 mergeSubkey :: Int -> Packet -> Maybe SubKey -> Maybe SubKey
300 mergeSubkey n p Nothing = Just $ SubKey (asMapped n p) []
301 mergeSubkey n p (Just (SubKey key sigs)) = Just $
302 SubKey ((asMapped n (minimumBy subcomp [packet key,p]))
303 { locations = Map.insert filename (origin p n) (locations key) })
304 sigs
305 where
306 -- Compare master keys, LT is prefered for merging
307 -- Compare subkeys, LT is prefered for merging
308 subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
309 subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
310 subcomp a b | keykey a==keykey b = EQ
311 subcomp a b = error $ unlines ["Unable to merge subs:"
312 , fingerprint a
313 , PP.ppShow a
314 , fingerprint b
315 , PP.ppShow b
316 ]
317 subcomp_m a b = subcomp (packet a) (packet b)
318
319 mergeUid :: Int ->(Packet,a) -> Maybe ([SigAndTrust],OriginMap) -> Maybe ([SigAndTrust],OriginMap)
320 mergeUid n (UserIDPacket s,_) Nothing = Just ([],Map.singleton filename (origin MarkerPacket n))
321 mergeUid n (UserIDPacket s,_) (Just (sigs,m)) = Just (sigs, Map.insert filename (origin MarkerPacket n) m)
322 mergeUid n p _ = error $ "Unable to merge into UID record: " ++whatP p
323
324 whatP (a,_) = concat . take 1 . words . show $ a
325
326
327 mergeSig :: Int -> (Packet,TrustMap) -> [SigAndTrust] -> [SigAndTrust]
328 mergeSig n sig sigs =
329 let (xs,ys) = break (isSameSig sig) sigs
330 in if null ys
331 then sigs++[first (asMapped n) sig]
332 else let y:ys'=ys
333 in xs ++ (mergeSameSig n sig y : ys')
334
335
336 isSameSig (a,_) (MappedPacket {packet=b},_) | isSignaturePacket a && isSignaturePacket b =
337 a { unhashed_subpackets=[] } == b { unhashed_subpackets = [] }
338 isSameSig (a,_) (MappedPacket {packet=b},_) = a==b
339
340 mergeSameSig :: Int -> (Packet,TrustMap) -> (MappedPacket,TrustMap) -> (MappedPacket, TrustMap)
341 mergeSameSig n (a,ta) (m@(MappedPacket{packet=b,locations=locs}),tb) | isSignaturePacket a && isSignaturePacket b =
342 ( m { packet = (b { unhashed_subpackets =
343 foldl mergeItem (unhashed_subpackets b) (unhashed_subpackets a) })
344 , locations = Map.insert filename (origin a n) locs }
345 , tb `Map.union` ta )
346
347 where
348 -- TODO: when merging items, we should delete invalidated origins
349 -- from the orgin map.
350 mergeItem ys x = if x `elem` ys then ys else ys++[x]
351
352 mergeSameSig n a b = b -- trace ("discarding dup "++show a) b
353
354 mergeUidSig n sig (Just (sigs,m)) = Just (mergeSig n sig sigs, m)
355 mergeUidSig n sig Nothing = Just ([asSigAndTrust n sig],Map.empty)
356
357 mergeSubSig n sig (Just (SubKey key sigs)) = Just $ SubKey key (mergeSig n sig sigs)
358 mergeSubSig n sig Nothing = error $
359 "Unable to merge subkey signature: "++(words (show sig) >>= take 1)
360
diff --git a/kiki.hs b/kiki.hs
index 217f70f..47d9bdb 100644
--- a/kiki.hs
+++ b/kiki.hs
@@ -328,22 +328,12 @@ writePEM typ dta = pem
328 328
329 -- 64 byte lines 329 -- 64 byte lines
330 330
331isKey (PublicKeyPacket {}) = True
332isKey (SecretKeyPacket {}) = True
333isKey _ = False
334
335isUserID (UserIDPacket {}) = True
336isUserID _ = False
337
338isEmbeddedSignature (EmbeddedSignaturePacket {}) = True 331isEmbeddedSignature (EmbeddedSignaturePacket {}) = True
339isEmbeddedSignature _ = False 332isEmbeddedSignature _ = False
340 333
341isCertificationSig (CertificationSignature {}) = True 334isCertificationSig (CertificationSignature {}) = True
342isCertificationSig _ = True 335isCertificationSig _ = True
343 336
344isTrust (TrustPacket {}) = True
345isTrust _ = False
346
347issuer (IssuerPacket issuer) = Just issuer 337issuer (IssuerPacket issuer) = Just issuer
348issuer _ = Nothing 338issuer _ = Nothing
349backsig (EmbeddedSignaturePacket s) = Just s 339backsig (EmbeddedSignaturePacket s) = Just s
@@ -1008,203 +998,8 @@ is40digitHex xs = ys == xs && length ys==40
1008 | 'a' <= c && c <= 'f' = True 998 | 'a' <= c && c <= 'f' = True
1009 ishex c = False 999 ishex c = False
1010 1000
1011scanPackets :: FilePath -> [Packet] -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
1012scanPackets filename [] = []
1013scanPackets filename (p:ps) = scanl doit (doit (MarkerPacket,MarkerPacket,ret MarkerPacket) p) ps
1014 where
1015 ret p = (p,Map.empty)
1016 doit (top,sub,prev) p =
1017 case p of
1018 _ | isKey p && not (is_subkey p) -> (p,MarkerPacket,ret p)
1019 _ | isKey p && is_subkey p -> (top,p,ret p)
1020 _ | isUserID p -> (top,p,ret p)
1021 _ | isTrust p -> (top,sub,updateTrust top sub prev p)
1022 _ | otherwise -> (top,sub,ret p)
1023
1024 updateTrust top (PublicKeyPacket {}) (pre,t) p = (pre,Map.insert filename p t) -- public
1025 updateTrust (PublicKeyPacket {}) _ (pre,t) p = (pre,Map.insert filename p t) -- public
1026 updateTrust _ _ (pre,t) p = (pre,Map.insert filename p t) -- secret
1027
1028
1029data OriginFlags = OriginFlags {
1030 originallyPublic :: Bool,
1031 originalNum :: Int
1032 }
1033 deriving Show
1034origin :: Packet -> Int -> OriginFlags
1035origin p n = OriginFlags ispub n
1036 where
1037 ispub = case p of
1038 SecretKeyPacket {} -> False
1039 _ -> True
1040
1041type OriginMap = Map.Map FilePath OriginFlags
1042data MappedPacket = MappedPacket
1043 { packet :: Packet
1044 , usage_tag :: Maybe String
1045 , locations :: OriginMap
1046 }
1047
1048mappedPacket filename p = MappedPacket
1049 { packet = p
1050 , usage_tag = Nothing
1051 , locations = Map.singleton filename (origin p (-1))
1052 }
1053
1054type TrustMap = Map.Map FilePath Packet
1055type SigAndTrust = ( MappedPacket
1056 , TrustMap ) -- trust packets
1057
1058type KeyKey = [Char8.ByteString]
1059data SubKey = SubKey MappedPacket [SigAndTrust]
1060data KeyData = KeyData MappedPacket -- main key
1061 [SigAndTrust] -- sigs on main key
1062 (Map.Map String ([SigAndTrust],OriginMap)) -- uids
1063 (Map.Map KeyKey SubKey) -- subkeys
1064
1065type KeyDB = Map.Map KeyKey KeyData
1066
1067torhash key = maybe "" id $ derToBase32 <$> derRSA key 1001torhash key = maybe "" id $ derToBase32 <$> derRSA key
1068 1002
1069keykey key =
1070 -- Note: The key's timestamp is included in it's fingerprint.
1071 -- Therefore, the same key with a different timestamp is
1072 -- considered distinct using this keykey implementation.
1073 fingerprint_material (key {timestamp=0}) -- TODO: smaller key?
1074
1075
1076
1077uidkey (UserIDPacket str) = str
1078
1079-- Compare master keys, LT is prefered for merging
1080keycomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
1081keycomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
1082keycomp a b | keykey a==keykey b = EQ
1083keycomp a b = error $ unlines ["Unable to merge keys:"
1084 , fingerprint a
1085 , PP.ppShow a
1086 , fingerprint b
1087 , PP.ppShow b
1088 ]
1089
1090-- Compare subkeys, LT is prefered for merging
1091subcomp (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
1092subcomp (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
1093subcomp a b | keykey a==keykey b = EQ
1094subcomp a b = error $ unlines ["Unable to merge subs:"
1095 , fingerprint a
1096 , PP.ppShow a
1097 , fingerprint b
1098 , PP.ppShow b
1099 ]
1100subcomp_m a b = subcomp (packet a) (packet b)
1101
1102merge :: KeyDB -> FilePath -> Message -> KeyDB
1103merge db filename (Message ps) = merge_ db filename qs
1104 where
1105 qs = scanPackets filename ps
1106
1107merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
1108 -> KeyDB
1109merge_ db filename qs = foldl mergeit db (zip [0..] qs)
1110 where
1111 asMapped n p = let m = mappedPacket filename p
1112 in m { locations = fmap (\x->x {originalNum=n}) (locations m) }
1113 asSigAndTrust n (p,tm) = (asMapped n p,tm)
1114 emptyUids = Map.empty
1115 -- mergeit db (_,_,TrustPacket {}) = db -- Filter TrustPackets
1116 mergeit :: KeyDB -> (Int,(Packet,Packet,(Packet,Map.Map FilePath Packet))) -> KeyDB
1117 mergeit db (n,(top,sub,ptt@(p,trustmap))) | isKey top = Map.alter update (keykey top) db
1118 where
1119 -- NOTE:
1120 -- if a keyring file has both a public key packet and a secret key packet
1121 -- for the same key, then only one of them will survive, which ever is
1122 -- later in the file.
1123 --
1124 -- This is due to the use of statements like
1125 -- (Map.insert filename (origin p n) (locations key))
1126 --
1127 update v | isKey p && not (is_subkey p)
1128 = case v of
1129 Nothing -> Just $ KeyData (asMapped n p) [] emptyUids Map.empty
1130 Just (KeyData key sigs uids subkeys) | keykey (packet key) == keykey p
1131 -> Just $ KeyData ( (asMapped n (minimumBy keycomp [packet key,p]))
1132 { locations = Map.insert filename (origin p n) (locations key) } )
1133 sigs
1134 uids
1135 subkeys
1136 _ -> error . concat $ ["Unexpected master key merge error: "
1137 ,show (fingerprint top, fingerprint p)]
1138 update (Just (KeyData key sigs uids subkeys)) | isKey p && is_subkey p
1139 = Just $ KeyData key sigs uids (Map.alter (mergeSubkey n p) (keykey p) subkeys)
1140 update (Just (KeyData key sigs uids subkeys)) | isUserID p
1141 = Just $ KeyData key sigs (Map.alter (mergeUid n ptt) (uidkey p) uids)
1142 subkeys
1143 update (Just (KeyData key sigs uids subkeys))
1144 = case sub of
1145 MarkerPacket -> Just $ KeyData key (mergeSig n ptt sigs) uids subkeys
1146 UserIDPacket {} -> Just $ KeyData key
1147 sigs
1148 (Map.alter (mergeUidSig n ptt) (uidkey sub) uids)
1149 subkeys
1150 _ | isKey sub -> Just $ KeyData key
1151 sigs
1152 uids
1153 (Map.alter (mergeSubSig n ptt) (keykey sub) subkeys)
1154 _ -> error $ "Unexpected PGP packet 1: "++(words (show p) >>= take 1)
1155 update _ = error $ "Unexpected PGP packet 2: "++(words (show p) >>= take 1)
1156
1157 mergeit _ (_,(_,_,p)) = error $ "Unexpected PGP packet 3: "++whatP p
1158
1159 mergeSubkey :: Int -> Packet -> Maybe SubKey -> Maybe SubKey
1160 mergeSubkey n p Nothing = Just $ SubKey (asMapped n p) []
1161 mergeSubkey n p (Just (SubKey key sigs)) = Just $
1162 SubKey ((asMapped n (minimumBy subcomp [packet key,p]))
1163 { locations = Map.insert filename (origin p n) (locations key) })
1164 sigs
1165
1166 mergeUid :: Int ->(Packet,a) -> Maybe ([SigAndTrust],OriginMap) -> Maybe ([SigAndTrust],OriginMap)
1167 mergeUid n (UserIDPacket s,_) Nothing = Just ([],Map.singleton filename (origin MarkerPacket n))
1168 mergeUid n (UserIDPacket s,_) (Just (sigs,m)) = Just (sigs, Map.insert filename (origin MarkerPacket n) m)
1169 mergeUid n p _ = error $ "Unable to merge into UID record: " ++whatP p
1170
1171 whatP (a,_) = concat . take 1 . words . show $ a
1172
1173
1174 mergeSig :: Int -> (Packet,TrustMap) -> [SigAndTrust] -> [SigAndTrust]
1175 mergeSig n sig sigs =
1176 let (xs,ys) = break (isSameSig sig) sigs
1177 in if null ys
1178 then sigs++[first (asMapped n) sig]
1179 else let y:ys'=ys
1180 in xs ++ (mergeSameSig n sig y : ys')
1181
1182
1183 isSameSig (a,_) (MappedPacket {packet=b},_) | isSignaturePacket a && isSignaturePacket b =
1184 a { unhashed_subpackets=[] } == b { unhashed_subpackets = [] }
1185 isSameSig (a,_) (MappedPacket {packet=b},_) = a==b
1186
1187 mergeSameSig :: Int -> (Packet,TrustMap) -> (MappedPacket,TrustMap) -> (MappedPacket, TrustMap)
1188 mergeSameSig n (a,ta) (m@(MappedPacket{packet=b,locations=locs}),tb) | isSignaturePacket a && isSignaturePacket b =
1189 ( m { packet = (b { unhashed_subpackets =
1190 foldl mergeItem (unhashed_subpackets b) (unhashed_subpackets a) })
1191 , locations = Map.insert filename (origin a n) locs }
1192 , tb `Map.union` ta )
1193
1194 where
1195 -- TODO: when merging items, we should delete invalidated origins
1196 -- from the orgin map.
1197 mergeItem ys x = if x `elem` ys then ys else ys++[x]
1198
1199 mergeSameSig n a b = trace ("discarding dup "++show a) b
1200
1201 mergeUidSig n sig (Just (sigs,m)) = Just (mergeSig n sig sigs, m)
1202 mergeUidSig n sig Nothing = Just ([asSigAndTrust n sig],Map.empty)
1203
1204 mergeSubSig n sig (Just (SubKey key sigs)) = Just $ SubKey key (mergeSig n sig sigs)
1205 mergeSubSig n sig Nothing = error $
1206 "Unable to merge subkey signature: "++(words (show sig) >>= take 1)
1207
1208flattenKeys :: Bool -> KeyDB -> Message 1003flattenKeys :: Bool -> KeyDB -> Message
1209flattenKeys isPublic db = Message $ concatMap (map packet . flattenTop "" isPublic . snd) (prefilter . Map.assocs $ db) 1004flattenKeys isPublic db = Message $ concatMap (map packet . flattenTop "" isPublic . snd) (prefilter . Map.assocs $ db)
1210 where 1005 where