summaryrefslogtreecommitdiff
path: root/lib/KeyRing.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2016-08-28 16:10:41 -0400
committerjoe <joe@jerkface.net>2016-08-28 16:10:41 -0400
commit7a579e7b82a2f5707af77f4a7101ce72e57635ac (patch)
treea5ff6eac4888a5577a6ba89a76dde939af4bc038 /lib/KeyRing.hs
parent83e97b86973fc63eda92f5b38c112f0d374503c0 (diff)
Refactored for smaller modules (faster rebuild).
Diffstat (limited to 'lib/KeyRing.hs')
-rw-r--r--lib/KeyRing.hs428
1 files changed, 2 insertions, 426 deletions
diff --git a/lib/KeyRing.hs b/lib/KeyRing.hs
index a055dad..313258d 100644
--- a/lib/KeyRing.hs
+++ b/lib/KeyRing.hs
@@ -211,6 +211,8 @@ import FunctorToMaybe
211import DotLock 211import DotLock
212import ProcessUtils (systemEnv, ExitCode(ExitFailure, ExitSuccess) ) 212import ProcessUtils (systemEnv, ExitCode(ExitFailure, ExitSuccess) )
213import GnuPGAgent as Agent 213import GnuPGAgent as Agent
214import Types
215import PacketTranscoder
214 216
215-- DER-encoded elliptic curve ids 217-- DER-encoded elliptic curve ids
216-- nistp256_id = 0x2a8648ce3d030107 218-- nistp256_id = 0x2a8648ce3d030107
@@ -247,114 +249,6 @@ home = HomeDir
247 , optfile_alts = ["keys.conf","gpg.conf-2","gpg.conf"] 249 , optfile_alts = ["keys.conf","gpg.conf-2","gpg.conf"]
248 } 250 }
249 251
250data InputFile = HomeSec
251 -- ^ A file named secring.gpg located in the home directory.
252 -- See 'opHome'.
253 | HomePub
254 -- ^ A file named pubring.gpg located in the home directory.
255 -- See 'opHome'.
256 | ArgFile FilePath
257 -- ^ Contents will be read or written from the specified path.
258 | FileDesc Posix.Fd
259 -- ^ Contents will be read or written from the specified file
260 -- descriptor.
261 | Pipe Posix.Fd Posix.Fd
262 -- ^ Contents will be read from the first descriptor and updated
263 -- content will be writen to the second. Note: Don't use Pipe
264 -- for 'Wallet' files. (TODO: Wallet support)
265 | Generate Int GenerateKeyParams
266 -- ^ New key packets will be generated if there is no
267 -- matching content already in the key pool. The integer is
268 -- a unique id number so that multiple generations can be
269 -- inserted into 'opFiles'
270 deriving (Eq,Ord,Show)
271
272-- type UsageTag = String
273data Initializer = NoCreate | Internal GenerateKeyParams | External String
274 deriving (Eq,Ord,Show)
275
276data FileType = KeyRingFile
277 | PEMFile
278 | WalletFile
279 | DNSPresentation
280 | Hosts
281 deriving (Eq,Ord,Enum,Show)
282
283-- | Use this type to indicate whether a file of type 'KeyRingFile' is expected
284-- to contain secret or public PGP key packets. Note that it is not supported
285-- to mix both in the same file and that the secret key packets include all of
286-- the information contained in their corresponding public key packets.
287data Access = AutoAccess -- ^ secret or public as appropriate based on existing content.
288 -- (see 'rtRingAccess')
289 | Sec -- ^ secret information
290 | Pub -- ^ public information
291 deriving (Eq,Ord,Show)
292
293-- | Note that the documentation here is intended for when this value is
294-- assigned to 'fill'. For other usage, see 'spill'.
295data KeyFilter = KF_None -- ^ No keys will be imported.
296 | KF_Match String -- ^ Only the key that matches the spec will be imported.
297 | KF_Subkeys -- ^ Subkeys will be imported if their owner key is
298 -- already in the ring. TODO: Even if their signatures
299 -- are bad?
300 | KF_Authentic -- ^ Keys are imported if they belong to an authenticated
301 -- identity (signed or self-authenticating).
302 | KF_All -- ^ All keys will be imported.
303 deriving (Eq,Ord,Show)
304
305-- | This type describes how 'runKeyRing' will treat a file.
306data StreamInfo = StreamInfo
307 { access :: Access
308 -- ^ Indicates whether the file is allowed to contain secret information.
309 , typ :: FileType
310 -- ^ Indicates the format and content type of the file.
311 , fill :: KeyFilter
312 -- ^ This filter controls what packets will be inserted into a file.
313 , spill :: KeyFilter
314 --
315 -- ^ Use this to indicate whether or not a file's contents should be
316 -- available for updating other files. Note that although its type is
317 -- 'KeyFilter', it is usually interpretted as a boolean flag. Details
318 -- depend on 'typ' and are as follows:
319 --
320 -- 'KeyRingFile':
321 --
322 -- * 'KF_None' - The file's contents will not be shared.
323 --
324 -- * otherwise - The file's contents will be shared.
325 --
326 -- 'PEMFile':
327 --
328 -- * 'KF_None' - The file's contents will not be shared.
329 --
330 -- * 'KF_Match' - The file's key will be shared with the specified owner
331 -- key and usage tag. If 'fill' is also a 'KF_Match', then it must be
332 -- equal to this value; changing the usage or owner of a key is not
333 -- supported via the fill/spill mechanism.
334 --
335 -- * otherwise - Unspecified. Do not use.
336 --
337 -- 'WalletFile':
338 --
339 -- * The 'spill' setting is ignored and the file's contents are shared.
340 -- (TODO)
341 --
342 -- 'Hosts':
343 --
344 -- * The 'spill' setting is ignored and the file's contents are shared.
345 -- (TODO)
346 --
347 , initializer :: Initializer
348 -- ^ If 'typ' is 'PEMFile' and an 'External' 'initializer' string is set,
349 -- then it is interpretted as a shell command that may be used to create
350 -- the key if it does not exist.
351 , transforms :: [Transform]
352 -- ^ Per-file transformations that occur before the contents of a file are
353 -- spilled into the common pool.
354 }
355 deriving (Eq,Show)
356
357
358spillable :: StreamInfo -> Bool 252spillable :: StreamInfo -> Bool
359spillable (spill -> KF_None) = False 253spillable (spill -> KF_None) = False
360spillable _ = True 254spillable _ = True
@@ -387,10 +281,6 @@ usageFromFilter (KF_Match usage) = return usage
387usageFromFilter _ = mzero 281usageFromFilter _ = mzero
388 282
389 283
390type PacketTranscoder = (SymmetricAlgorithm,S2K) -> MappedPacket -> IO (KikiCondition Packet)
391
392type PacketDecrypter = MappedPacket -> IO (KikiCondition Packet)
393
394data KeyRingRuntime = KeyRingRuntime 284data KeyRingRuntime = KeyRingRuntime
395 { rtPubring :: FilePath 285 { rtPubring :: FilePath
396 -- ^ Path to the file represented by 'HomePub' 286 -- ^ Path to the file represented by 'HomePub'
@@ -418,84 +308,6 @@ data KeyRingRuntime = KeyRingRuntime
418data PacketUpdate = InducerSignature String [SignatureSubpacket] 308data PacketUpdate = InducerSignature String [SignatureSubpacket]
419 | SubKeyDeletion KeyKey KeyKey 309 | SubKeyDeletion KeyKey KeyKey
420 310
421-- | This type is used to indicate where to obtain passphrases.
422data PassphraseSpec = PassphraseSpec
423 { passSpecRingFile :: Maybe FilePath
424 -- ^ If not Nothing, the passphrase is to be used for packets
425 -- from this file.
426 , passSpecKeySpec :: Maybe String
427 -- ^ Non-Nothing value reserved for future use.
428 -- (TODO: Use this to implement per-key passphrase associations).
429 , passSpecPassFile :: InputFile
430 -- ^ The passphrase will be read from this file or file descriptor.
431 }
432 -- | Use this to carry pasphrases from a previous run.
433 | PassphraseMemoizer PacketTranscoder
434 | PassphraseAgent
435
436instance Show PassphraseSpec where
437 show (PassphraseSpec a b c) = "PassphraseSpec "++show (a,b,c)
438 show (PassphraseMemoizer _) = "PassphraseMemoizer"
439instance Eq PassphraseSpec where
440 PassphraseSpec a b c == PassphraseSpec d e f
441 = and [a==d,b==e,c==f]
442 _ == _
443 = False
444
445
446
447data Transform =
448 Autosign
449 -- ^ This operation will make signatures for any tor-style UID
450 -- that matches a tor subkey and thus can be authenticated without
451 -- requring the judgement of a human user.
452 --
453 -- A tor-style UID is one of the following form:
454 --
455 -- > Anonymous <root@HOSTNAME.onion>
456 | DeleteSubkeyByFingerprint String
457 -- ^ Delete the subkey specified by the given fingerprint and any
458 -- associated signatures on that key.
459 | DeleteSubkeyByUsage String
460 -- ^ Delete the subkey specified by the given fingerprint and any
461 -- associated signatures on that key.
462 deriving (Eq,Ord,Show)
463
464-- | This type describes an idempotent transformation (merge or import) on a
465-- set of GnuPG keyrings and other key files.
466data KeyRingOperation = KeyRingOperation
467 { opFiles :: Map.Map InputFile StreamInfo
468 -- ^ Indicates files to be read or updated.
469 , opPassphrases :: [PassphraseSpec]
470 -- ^ Indicates files or file descriptors where passphrases can be found.
471 , opTransforms :: [Transform]
472 -- ^ Transformations to be performed on the key pool after all files have
473 -- been read and before any have been written.
474 , opHome :: Maybe FilePath
475 -- ^ If provided, this is the directory where the 'HomeSec' and 'HomePub'
476 -- files reside. Otherwise, the evironment variable $GNUPGHOME is consulted
477 -- and if that is not set, it falls back to $HOME/.gnupg.
478 }
479 deriving (Eq,Show)
480
481resolveInputFile :: InputFileContext -> InputFile -> [FilePath]
482resolveInputFile ctx = resolve
483 where
484 resolve HomeSec = return (homesecPath ctx)
485 resolve HomePub = return (homepubPath ctx)
486 resolve (ArgFile f) = return f
487 resolve _ = []
488
489resolveForReport :: Maybe InputFileContext -> InputFile -> FilePath
490resolveForReport mctx (Pipe fdr fdw) = resolveForReport mctx (ArgFile str)
491 where str = case (fdr,fdw) of
492 (0,1) -> "-"
493 _ -> "&pipe" ++ show (fdr,fdw)
494resolveForReport mctx (FileDesc fd) = resolveForReport mctx (ArgFile str)
495 where str = "&" ++ show fd
496resolveForReport mctx f = concat $ resolveInputFile ctx f
497 where ctx = fromMaybe (InputFileContext "&secret" "&public") mctx
498
499filesToLock :: 311filesToLock ::
500 KeyRingOperation -> InputFileContext -> [FilePath] 312 KeyRingOperation -> InputFileContext -> [FilePath]
501filesToLock k ctx = do 313filesToLock k ctx = do
@@ -635,33 +447,6 @@ instance ASN1Object RSAPrivateKey where
635 447
636 448
637 449
638-- | This type is used to indicate success or failure
639-- and in the case of success, return the computed object.
640-- The 'FunctorToMaybe' class is implemented to facilitate
641-- branching on failture.
642data KikiCondition a = KikiSuccess a
643 | FailedToLock [FilePath]
644 | BadPassphrase
645 | FailedToMakeSignature
646 | CantFindHome
647 | AmbiguousKeySpec FilePath
648 | CannotImportMasterKey
649 | NoWorkingKey
650 deriving ( Functor, Show )
651
652instance FunctorToMaybe KikiCondition where
653 functorToMaybe (KikiSuccess a) = Just a
654 functorToMaybe _ = Nothing
655
656instance Applicative KikiCondition where
657 pure a = KikiSuccess a
658 f <*> a =
659 case functorToEither f of
660 Right f -> case functorToEither a of
661 Right a -> pure (f a)
662 Left err -> err
663 Left err -> err
664
665-- | This type is used to describe events triggered by 'runKeyRing'. In 450-- | This type is used to describe events triggered by 'runKeyRing'. In
666-- addition to normal feedback (e.g. 'NewPacket'), it also may indicate 451-- addition to normal feedback (e.g. 'NewPacket'), it also may indicate
667-- non-fatal IO exceptions (e.g. 'FailedExternal'). Because a 452-- non-fatal IO exceptions (e.g. 'FailedExternal'). Because a
@@ -1349,18 +1134,6 @@ seek_key (KeyUidMatch pat) ps
1349 uidStr _ = "" 1134 uidStr _ = ""
1350 1135
1351 1136
1352data InputFileContext = InputFileContext
1353 { homesecPath :: FilePath
1354 , homepubPath :: FilePath
1355 }
1356
1357readInputFileS :: InputFileContext -> InputFile -> IO S.ByteString
1358readInputFileS ctx (Pipe fd _) = fdToHandle fd >>= S.hGetContents
1359readInputFileS ctx (FileDesc fd) = fdToHandle fd >>= S.hGetContents
1360readInputFileS ctx inp = do
1361 let fname = resolveInputFile ctx inp
1362 fmap S.concat $ mapM S.readFile fname
1363
1364readInputFileL :: InputFileContext -> InputFile -> IO L.ByteString 1137readInputFileL :: InputFileContext -> InputFile -> IO L.ByteString
1365readInputFileL ctx (Pipe fd _) = fdToHandle fd >>= L.hGetContents 1138readInputFileL ctx (Pipe fd _) = fdToHandle fd >>= L.hGetContents
1366readInputFileL ctx (FileDesc fd) = fdToHandle fd >>= L.hGetContents 1139readInputFileL ctx (FileDesc fd) = fdToHandle fd >>= L.hGetContents
@@ -1436,27 +1209,6 @@ doesInputFileExist ctx f = do
1436-} 1209-}
1437 1210
1438 1211
1439-- | Reads contents of an 'InputFile' or returns the cached content from a prior call.
1440-- An optional prompt is provided and will be printed on stdout only in the case that
1441-- the provided 'InputFile' is 'FileDesc' 0 (i.e. stdin).
1442cachedContents :: Maybe S.ByteString -> InputFileContext -> InputFile -> IO (IO S.ByteString)
1443cachedContents maybePrompt ctx fd = do
1444 ref <- newIORef Nothing
1445 return $ get maybePrompt ref fd
1446 where
1447 trimCR bs = fst $ S.spanEnd (\x -> x==10 || x==13) bs
1448
1449 get maybePrompt ref fd = do
1450 pw <- readIORef ref
1451 flip (flip maybe return) pw $ do
1452 if fd == FileDesc 0 then case maybePrompt of
1453 Just prompt -> S.hPutStr stderr prompt
1454 Nothing -> return ()
1455 else return ()
1456 pw <- fmap trimCR $ readInputFileS ctx fd
1457 writeIORef ref (Just pw)
1458 return pw
1459
1460generateSubkey :: 1212generateSubkey ::
1461 PacketTranscoder 1213 PacketTranscoder
1462 -> KikiCondition (KeyData, [(FilePath, KikiReportAction)]) -- db 1214 -> KikiCondition (KeyData, [(FilePath, KikiReportAction)]) -- db
@@ -2589,115 +2341,6 @@ writePEMKeys doDecrypt db exports = do
2589 try pun $ \pun -> do 2341 try pun $ \pun -> do
2590 return $ KikiSuccess (fname,stream,pun) 2342 return $ KikiSuccess (fname,stream,pun)
2591 2343
2592makeMemoizingDecrypter :: KeyRingOperation -> InputFileContext
2593 -> Map.Map KeyKey (OriginMapped Query)
2594 -> IO PacketTranscoder
2595makeMemoizingDecrypter operation ctx keys = do
2596 if null chains then do
2597 -- (*) Notice we do not pass ctx to resolveForReport.
2598 -- This is because the merge function does not currently use a context
2599 -- and the pws map keys must match the MappedPacket locations.
2600 -- TODO: Perhaps these should both be of type InputFile rather than
2601 -- FilePath?
2602 -- pws :: Map.Map FilePath (IO S.ByteString)
2603 {-
2604 -- This disabled code obtained password sources from StreamInfo records.
2605 pws <-
2606 Traversable.mapM (cachedContents ctx . fromJust . pwfile . typ)
2607 (Map.mapKeys (resolveForReport Nothing) -- see note (*) note above
2608 $ Map.filter (isJust . pwfile . typ) $ opFiles operation)
2609 -}
2610 let prompt = Just "Enter possibly multi-line passphrase (Press CTRL-D when finished):\n"
2611 -- List of file-specific password sources.
2612 pws2 <-
2613 Traversable.mapM (cachedContents prompt ctx)
2614 $ Map.fromList $ mapMaybe
2615 (\spec -> (,passSpecPassFile spec) `fmap` do
2616 guard $ isNothing $ passSpecKeySpec spec
2617 passSpecRingFile spec)
2618 passspecs
2619 -- List of general password sources.
2620 defpw <- do
2621 Traversable.mapM (cachedContents prompt ctx . passSpecPassFile)
2622 $ listToMaybe $ filter (\sp -> isNothing (passSpecRingFile sp)
2623 && isNothing (passSpecKeySpec sp))
2624 $ passspecs
2625 unkeysRef <- newIORef (Map.empty :: Map.Map KeyKey Packet)
2626 return $ doDecrypt unkeysRef ({- pws `Map.union` -} pws2) defpw (not $ null agentspec)
2627 else let PassphraseMemoizer f = head chains
2628 in return f
2629 where
2630 (chains,passspecs0) = partition isChain $ opPassphrases operation
2631 where isChain (PassphraseMemoizer {}) = True
2632 isChain _ = False
2633 (agentspec,passspecs) = partition isAgent $ opPassphrases operation
2634 where isAgent PassphraseAgent = True
2635 isAgent _ = False
2636 doDecrypt :: IORef (Map.Map KeyKey Packet)
2637 -> Map.Map FilePath (IO S.ByteString)
2638 -> Maybe (IO S.ByteString)
2639 -> Bool
2640 -> (SymmetricAlgorithm,S2K)
2641 -> MappedPacket
2642 -> IO (KikiCondition Packet)
2643 doDecrypt unkeysRef pws defpw agent_requested (dest_alg,dest_s2k) mp0 = do
2644 unkeys <- readIORef unkeysRef
2645 let (mp,qry) = fromMaybe (mp0,Query (packet mp0) "anonymous2" Nothing) $ do
2646 k <- Map.lookup kk keys
2647 return (mergeKeyPacket "decrypt" mp0 (fmap queryPacket k), packet k)
2648 wk = packet mp0
2649 kk = keykey wk
2650 fs = Map.keys $ locations mp
2651
2652 decryptIt [] = return BadPassphrase
2653 decryptIt (getpw:getpws) = tries 1 getpw (decryptIt getpws)
2654 where
2655 tries count getpw recurse = do
2656 -- TODO: This function should use mergeKeyPacket to
2657 -- combine the packet with it's unspilled version before
2658 -- attempting to decrypt it. Note: We are uninterested
2659 -- in the 'locations' field, so this would effectively
2660 -- allow you to run 'decryptIt' on an unencrypted public key
2661 -- to obtain it's secret key.
2662 (pw,wants_retry) <- getpw (if count>1 then AskAgain "Bad pasphrase." else Ask,qry)
2663 let wkun = fromMaybe wk $ do
2664 guard $ symmetric_algorithm (packet mp) /= Unencrypted
2665 decryptSecretKey pw (packet mp)
2666
2667 case symmetric_algorithm wkun of
2668
2669 Unencrypted -> do
2670 writeIORef unkeysRef (Map.insert kk wkun unkeys)
2671 ek <- if dest_alg==Unencrypted
2672 then return $ Just wkun
2673 else encryptSecretKey pw dest_s2k dest_alg wkun
2674 case ek of
2675 Nothing | wants_retry && count<3 -> tries (count+1) getpw recurse
2676 Nothing -> recurse
2677 Just wken -> return $ KikiSuccess wken
2678
2679 _ -> recurse
2680
2681 getpws = (map (const . fmap (,False)) $ mapMaybe (`Map.lookup` pws) fs ++ maybeToList defpw) ++ [ agentpw | agent_requested ]
2682
2683 agentpw (ask,qry) = do
2684 s <- session
2685 fromMaybe (return ("",False)) $ do
2686 s <- s
2687 Just $ do
2688 case ask of AskAgain _ -> clearPassphrase s (queryPacket qry)
2689 _ -> return ()
2690 mbpw <- getPassphrase s ask qry
2691 quit s
2692 return ( maybe "" S8.pack mbpw, True)
2693
2694 if symmetric_algorithm wk == dest_alg
2695 && ( symmetric_algorithm wk == Unencrypted || s2k wk == dest_s2k )
2696 then return (KikiSuccess wk)
2697 else maybe (decryptIt getpws)
2698 (return . KikiSuccess)
2699 $ Map.lookup kk unkeys
2700
2701performManipulations :: 2344performManipulations ::
2702 (PacketDecrypter) 2345 (PacketDecrypter)
2703 -> KeyRingRuntime 2346 -> KeyRingRuntime
@@ -3162,19 +2805,6 @@ lookupEnv var =
3162 handleIO_ (return Nothing) $ fmap Just (getEnv var) 2805 handleIO_ (return Nothing) $ fmap Just (getEnv var)
3163#endif 2806#endif
3164 2807
3165isKey :: Packet -> Bool
3166isKey (PublicKeyPacket {}) = True
3167isKey (SecretKeyPacket {}) = True
3168isKey _ = False
3169
3170isUserID :: Packet -> Bool
3171isUserID (UserIDPacket {}) = True
3172isUserID _ = False
3173
3174isTrust :: Packet -> Bool
3175isTrust (TrustPacket {}) = True
3176isTrust _ = False
3177
3178sigpackets :: 2808sigpackets ::
3179 Monad m => 2809 Monad m =>
3180 Word8 -> [SignatureSubpacket] -> [SignatureSubpacket] -> m Packet 2810 Word8 -> [SignatureSubpacket] -> [SignatureSubpacket] -> m Packet
@@ -3497,32 +3127,10 @@ makeSig doDecrypt top fname subkey_p tags mbsig = do
3497 return $ fmap (,[]) newsig 3127 return $ fmap (,[]) newsig
3498 3128
3499 3129
3500-- | The position and acces a packet had before the operation
3501data OriginFlags = OriginFlags
3502 { originallyPublic :: Bool
3503 -- ^ false if SecretKeyPacket
3504 , originalNum :: Int
3505 -- ^ packets are numbered, starting from 1..
3506 } deriving Show
3507
3508type OriginMap = Map.Map FilePath OriginFlags
3509
3510type MappedPacket = OriginMapped Packet
3511data OriginMapped a = MappedPacket
3512 { packet :: a
3513 , locations :: OriginMap
3514 } deriving Show
3515instance Functor OriginMapped where
3516 fmap f (MappedPacket x ls) = MappedPacket (f x) ls
3517
3518type TrustMap = Map.Map FilePath Packet 3130type TrustMap = Map.Map FilePath Packet
3519type SigAndTrust = ( MappedPacket 3131type SigAndTrust = ( MappedPacket
3520 , TrustMap ) -- trust packets 3132 , TrustMap ) -- trust packets
3521 3133
3522-- | The 'KeyKey'-type is used to store the information of a key
3523-- which is used for finger-printing
3524type KeyKey = [ByteString]
3525
3526data SubKey = SubKey MappedPacket [SigAndTrust] deriving Show 3134data SubKey = SubKey MappedPacket [SigAndTrust] deriving Show
3527 3135
3528-- | This is a GPG Identity which includes a master key and all its UIDs and 3136-- | This is a GPG Identity which includes a master key and all its UIDs and
@@ -3554,15 +3162,6 @@ mappedPacketWithHint filename p hint = MappedPacket
3554 , locations = Map.singleton filename (origin p hint) 3162 , locations = Map.singleton filename (origin p hint)
3555 } 3163 }
3556 3164
3557keykey :: Packet -> KeyKey
3558keykey key =
3559 -- Note: The key's timestamp is normally included in it's fingerprint.
3560 -- This is undesirable for kiki because it causes the same
3561 -- key to be imported multiple times and show as apparently
3562 -- distinct keys with different fingerprints.
3563 -- Thus, we will remove the timestamp.
3564 fingerprint_material (key {timestamp=0}) -- TODO: smaller key?
3565
3566uidkey :: Packet -> String 3165uidkey :: Packet -> String
3567uidkey (UserIDPacket str) = str 3166uidkey (UserIDPacket str) = str
3568 3167
@@ -3598,29 +3197,6 @@ onionName kd = (addr,name)
3598 (addr,(name:_,_)) = getHostnames kd 3197 (addr,(name:_,_)) = getHostnames kd
3599-} 3198-}
3600 3199
3601-- | Compare different versions if the same key pair. Public versions
3602-- are considered greater. If the two packets do not represent the same
3603-- key or the packets are not keys at all, an error will result that
3604-- includes the context provided as the first argument.
3605keyCompare :: String -> Packet -> Packet -> Ordering
3606keyCompare what (SecretKeyPacket {}) (PublicKeyPacket {}) = LT
3607keyCompare what (PublicKeyPacket {}) (SecretKeyPacket {}) = GT
3608keyCompare what a b | keykey a==keykey b = EQ
3609keyCompare what a b = error $ unlines ["Unable to merge "++what++":"
3610 , if isKey a then fingerprint a else ""
3611 , PP.ppShow a
3612 , if isKey b then fingerprint b else ""
3613 , PP.ppShow b
3614 ]
3615
3616-- | Merge two representations of the same key, prefering secret version
3617-- because they have more information.
3618mergeKeyPacket :: String -> MappedPacket -> MappedPacket -> MappedPacket
3619mergeKeyPacket what key p =
3620 key { packet = minimumBy (keyCompare what) [packet key,packet p]
3621 , locations = Map.union (locations key) (locations p)
3622 }
3623
3624 3200
3625merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))] 3201merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))]
3626 -> KeyDB 3202 -> KeyDB