summaryrefslogtreecommitdiff
path: root/KeyRing.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2014-04-12 17:19:46 -0400
committerjoe <joe@jerkface.net>2014-04-12 17:19:46 -0400
commita8da6f5843f3a5e6f7c975a746dea27adcf3907e (patch)
treee23abc66e741c336b9c70aca08f34af9bac4309e /KeyRing.hs
parent23dfd840a059877af0ff2538b2d46d85a0842ed9 (diff)
moved merge function into KeyRing module
Diffstat (limited to 'KeyRing.hs')
-rw-r--r--KeyRing.hs218
1 files changed, 215 insertions, 3 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