diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/GnuPGAgent.hs | 23 | ||||
-rw-r--r-- | lib/KeyRing.hs | 143 | ||||
-rw-r--r-- | lib/Kiki.hs | 47 | ||||
-rw-r--r-- | lib/PacketTranscoder.hs | 137 | ||||
-rw-r--r-- | lib/Types.hs | 38 |
5 files changed, 229 insertions, 159 deletions
diff --git a/lib/GnuPGAgent.hs b/lib/GnuPGAgent.hs index 4a0e8c8..5878357 100644 --- a/lib/GnuPGAgent.hs +++ b/lib/GnuPGAgent.hs | |||
@@ -60,15 +60,15 @@ percentPlusEscape :: String -> String | |||
60 | percentPlusEscape s = do | 60 | percentPlusEscape s = do |
61 | c <- s | 61 | c <- s |
62 | case c of | 62 | case c of |
63 | ' ' -> "+" | 63 | ' ' -> "+" |
64 | '+' -> "%2B" | 64 | '+' -> "%2B" |
65 | '"' -> "%22" | 65 | '"' -> "%22" |
66 | '%' -> "%25" | 66 | '%' -> "%25" |
67 | _ | c < ' ' -> printf "%%%02X" (ord c) | 67 | _ | c < ' ' -> printf "%%%02X" (ord c) |
68 | _ -> return c | 68 | _ -> return c |
69 | 69 | ||
70 | clearPassphrase agent key = do | 70 | clearPassphrase agent key = do |
71 | let cmd = "clear_passphrase "++fingerprint key | 71 | let cmd = "clear_passphrase --mode=normal "++fingerprint key |
72 | hPutStrLn (agentHandle agent) cmd | 72 | hPutStrLn (agentHandle agent) cmd |
73 | 73 | ||
74 | data Query = Query | 74 | data Query = Query |
@@ -78,21 +78,22 @@ data Query = Query | |||
78 | } | 78 | } |
79 | deriving Show | 79 | deriving Show |
80 | 80 | ||
81 | data QueryMode = AskNot | AskAgain String | Ask | 81 | data QueryMode = AskNot | AskAgain String | AskExisting | AskNew |
82 | deriving (Show,Eq,Ord) | 82 | deriving (Show,Eq,Ord) |
83 | 83 | ||
84 | getPassphrase :: GnuPGAgent -> QueryMode -> Query -> IO (Maybe String) | 84 | getPassphrase :: GnuPGAgent -> QueryMode -> Query -> IO (Maybe String) |
85 | getPassphrase agent ask (Query key uid masterkey) = do | 85 | getPassphrase agent ask (Query key uid masterkey) = do |
86 | let (er0,pr,desc) = prompts key uid masterkey | 86 | let (er0,pr,desc) = prompts key uid masterkey |
87 | (er,askopt) = case ask of | 87 | (er,askopt) = case ask of |
88 | AskNot -> (er0,"--no-ask") | 88 | AskNot -> (er0,"--no-ask ") |
89 | AskAgain ermsg -> (ermsg,"") | 89 | AskAgain ermsg -> (ermsg,"") |
90 | Ask -> (er0,"") | 90 | AskExisting -> (er0,"") |
91 | AskNew -> (er0,"--repeat=1 ") | ||
91 | cmd = "get_passphrase "++askopt++fingerprint key++" "++unwords (map percentPlusEscape [er,pr,desc]) | 92 | cmd = "get_passphrase "++askopt++fingerprint key++" "++unwords (map percentPlusEscape [er,pr,desc]) |
92 | -- putStrLn cmd | 93 | hPutStrLn stderr $ "gpg-agent <- " ++ cmd |
93 | hPutStrLn (agentHandle agent) cmd | 94 | hPutStrLn (agentHandle agent) cmd |
94 | r0 <- hGetLine (agentHandle agent) | 95 | r0 <- hGetLine (agentHandle agent) |
95 | -- putStrLn $ "agent says: " ++ r0 | 96 | -- hPutStrLn stderr $ "agent says: " ++ r0 |
96 | case takeWhile (/=' ') r0 of | 97 | case takeWhile (/=' ') r0 of |
97 | "OK" -> hGetLine (agentHandle agent) >>= unhex . drop 3 | 98 | "OK" -> hGetLine (agentHandle agent) >>= unhex . drop 3 |
98 | where | 99 | where |
diff --git a/lib/KeyRing.hs b/lib/KeyRing.hs index 313258d..e084fcd 100644 --- a/lib/KeyRing.hs +++ b/lib/KeyRing.hs | |||
@@ -23,6 +23,7 @@ | |||
23 | {-# LANGUAGE DoAndIfThenElse #-} | 23 | {-# LANGUAGE DoAndIfThenElse #-} |
24 | {-# LANGUAGE PatternGuards #-} | 24 | {-# LANGUAGE PatternGuards #-} |
25 | {-# LANGUAGE ForeignFunctionInterface #-} | 25 | {-# LANGUAGE ForeignFunctionInterface #-} |
26 | {-# LANGUAGE LambdaCase #-} | ||
26 | module KeyRing | 27 | module KeyRing |
27 | ( | 28 | ( |
28 | -- * Error Handling | 29 | -- * Error Handling |
@@ -84,6 +85,7 @@ module KeyRing | |||
84 | , accBindings | 85 | , accBindings |
85 | , isSubkeySignature | 86 | , isSubkeySignature |
86 | , torhash | 87 | , torhash |
88 | , torUIDFromKey | ||
87 | , ParsedCert(..) | 89 | , ParsedCert(..) |
88 | , parseCertBlob | 90 | , parseCertBlob |
89 | , packetFromPublicRSAKey | 91 | , packetFromPublicRSAKey |
@@ -186,7 +188,7 @@ import Foreign.C.Error ( throwErrnoIfMinus1_ ) | |||
186 | import Foreign.Storable | 188 | import Foreign.Storable |
187 | #endif | 189 | #endif |
188 | import System.FilePath ( takeDirectory ) | 190 | import System.FilePath ( takeDirectory ) |
189 | import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr, stderr) | 191 | import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr, stderr, hClose) |
190 | import Data.IORef | 192 | import Data.IORef |
191 | import System.Posix.IO ( fdToHandle ) | 193 | import System.Posix.IO ( fdToHandle ) |
192 | import qualified Data.Traversable as Traversable | 194 | import qualified Data.Traversable as Traversable |
@@ -624,14 +626,6 @@ usageString flgs = | |||
624 | 626 | ||
625 | 627 | ||
626 | 628 | ||
627 | -- matchpr computes the fingerprint of the given key truncated to | ||
628 | -- be the same lenght as the given fingerprint for comparison. | ||
629 | -- | ||
630 | -- matchpr fp = Data.List.Extra.takeEnd (length fp) | ||
631 | -- | ||
632 | matchpr :: String -> Packet -> String | ||
633 | matchpr fp k = reverse $ zipWith const (reverse (fingerprint k)) fp | ||
634 | |||
635 | keyFlags :: t -> [Packet] -> [SignatureSubpacket] | 629 | keyFlags :: t -> [Packet] -> [SignatureSubpacket] |
636 | keyFlags wkun uids = keyFlags0 wkun (filter isSignaturePacket uids) | 630 | keyFlags wkun uids = keyFlags0 wkun (filter isSignaturePacket uids) |
637 | 631 | ||
@@ -1181,7 +1175,7 @@ writeStampedL ctx f stamp bs = writeStamped0 ctx f stamp (either L.hPut L.writeF | |||
1181 | -} | 1175 | -} |
1182 | 1176 | ||
1183 | writeStamped :: InputFileContext -> InputFile -> Posix.EpochTime -> String -> IO () | 1177 | writeStamped :: InputFileContext -> InputFile -> Posix.EpochTime -> String -> IO () |
1184 | writeStamped ctx f stamp str = writeStamped0 ctx f stamp (either hPutStr writeFile) str | 1178 | writeStamped ctx f stamp str = writeStamped0 ctx f stamp (either (\h x-> hPutStr h x >> hClose h) writeFile) str |
1185 | 1179 | ||
1186 | getInputFileTime :: InputFileContext -> InputFile -> IO CTime | 1180 | getInputFileTime :: InputFileContext -> InputFile -> IO CTime |
1187 | getInputFileTime ctx (Pipe fdr fdw) = do | 1181 | getInputFileTime ctx (Pipe fdr fdw) = do |
@@ -1222,7 +1216,7 @@ generateSubkey transcode kd' (genparam,StreamInfo { spill = KF_Match tag }) = do | |||
1222 | if null subs | 1216 | if null subs |
1223 | then do | 1217 | then do |
1224 | newkey <- generateKey genparam | 1218 | newkey <- generateKey genparam |
1225 | kdr <- insertSubkey transcode (keykey (keyPacket kd)) kd [mkUsage tag] "" newkey | 1219 | kdr <- insertSubkey transcode (keykey (keyPacket kd)) kd [mkUsage tag] (ArgFile "") newkey |
1226 | try kdr $ \(newkd,report) -> do | 1220 | try kdr $ \(newkd,report) -> do |
1227 | return $ KikiSuccess (newkd, report ++ [("", NewPacket $ showPacket newkey)]) | 1221 | return $ KikiSuccess (newkd, report ++ [("", NewPacket $ showPacket newkey)]) |
1228 | else do | 1222 | else do |
@@ -1233,7 +1227,7 @@ importSecretKey :: | |||
1233 | (PacketTranscoder) | 1227 | (PacketTranscoder) |
1234 | -> KikiCondition | 1228 | -> KikiCondition |
1235 | (Map.Map KeyKey KeyData, [(FilePath, KikiReportAction)]) | 1229 | (Map.Map KeyKey KeyData, [(FilePath, KikiReportAction)]) |
1236 | -> (FilePath, Maybe [Char], [KeyKey], StreamInfo, t) | 1230 | -> (InputFile, Maybe [Char], [KeyKey], StreamInfo, t) |
1237 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath, KikiReportAction)])) | 1231 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath, KikiReportAction)])) |
1238 | importSecretKey transcode db' tup = do | 1232 | importSecretKey transcode db' tup = do |
1239 | try db' $ \(db',report0) -> do | 1233 | try db' $ \(db',report0) -> do |
@@ -1345,10 +1339,6 @@ writeHostsFiles krd ctx (hostdbs0,hostdbs,u1,gpgnames,outgoing_names) = do | |||
1345 | return $ map (first $ resolveForReport $ Just ctx) rs | 1339 | return $ map (first $ resolveForReport $ Just ctx) rs |
1346 | return $ concat rss | 1340 | return $ concat rss |
1347 | 1341 | ||
1348 | isSecretKey :: Packet -> Bool | ||
1349 | isSecretKey (SecretKeyPacket {}) = True | ||
1350 | isSecretKey _ = False | ||
1351 | |||
1352 | -- | buildKeyDB | 1342 | -- | buildKeyDB |
1353 | -- | 1343 | -- |
1354 | -- merge all keyrings, PEM files, and wallets into process memory. | 1344 | -- merge all keyrings, PEM files, and wallets into process memory. |
@@ -1368,11 +1358,10 @@ buildKeyDB :: InputFileContext -> Maybe String -> KeyRingOperation | |||
1368 | ) | 1358 | ) |
1369 | ,{- report_imports -} [(FilePath,KikiReportAction)])) | 1359 | ,{- report_imports -} [(FilePath,KikiReportAction)])) |
1370 | buildKeyDB ctx grip0 keyring = do | 1360 | buildKeyDB ctx grip0 keyring = do |
1371 | let | 1361 | let files istyp = do |
1372 | files istyp = do | ||
1373 | (f,stream) <- Map.toList (opFiles keyring) | 1362 | (f,stream) <- Map.toList (opFiles keyring) |
1374 | guard (istyp $ typ stream) | 1363 | guard (istyp $ typ stream) |
1375 | resolveInputFile ctx f | 1364 | return f -- resolveInputFile ctx f |
1376 | 1365 | ||
1377 | ringMap0 = Map.filter (isring . typ) $ opFiles keyring | 1366 | ringMap0 = Map.filter (isring . typ) $ opFiles keyring |
1378 | (genMap,ringMap) = Map.partitionWithKey isgen ringMap0 | 1367 | (genMap,ringMap) = Map.partitionWithKey isgen ringMap0 |
@@ -1392,7 +1381,7 @@ buildKeyDB ctx grip0 keyring = do | |||
1392 | _ -> AutoAccess | 1381 | _ -> AutoAccess |
1393 | acc -> acc | 1382 | acc -> acc |
1394 | 1383 | ||
1395 | readw wk n = fmap (n,) (readPacketsFromWallet wk (ArgFile n)) | 1384 | readw wk n = fmap (n,) (readPacketsFromWallet wk n) |
1396 | 1385 | ||
1397 | -- KeyRings (todo: KikiCondition reporting?) | 1386 | -- KeyRings (todo: KikiCondition reporting?) |
1398 | (spilled,mwk,grip,accs,keyqs,unspilled) <- do | 1387 | (spilled,mwk,grip,accs,keyqs,unspilled) <- do |
@@ -1418,69 +1407,17 @@ buildKeyDB ctx grip0 keyring = do | |||
1418 | -- | keys | 1407 | -- | keys |
1419 | -- process ringPackets, and get a map of fingerprint info to | 1408 | -- process ringPackets, and get a map of fingerprint info to |
1420 | -- to a packet, remembering it's original file, access. | 1409 | -- to a packet, remembering it's original file, access. |
1421 | keys :: Map.Map KeyKey (MappedPacket,Map.Map String [Packet]) | 1410 | keys :: Map.Map KeyKey (OriginMapped Query) |
1422 | keys = Map.foldl slurpkeys Map.empty | 1411 | mwk :: Maybe MappedPacket |
1423 | $ Map.mapWithKey filterSecrets ringPackets | 1412 | (mwk, keys) = keyQueries grip ringPackets |
1424 | where | ||
1425 | filterSecrets :: InputFile -> (a,Message) -> [[MappedPacket]] | ||
1426 | filterSecrets f (_,Message ps) = keygroups | ||
1427 | -- filter (isSecretKey . packet) mps | ||
1428 | where | ||
1429 | mps = zipWith (mappedPacketWithHint fname) ps [1..] | ||
1430 | fname = resolveForReport (Just ctx) f | ||
1431 | keygroups = dropWhile (not . isSecretKey . packet . head) | ||
1432 | $ groupBy (const $ not . isSecretKey . packet) mps | ||
1433 | slurpkeys :: (Map.Map KeyKey (MappedPacket,Map.Map String [Packet])) | ||
1434 | -> [[MappedPacket]] | ||
1435 | -> (Map.Map KeyKey (MappedPacket,Map.Map String [Packet])) | ||
1436 | slurpkeys m pss = Map.unionWith combineKeyKey m m2 | ||
1437 | where | ||
1438 | m2 :: Map.Map KeyKey (MappedPacket, (Map.Map String [Packet])) | ||
1439 | m2 = Map.fromList $ map build pss | ||
1440 | where | ||
1441 | build ps = (kk,(kp,uidmap ps')) | ||
1442 | where | ||
1443 | (kpkt,ps') = splitAt 1 ps | ||
1444 | kp = head kpkt | ||
1445 | kk = keykey . packet $ kp | ||
1446 | combineKeyKey (mp,um) (mp2,um2) = (mp,Map.unionWith (++) um um2) | ||
1447 | uidmap ps = um2 | ||
1448 | where | ||
1449 | ugs = dropWhile (not . isUserID . packet .head) $ groupBy (const $ not . isUserID . packet) ps | ||
1450 | um2 = Map.fromList | ||
1451 | $ map (\(MappedPacket (UserIDPacket s) _:sigs)->(s,takeWhile isSignaturePacket $ map packet sigs)) ugs | ||
1452 | -- | mwk | ||
1453 | -- first master key matching the provided grip | ||
1454 | -- (the m is for "MappedPacket", wk for working key) | ||
1455 | mwk = fst <$> mwkq | ||
1456 | |||
1457 | main_query = fromMaybe (Query MarkerPacket "anonymous1" Nothing) $ snd <$> mwkq | ||
1458 | |||
1459 | keyqs :: Map.Map KeyKey (OriginMapped Query) | ||
1460 | keyqs = fmap (\(mp,us) -> mp { packet = main_query { queryPacket = packet mp} }) keys | ||
1461 | |||
1462 | mwkq :: Maybe (MappedPacket,Query) | ||
1463 | mwkq = listToMaybe $ do | ||
1464 | fp <- maybeToList grip | ||
1465 | let matchfp (mp,us) | ||
1466 | | not (is_subkey p) && matchpr fp p == fp = Just (mp,query p us) | ||
1467 | | otherwise = Nothing | ||
1468 | where p = packet mp | ||
1469 | -- TODO: check signature on UID packet? | ||
1470 | -- TODO: custom queries for subkeys? | ||
1471 | query p us = Query p | ||
1472 | (fromMaybe "" $ listToMaybe $ Map.keys us) | ||
1473 | Nothing -- No subkey queries for now. | ||
1474 | Map.elems $ Map.mapMaybe matchfp keys | ||
1475 | 1413 | ||
1476 | -- | accs | 1414 | -- | accs |
1477 | -- file access(Sec | Pub) lookup table | 1415 | -- file access(Sec | Pub) lookup table |
1478 | accs :: Map.Map InputFile Access | 1416 | accs :: Map.Map InputFile Access |
1479 | accs = fmap (access . fst) ringPackets | 1417 | accs = fmap (access . fst) ringPackets |
1480 | return (spilled,mwk,grip,accs,keyqs,fmap snd unspilled) | 1418 | return (spilled,mwk,grip,accs,keys,fmap snd unspilled) |
1481 | 1419 | ||
1482 | putStrLn $ ppShow keyqs | 1420 | transcode <- makeMemoizingDecrypter keyring ctx (mwk,keyqs) |
1483 | transcode <- makeMemoizingDecrypter keyring ctx keyqs | ||
1484 | let doDecrypt = transcode (Unencrypted,S2K 100 "") | 1421 | let doDecrypt = transcode (Unencrypted,S2K 100 "") |
1485 | 1422 | ||
1486 | let wk = fmap packet mwk | 1423 | let wk = fmap packet mwk |
@@ -1548,7 +1485,6 @@ buildKeyDB ctx grip0 keyring = do | |||
1548 | let pems = do | 1485 | let pems = do |
1549 | (n,stream) <- Map.toList $ opFiles keyring | 1486 | (n,stream) <- Map.toList $ opFiles keyring |
1550 | grip <- maybeToList grip | 1487 | grip <- maybeToList grip |
1551 | n <- resolveInputFile ctx n | ||
1552 | guard $ spillable stream && isSecretKeyFile (typ stream) | 1488 | guard $ spillable stream && isSecretKeyFile (typ stream) |
1553 | let us = mapMaybe usageFromFilter [fill stream,spill stream] | 1489 | let us = mapMaybe usageFromFilter [fill stream,spill stream] |
1554 | usage <- take 1 us | 1490 | usage <- take 1 us |
@@ -1559,7 +1495,10 @@ buildKeyDB ctx grip0 keyring = do | |||
1559 | ms = map fst $ filterMatches topspec (Map.toList db) | 1495 | ms = map fst $ filterMatches topspec (Map.toList db) |
1560 | cmd = initializer stream | 1496 | cmd = initializer stream |
1561 | return (n,subspec,ms,stream, cmd) | 1497 | return (n,subspec,ms,stream, cmd) |
1562 | imports <- filterM (\(n,_,_,_,_) -> doesFileExist n) pems | 1498 | |
1499 | imports <- filterM (\case (ArgFile n,_,_,_,_) -> doesFileExist n | ||
1500 | _ -> return True) | ||
1501 | pems | ||
1563 | db <- foldM (importSecretKey transcode) (KikiSuccess (db,[])) imports | 1502 | db <- foldM (importSecretKey transcode) (KikiSuccess (db,[])) imports |
1564 | try db $ \(db,reportPEMs) -> do | 1503 | try db $ \(db,reportPEMs) -> do |
1565 | 1504 | ||
@@ -1595,6 +1534,9 @@ generateInternals transcode mwk db gens = do | |||
1595 | torhash :: Packet -> String | 1534 | torhash :: Packet -> String |
1596 | torhash key = fromMaybe "" $ derToBase32 <$> derRSA key | 1535 | torhash key = fromMaybe "" $ derToBase32 <$> derRSA key |
1597 | 1536 | ||
1537 | torUIDFromKey :: Packet -> String | ||
1538 | torUIDFromKey key = "Anonymous <root@" ++ take 16 (torhash key) ++ ".onion>" | ||
1539 | |||
1598 | derToBase32 :: ByteString -> String | 1540 | derToBase32 :: ByteString -> String |
1599 | derToBase32 = map toLower . base32 . sha1 | 1541 | derToBase32 = map toLower . base32 . sha1 |
1600 | where | 1542 | where |
@@ -1827,19 +1769,19 @@ readSecretPEMFile fname = do | |||
1827 | doImport | 1769 | doImport |
1828 | :: PacketTranscoder | 1770 | :: PacketTranscoder |
1829 | -> Map.Map KeyKey KeyData | 1771 | -> Map.Map KeyKey KeyData |
1830 | -> (FilePath, Maybe [Char], [KeyKey], StreamInfo, t) | 1772 | -> (InputFile, Maybe [Char], [KeyKey], StreamInfo, t) |
1831 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath,KikiReportAction)])) | 1773 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath,KikiReportAction)])) |
1832 | doImport transcode db (fname,subspec,ms,typ -> typ,_) = do | 1774 | doImport transcode db (fname,subspec,ms,typ -> typ,_) = do |
1833 | flip (maybe $ return CannotImportMasterKey) | 1775 | flip (maybe $ return CannotImportMasterKey) |
1834 | subspec $ \tag -> do | 1776 | subspec $ \tag -> do |
1835 | (certs,keys) <- case typ of | 1777 | (certs,keys) <- case typ of |
1836 | PEMFile -> do | 1778 | PEMFile -> do |
1837 | ps <- readSecretPEMFile (ArgFile fname) | 1779 | ps <- readSecretPEMFile fname |
1838 | let (mapMaybe spemCert -> certs,mapMaybe spemPacket-> keys) | 1780 | let (mapMaybe spemCert -> certs,mapMaybe spemPacket-> keys) |
1839 | = partition (isJust . spemCert) ps | 1781 | = partition (isJust . spemCert) ps |
1840 | return (certs,keys) | 1782 | return (certs,keys) |
1841 | DNSPresentation -> do | 1783 | DNSPresentation -> do |
1842 | p <- readSecretDNSFile (ArgFile fname) | 1784 | p <- readSecretDNSFile fname |
1843 | return ([],[p]) | 1785 | return ([],[p]) |
1844 | -- TODO Probably we need to move to a new design where signature | 1786 | -- TODO Probably we need to move to a new design where signature |
1845 | -- packets are merged into the database in one phase with null | 1787 | -- packets are merged into the database in one phase with null |
@@ -1852,7 +1794,7 @@ doImport transcode db (fname,subspec,ms,typ -> typ,_) = do | |||
1852 | try prior $ \(db,report) -> do | 1794 | try prior $ \(db,report) -> do |
1853 | let (m0,tailms) = splitAt 1 ms | 1795 | let (m0,tailms) = splitAt 1 ms |
1854 | if (not (null tailms) || null m0) | 1796 | if (not (null tailms) || null m0) |
1855 | then return $ AmbiguousKeySpec fname | 1797 | then return $ AmbiguousKeySpec (resolveForReport Nothing fname) |
1856 | else do | 1798 | else do |
1857 | let kk = keykey key | 1799 | let kk = keykey key |
1858 | cs = filter (\c -> kk==keykey (pcertKey c)) certs | 1800 | cs = filter (\c -> kk==keykey (pcertKey c)) certs |
@@ -1872,7 +1814,7 @@ doImportG | |||
1872 | -> Map.Map KeyKey KeyData | 1814 | -> Map.Map KeyKey KeyData |
1873 | -> [KeyKey] -- m0, only head is used | 1815 | -> [KeyKey] -- m0, only head is used |
1874 | -> [SignatureSubpacket] -- tags | 1816 | -> [SignatureSubpacket] -- tags |
1875 | -> FilePath | 1817 | -> InputFile |
1876 | -> Packet | 1818 | -> Packet |
1877 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath,KikiReportAction)])) | 1819 | -> IO (KikiCondition (Map.Map KeyKey KeyData, [(FilePath,KikiReportAction)])) |
1878 | doImportG transcode db m0 tags fname key = do | 1820 | doImportG transcode db m0 tags fname key = do |
@@ -1881,13 +1823,16 @@ doImportG transcode db m0 tags fname key = do | |||
1881 | kdr <- insertSubkey transcode kk kd tags fname key | 1823 | kdr <- insertSubkey transcode kk kd tags fname key |
1882 | try kdr $ \(kd',rrs) -> return $ KikiSuccess (Map.insert kk kd' db, rrs) | 1824 | try kdr $ \(kd',rrs) -> return $ KikiSuccess (Map.insert kk kd' db, rrs) |
1883 | 1825 | ||
1884 | insertSubkey transcode kk (KeyData top topsigs uids subs) tags fname key0 = do | 1826 | insertSubkey transcode kk (KeyData top topsigs uids subs) tags inputfile key0 = do |
1885 | let topcipher = symmetric_algorithm $ packet top | 1827 | let topcipher = symmetric_algorithm $ packet top |
1886 | tops2k = s2k $ packet top | 1828 | tops2k = s2k $ packet top |
1829 | doDecrypt = transcode (Unencrypted,S2K 100 "") | ||
1830 | fname = resolveForReport Nothing inputfile | ||
1831 | wkun <- doDecrypt top | ||
1832 | try wkun $ \wkun -> do | ||
1887 | key' <- transcode (topcipher,tops2k) $ mappedPacket "" key0 | 1833 | key' <- transcode (topcipher,tops2k) $ mappedPacket "" key0 |
1888 | try key' $ \key -> do | 1834 | try key' $ \key -> do |
1889 | let subkk = keykey key | 1835 | let subkk = keykey key |
1890 | doDecrypt = transcode (Unencrypted,S2K 100 "") | ||
1891 | (is_new, subkey) = maybe (True, SubKey (mappedPacket fname key) | 1836 | (is_new, subkey) = maybe (True, SubKey (mappedPacket fname key) |
1892 | []) | 1837 | []) |
1893 | ( (False,) . addOrigin ) | 1838 | ( (False,) . addOrigin ) |
@@ -1903,7 +1848,7 @@ insertSubkey transcode kk (KeyData top topsigs uids subs) tags fname key0 = do | |||
1903 | 1848 | ||
1904 | istor = do | 1849 | istor = do |
1905 | guard ("tor" `elem` mapMaybe usage tags) | 1850 | guard ("tor" `elem` mapMaybe usage tags) |
1906 | return $ "Anonymous <root@" ++ take 16 (torhash key) ++ ".onion>" | 1851 | return $ torUIDFromKey key |
1907 | 1852 | ||
1908 | uids' <- flip (maybe $ return $ KikiSuccess (uids,[])) istor $ \idstr -> do | 1853 | uids' <- flip (maybe $ return $ KikiSuccess (uids,[])) istor $ \idstr -> do |
1909 | let has_torid = do | 1854 | let has_torid = do |
@@ -1913,9 +1858,6 @@ insertSubkey transcode kk (KeyData top topsigs uids subs) tags fname key0 = do | |||
1913 | s <- (signatures $ Message (packet top:UserIDPacket idstr:map (packet . fst) sigtrusts)) | 1858 | s <- (signatures $ Message (packet top:UserIDPacket idstr:map (packet . fst) sigtrusts)) |
1914 | signatures_over $ verify (Message [packet top]) s | 1859 | signatures_over $ verify (Message [packet top]) s |
1915 | flip (flip maybe $ const $ return $ KikiSuccess (uids,[])) has_torid $ do | 1860 | flip (flip maybe $ const $ return $ KikiSuccess (uids,[])) has_torid $ do |
1916 | wkun <- doDecrypt top | ||
1917 | |||
1918 | try wkun $ \wkun -> do | ||
1919 | 1861 | ||
1920 | let keyflags = keyFlags wkun (map packet $ flattenAllUids fname True uids) | 1862 | let keyflags = keyFlags wkun (map packet $ flattenAllUids fname True uids) |
1921 | uid = UserIDPacket idstr | 1863 | uid = UserIDPacket idstr |
@@ -2482,7 +2424,7 @@ initializeMissingPEMFiles operation ctx grip mwk transcode db = do | |||
2482 | guard $ case r of | 2424 | guard $ case r of |
2483 | ExternallyGeneratedFile -> True | 2425 | ExternallyGeneratedFile -> True |
2484 | _ -> False | 2426 | _ -> False |
2485 | return (f,subspec,map fst ms,stream,cmd) | 2427 | return (ArgFile f,subspec,map fst ms,stream,cmd) |
2486 | 2428 | ||
2487 | try v $ \(db,import_rs) -> do | 2429 | try v $ \(db,import_rs) -> do |
2488 | 2430 | ||
@@ -3143,25 +3085,6 @@ data KeyData = KeyData { keyMappedPacket :: MappedPacket -- main key | |||
3143 | 3085 | ||
3144 | type KeyDB = Map.Map KeyKey KeyData | 3086 | type KeyDB = Map.Map KeyKey KeyData |
3145 | 3087 | ||
3146 | origin :: Packet -> Int -> OriginFlags | ||
3147 | origin p n = OriginFlags ispub n | ||
3148 | where | ||
3149 | ispub = case p of | ||
3150 | SecretKeyPacket {} -> False | ||
3151 | _ -> True | ||
3152 | |||
3153 | mappedPacket :: FilePath -> Packet -> MappedPacket | ||
3154 | mappedPacket filename p = MappedPacket | ||
3155 | { packet = p | ||
3156 | , locations = Map.singleton filename (origin p (-1)) | ||
3157 | } | ||
3158 | |||
3159 | mappedPacketWithHint :: FilePath -> Packet -> Int -> MappedPacket | ||
3160 | mappedPacketWithHint filename p hint = MappedPacket | ||
3161 | { packet = p | ||
3162 | , locations = Map.singleton filename (origin p hint) | ||
3163 | } | ||
3164 | |||
3165 | uidkey :: Packet -> String | 3088 | uidkey :: Packet -> String |
3166 | uidkey (UserIDPacket str) = str | 3089 | uidkey (UserIDPacket str) = str |
3167 | 3090 | ||
diff --git a/lib/Kiki.hs b/lib/Kiki.hs index 25c98e2..c042540 100644 --- a/lib/Kiki.hs +++ b/lib/Kiki.hs | |||
@@ -2,9 +2,10 @@ | |||
2 | {-# LANGUAGE OverloadedStrings #-} | 2 | {-# LANGUAGE OverloadedStrings #-} |
3 | module Kiki where | 3 | module Kiki where |
4 | 4 | ||
5 | import Control.Exception | ||
6 | import Control.Applicative | 5 | import Control.Applicative |
7 | import Control.Arrow | 6 | import Control.Arrow |
7 | import Control.Concurrent | ||
8 | import Control.Exception | ||
8 | import Control.Monad | 9 | import Control.Monad |
9 | import Data.ASN1.BinaryEncoding | 10 | import Data.ASN1.BinaryEncoding |
10 | import Data.ASN1.Encoding | 11 | import Data.ASN1.Encoding |
@@ -22,6 +23,7 @@ import System.FilePath.Posix | |||
22 | import System.IO | 23 | import System.IO |
23 | import System.IO.Temp | 24 | import System.IO.Temp |
24 | import System.IO.Error | 25 | import System.IO.Error |
26 | import System.Posix.IO as Posix (createPipe) | ||
25 | import System.Posix.User | 27 | import System.Posix.User |
26 | import System.Process | 28 | import System.Process |
27 | import System.Posix.Files | 29 | import System.Posix.Files |
@@ -37,6 +39,7 @@ import qualified Data.ByteString.Lazy.Char8 as Char8 | |||
37 | import qualified Data.Map.Strict as Map | 39 | import qualified Data.Map.Strict as Map |
38 | import qualified SSHKey as SSH | 40 | import qualified SSHKey as SSH |
39 | 41 | ||
42 | import GnuPGAgent (Query(..)) | ||
40 | import CommandLine | 43 | import CommandLine |
41 | import KeyRing | 44 | import KeyRing |
42 | import DotLock | 45 | import DotLock |
@@ -138,9 +141,9 @@ importAndRefresh root cmn = do | |||
138 | 141 | ||
139 | let passfd = cap_passfd cmn | 142 | let passfd = cap_passfd cmn |
140 | 143 | ||
141 | pwds <- | 144 | (torgen,pwds) <- |
142 | if gotsec | 145 | if gotsec |
143 | then return [] | 146 | then return (Generate 0 $ GenRSA $ 1024 `div` 8, []) |
144 | else do | 147 | else do |
145 | {- ssh-keygen to create master key... | 148 | {- ssh-keygen to create master key... |
146 | let mkpath = home ++ "/master-key" | 149 | let mkpath = home ++ "/master-key" |
@@ -154,36 +157,47 @@ importAndRefresh root cmn = do | |||
154 | HomeSec | 157 | HomeSec |
155 | ( encode $ Message [mk { is_subkey = False }] ) | 158 | ( encode $ Message [mk { is_subkey = False }] ) |
156 | -} | 159 | -} |
157 | master_un <- (\k -> k { is_subkey = False }) <$> generateKey (GenRSA $ 4096 `div` 8 ) | 160 | master_un <- (\k -> MappedPacket (k { is_subkey = False }) Map.empty) <$> generateKey (GenRSA $ 4096 `div` 8 ) |
161 | tor_un <- generateKey (GenRSA $ 1024 `div` 8 ) | ||
162 | (read_tor,write_tor) <- Posix.createPipe | ||
163 | do rs <- writeKeyToFile (streaminfo { typ = PEMFile, access = Sec, spill = KF_Match "tor", fill = KF_All }) (FileDesc write_tor) tor_un | ||
164 | -- outputReport $ map (first show) rs | ||
165 | return () | ||
158 | let default_cipher = (CAST5 {- AES128 -}, IteratedSaltedS2K SHA1 4073382889203176146 7864320) | 166 | let default_cipher = (CAST5 {- AES128 -}, IteratedSaltedS2K SHA1 4073382889203176146 7864320) |
159 | ctx = InputFileContext secring pubring | 167 | ctx = InputFileContext secring pubring |
168 | main_passwds = withAgent $ do pfd <- maybeToList passfd | ||
169 | return $ PassphraseSpec Nothing Nothing pfd | ||
160 | passwordop = KeyRingOperation | 170 | passwordop = KeyRingOperation |
161 | { opFiles = Map.empty | 171 | { opFiles = Map.empty |
162 | -- TODO: ask agent for new passphrase | 172 | -- TODO: ask agent for new passphrase |
163 | , opPassphrases = do pfd <- maybeToList passfd | 173 | , opPassphrases = main_passwds |
164 | return $ PassphraseSpec Nothing Nothing pfd | ||
165 | , opHome = homespec | 174 | , opHome = homespec |
166 | , opTransforms = [] | 175 | , opTransforms = [] |
167 | } | 176 | } |
168 | transcoder <- makeMemoizingDecrypter passwordop ctx Map.empty | 177 | let uidentry = Map.singleton (keykey $ packet master_un) |
169 | master0 <- transcoder default_cipher $ MappedPacket master_un Map.empty | 178 | $ master_un { packet = Query (packet master_un) |
179 | (torUIDFromKey tor_un) | ||
180 | Nothing | ||
181 | } | ||
182 | transcoder <- makeMemoizingDecrypter passwordop ctx (Just master_un, uidentry) | ||
183 | master0 <- transcoder default_cipher master_un | ||
170 | case master0 of | 184 | case master0 of |
171 | KikiSuccess master -> do | 185 | KikiSuccess master -> do |
172 | mkdirFor secring | 186 | mkdirFor secring |
173 | writeInputFileL ctx | 187 | writeInputFileL ctx |
174 | HomeSec | 188 | HomeSec |
175 | $ encode $ Message [master { is_subkey = False}] | 189 | $ encode $ Message [master] |
176 | putStrLn "Wrote master key" | 190 | putStrLn "Wrote master key" |
177 | return [PassphraseMemoizer transcoder] | 191 | return (FileDesc read_tor, [PassphraseMemoizer transcoder]) |
178 | er -> do | 192 | er -> do |
179 | hPutStrLn stderr ("warning: " ++ errorString er) | 193 | hPutStrLn stderr ("warning: " ++ errorString er) |
180 | hPutStrLn stderr "warning: keys will not be encrypted."; | 194 | hPutStrLn stderr "warning: keys will not be encrypted."; |
181 | mkdirFor secring | 195 | mkdirFor secring |
182 | writeInputFileL ctx | 196 | writeInputFileL ctx |
183 | HomeSec | 197 | HomeSec |
184 | $ encode $ Message [master_un { is_subkey = False}] | 198 | $ encode $ Message [packet master_un] |
185 | putStrLn "Wrote master key" | 199 | putStrLn "Wrote master key" |
186 | return [] | 200 | return (Generate 0 (GenRSA $ 1024 `div` 8 ), []) |
187 | gotpub <- doesFileExist pubring | 201 | gotpub <- doesFileExist pubring |
188 | when (not gotpub) $ do | 202 | when (not gotpub) $ do |
189 | mkdirFor pubring | 203 | mkdirFor pubring |
@@ -233,7 +247,14 @@ importAndRefresh root cmn = do | |||
233 | { opFiles = Map.fromList $ | 247 | { opFiles = Map.fromList $ |
234 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) | 248 | [ ( HomeSec, buildStreamInfo KF_All KeyRingFile ) |
235 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) | 249 | , ( HomePub, (buildStreamInfo KF_All KeyRingFile) { access = Pub } ) |
236 | , ( Generate 0 (GenRSA (1024 `div` 8)), strm { spill = KF_Match "tor" }) | 250 | , ( torgen , case torgen of |
251 | FileDesc _ -> StreamInfo { typ = PEMFile | ||
252 | , fill = KF_Match "tor" | ||
253 | , spill = KF_Match "tor" | ||
254 | , access = Sec | ||
255 | , initializer = NoCreate | ||
256 | , transforms = [] } | ||
257 | _ -> strm { spill = KF_Match "tor" }) | ||
237 | , ( Generate 1 (GenRSA (1024 `div` 8)), strm { spill = KF_Match "ipsec" }) | 258 | , ( Generate 1 (GenRSA (1024 `div` 8)), strm { spill = KF_Match "ipsec" }) |
238 | , ( ArgFile sshcpath, (peminfo 2048 "ssh-client") ) | 259 | , ( ArgFile sshcpath, (peminfo 2048 "ssh-client") ) |
239 | , ( ArgFile sshspath, (peminfo 2048 "ssh-server") ) | 260 | , ( ArgFile sshspath, (peminfo 2048 "ssh-server") ) |
diff --git a/lib/PacketTranscoder.hs b/lib/PacketTranscoder.hs index 651b00c..07f235c 100644 --- a/lib/PacketTranscoder.hs +++ b/lib/PacketTranscoder.hs | |||
@@ -1,5 +1,6 @@ | |||
1 | {-# LANGUAGE TupleSections #-} | 1 | {-# LANGUAGE TupleSections #-} |
2 | {-# LANGUAGE OverloadedStrings #-} | 2 | {-# LANGUAGE OverloadedStrings #-} |
3 | {-# LANGUAGE PatternGuards #-} | ||
3 | module PacketTranscoder where | 4 | module PacketTranscoder where |
4 | 5 | ||
5 | import Control.Monad | 6 | import Control.Monad |
@@ -11,7 +12,8 @@ import Data.OpenPGP.Util | |||
11 | import GnuPGAgent | 12 | import GnuPGAgent |
12 | import qualified Data.ByteString as S | 13 | import qualified Data.ByteString as S |
13 | import qualified Data.ByteString.Char8 as S8 | 14 | import qualified Data.ByteString.Char8 as S8 |
14 | import qualified Data.Map as Map | 15 | import Data.Map as Map (Map) |
16 | import qualified Data.Map as Map | ||
15 | import qualified Data.Traversable as Traversable | 17 | import qualified Data.Traversable as Traversable |
16 | import System.IO ( stderr) | 18 | import System.IO ( stderr) |
17 | import System.Posix.IO ( fdToHandle ) | 19 | import System.Posix.IO ( fdToHandle ) |
@@ -92,9 +94,9 @@ cachedContents maybePrompt ctx fd = do | |||
92 | 94 | ||
93 | 95 | ||
94 | makeMemoizingDecrypter :: KeyRingOperation -> InputFileContext | 96 | makeMemoizingDecrypter :: KeyRingOperation -> InputFileContext |
95 | -> Map.Map KeyKey (OriginMapped Query) | 97 | -> (Maybe MappedPacket, Map.Map KeyKey (OriginMapped Query)) |
96 | -> IO PacketTranscoder | 98 | -> IO PacketTranscoder |
97 | makeMemoizingDecrypter operation ctx keys = do | 99 | makeMemoizingDecrypter operation ctx (workingkey,keys) = do |
98 | if null chains then do | 100 | if null chains then do |
99 | -- (*) Notice we do not pass ctx to resolveForReport. | 101 | -- (*) Notice we do not pass ctx to resolveForReport. |
100 | -- This is because the merge function does not currently use a context | 102 | -- This is because the merge function does not currently use a context |
@@ -144,9 +146,14 @@ makeMemoizingDecrypter operation ctx keys = do | |||
144 | -> IO (KikiCondition Packet) | 146 | -> IO (KikiCondition Packet) |
145 | doDecrypt unkeysRef pws defpw agent_requested (dest_alg,dest_s2k) mp0 = do | 147 | doDecrypt unkeysRef pws defpw agent_requested (dest_alg,dest_s2k) mp0 = do |
146 | unkeys <- readIORef unkeysRef | 148 | unkeys <- readIORef unkeysRef |
147 | let (mp,qry) = fromMaybe (mp0,Query (packet mp0) "anonymous2" Nothing) $ do | 149 | let (mp,qry) = fromMaybe (mp0,Query (packet mp0) "anonymous2" Nothing) |
148 | k <- Map.lookup kk keys | 150 | $ mplus (do k <- Map.lookup kk keys |
149 | return (mergeKeyPacket "decrypt" mp0 (fmap queryPacket k), packet k) | 151 | return (mergeKeyPacket "decrypt" mp0 (fmap queryPacket k), packet k)) |
152 | (do guard $ is_subkey (packet mp0) | ||
153 | working <- fmap packet workingkey | ||
154 | q <- fmap packet $ Map.lookup (keykey working) keys | ||
155 | return (mp0, Query (packet mp0) (queryUID q) (Just working))) | ||
156 | |||
150 | wk = packet mp0 | 157 | wk = packet mp0 |
151 | kk = keykey wk | 158 | kk = keykey wk |
152 | fs = Map.keys $ locations mp | 159 | fs = Map.keys $ locations mp |
@@ -161,39 +168,60 @@ makeMemoizingDecrypter operation ctx keys = do | |||
161 | -- in the 'locations' field, so this would effectively | 168 | -- in the 'locations' field, so this would effectively |
162 | -- allow you to run 'decryptIt' on an unencrypted public key | 169 | -- allow you to run 'decryptIt' on an unencrypted public key |
163 | -- to obtain it's secret key. | 170 | -- to obtain it's secret key. |
164 | (pw,wants_retry) <- getpw (if count>1 then AskAgain "Bad pasphrase." else Ask,qry) | 171 | (pw,wants_retry) <- getpw (count,qry) |
165 | let wkun = fromMaybe wk $ do | 172 | let wkun = fromMaybe wk $ do |
166 | guard $ symmetric_algorithm (packet mp) /= Unencrypted | 173 | guard $ symmetric_algorithm (packet mp) /= Unencrypted |
167 | decryptSecretKey pw (packet mp) | 174 | decryptSecretKey pw (packet mp) |
168 | 175 | ||
176 | retryOrFail | ||
177 | | Just clear <- wants_retry = if count < 4 | ||
178 | then tries (count+1) getpw recurse | ||
179 | else clear >> recurse | ||
180 | | otherwise = recurse | ||
181 | |||
169 | case symmetric_algorithm wkun of | 182 | case symmetric_algorithm wkun of |
170 | 183 | ||
171 | Unencrypted -> do | 184 | Unencrypted -> do |
172 | writeIORef unkeysRef (Map.insert kk wkun unkeys) | 185 | writeIORef unkeysRef (Map.insert kk wkun unkeys) |
173 | ek <- if dest_alg==Unencrypted | 186 | ek <- case dest_alg of |
174 | then return $ Just wkun | 187 | Unencrypted -> return $ Just wkun |
175 | else encryptSecretKey pw dest_s2k dest_alg wkun | 188 | _ -> encryptSecretKey pw dest_s2k dest_alg wkun |
176 | case ek of | 189 | case ek of |
177 | Nothing | wants_retry && count<3 -> tries (count+1) getpw recurse | 190 | Nothing -> retryOrFail |
178 | Nothing -> recurse | 191 | Just wken -> return $ KikiSuccess wken |
179 | Just wken -> return $ KikiSuccess wken | ||
180 | 192 | ||
181 | _ -> recurse | 193 | _ -> retryOrFail |
182 | 194 | ||
183 | getpws = (map (const . fmap (,False)) $ mapMaybe (`Map.lookup` pws) fs ++ maybeToList defpw) ++ [ agentpw | agent_requested ] | 195 | getpws = (map (const . fmap (,Nothing)) $ mapMaybe (`Map.lookup` pws) fs ++ maybeToList defpw) ++ [ agentpw | agent_requested ] |
184 | 196 | ||
185 | -- TODO: First we should try the master key with AskNot. | 197 | agentpw (count,qry) = do |
186 | -- If that fails, we should try the subkey. | ||
187 | agentpw (ask,qry) = do | ||
188 | s <- session | 198 | s <- session |
189 | fromMaybe (return ("",False)) $ do | 199 | fromMaybe (return ("",Nothing)) $ do |
190 | s <- s | 200 | s <- s |
191 | Just $ do | 201 | Just $ do |
192 | case ask of AskAgain _ -> clearPassphrase s (queryPacket qry) | 202 | let (firsttime,maink) | Just k <- (queryMainKey qry) = (2,k) |
193 | _ -> return () | 203 | | otherwise = (1,error "bug in makeMemoizingDecrypter") |
194 | mbpw <- getPassphrase s ask qry | 204 | |
205 | alg = symmetric_algorithm (queryPacket qry) | ||
206 | |||
207 | ask | count<firsttime = AskNot | ||
208 | | count>firsttime = AskAgain "Bad passphrase" | ||
209 | | count==firsttime = initial_ask | ||
210 | where | ||
211 | initial_ask | Unencrypted <- alg = AskNew | ||
212 | | otherwise = AskExisting | ||
213 | |||
214 | actual_qry | count<firsttime = qry { queryPacket = maink, queryMainKey = Nothing } | ||
215 | | otherwise = qry | ||
216 | |||
217 | let clear | count > firsttime = clearPassphrase s (queryPacket qry) | ||
218 | | otherwise = return () | ||
219 | clear | ||
220 | let sanitizeQry qry = (fingerprint $ queryPacket qry, queryUID qry, fmap fingerprint $ queryMainKey qry) | ||
221 | putStrLn $ "(count,firsttime,ask,qry,actual_qry)="++show (count,firsttime,ask,sanitizeQry qry, sanitizeQry actual_qry) | ||
222 | mbpw <- getPassphrase s ask actual_qry | ||
195 | quit s | 223 | quit s |
196 | return ( maybe "" S8.pack mbpw, True) | 224 | return ( maybe "" S8.pack mbpw, guard (ask /= AskNew) >> Just clear ) |
197 | 225 | ||
198 | if symmetric_algorithm wk == dest_alg | 226 | if symmetric_algorithm wk == dest_alg |
199 | && ( symmetric_algorithm wk == Unencrypted || s2k wk == dest_s2k ) | 227 | && ( symmetric_algorithm wk == Unencrypted || s2k wk == dest_s2k ) |
@@ -202,3 +230,66 @@ makeMemoizingDecrypter operation ctx keys = do | |||
202 | (return . KikiSuccess) | 230 | (return . KikiSuccess) |
203 | $ Map.lookup kk unkeys | 231 | $ Map.lookup kk unkeys |
204 | 232 | ||
233 | keyQueries :: Maybe String -> Map InputFile (StreamInfo,Message) -> (Maybe MappedPacket, Map KeyKey (OriginMapped Query)) | ||
234 | keyQueries grip ringPackets = (mwk, fmap makeQuery keys) | ||
235 | where | ||
236 | makeQuery (maink,mp,us) = mp { packet = q } | ||
237 | where q = Query { queryPacket = packet mp | ||
238 | , queryUID = concat $ take 1 $ Map.keys $ Map.union us (getUIDS maink) | ||
239 | , queryMainKey = if is_subkey (packet mp) | ||
240 | then maink `mplus` fmap packet mwk | ||
241 | else Nothing | ||
242 | } | ||
243 | |||
244 | getUIDS maink = fromMaybe Map.empty $ do | ||
245 | k <- maink | ||
246 | (_,_,mus) <- Map.lookup (keykey k) keys | ||
247 | return mus | ||
248 | |||
249 | -- | mwk | ||
250 | -- first master key matching the provided grip | ||
251 | -- (the m is for "MappedPacket", wk for working key) | ||
252 | mwk :: Maybe MappedPacket | ||
253 | mwk = listToMaybe $ do | ||
254 | fp <- maybeToList grip | ||
255 | let matchfp mp | ||
256 | | not (is_subkey p) && matchpr fp p == fp = Just mp | ||
257 | | otherwise = Nothing | ||
258 | where p = packet mp | ||
259 | Map.elems $ Map.mapMaybe matchfp $ fmap (\(_,p,_) -> p) $ keys | ||
260 | |||
261 | keys = Map.foldl slurpkeys Map.empty | ||
262 | $ Map.mapWithKey filterSecrets ringPackets | ||
263 | where | ||
264 | filterSecrets :: InputFile -> (a,Message) -> [[MappedPacket]] | ||
265 | filterSecrets f (_,Message ps) = keygroups | ||
266 | -- filter (isSecretKey . packet) mps | ||
267 | where | ||
268 | mps = zipWith (mappedPacketWithHint fname) ps [1..] | ||
269 | fname = resolveForReport Nothing f -- (Just ctx) f | ||
270 | keygroups = dropWhile (not . isSecretKey . packet . head) | ||
271 | $ groupBy (const $ not . isSecretKey . packet) mps | ||
272 | slurpkeys :: (Map KeyKey (Maybe Packet,MappedPacket,Map String [Packet])) | ||
273 | -> [[MappedPacket]] | ||
274 | -> (Map KeyKey (Maybe Packet, MappedPacket,Map String [Packet])) | ||
275 | slurpkeys m pss = Map.unionWith combineKeyKey m m2 | ||
276 | where | ||
277 | |||
278 | m2 :: Map.Map KeyKey (Maybe Packet, MappedPacket, (Map.Map String [Packet])) | ||
279 | m2 = Map.fromList | ||
280 | $ drop 1 | ||
281 | $ scanl' build ([],(Nothing,error "bug in PacketTranscoder (1)", error "bug in PacketTranscoder (2)")) pss | ||
282 | where | ||
283 | build (_,(main0,_,_)) ps = (kk,(main,kp,uidmap ps')) | ||
284 | where | ||
285 | main | is_subkey (packet kp) = main0 | ||
286 | | otherwise = Just $ packet kp | ||
287 | (kpkt,ps') = splitAt 1 ps | ||
288 | kp = head kpkt | ||
289 | kk = keykey . packet $ kp | ||
290 | combineKeyKey (master1,mp,um) (master2,mp2,um2) = (master1 `mplus` master2,mp,Map.unionWith (++) um um2) | ||
291 | uidmap ps = um2 | ||
292 | where | ||
293 | ugs = dropWhile (not . isUserID . packet .head) $ groupBy (const $ not . isUserID . packet) ps | ||
294 | um2 = Map.fromList | ||
295 | $ map (\(MappedPacket (UserIDPacket s) _:sigs)->(s,takeWhile isSignaturePacket $ map packet sigs)) ugs | ||
diff --git a/lib/Types.hs b/lib/Types.hs index 9aa0340..767ee98 100644 --- a/lib/Types.hs +++ b/lib/Types.hs | |||
@@ -1,12 +1,13 @@ | |||
1 | {-# LANGUAGE DeriveFunctor #-} | 1 | {-# LANGUAGE DeriveFunctor #-} |
2 | module Types where | 2 | module Types where |
3 | 3 | ||
4 | import Data.Map as Map (Map) | 4 | import Data.Map as Map (Map) |
5 | import qualified Data.Map as Map | ||
5 | import Data.OpenPGP | 6 | import Data.OpenPGP |
6 | import Data.OpenPGP.Util | 7 | import Data.OpenPGP.Util |
7 | import FunctorToMaybe | 8 | import FunctorToMaybe |
8 | import qualified System.Posix.Types as Posix | ||
9 | import qualified Data.ByteString.Lazy as L | 9 | import qualified Data.ByteString.Lazy as L |
10 | import qualified System.Posix.Types as Posix | ||
10 | 11 | ||
11 | -- | This type describes an idempotent transformation (merge or import) on a | 12 | -- | This type describes an idempotent transformation (merge or import) on a |
12 | -- set of GnuPG keyrings and other key files. | 13 | -- set of GnuPG keyrings and other key files. |
@@ -199,6 +200,26 @@ data OriginMapped a = MappedPacket | |||
199 | instance Functor OriginMapped where | 200 | instance Functor OriginMapped where |
200 | fmap f (MappedPacket x ls) = MappedPacket (f x) ls | 201 | fmap f (MappedPacket x ls) = MappedPacket (f x) ls |
201 | 202 | ||
203 | origin :: Packet -> Int -> OriginFlags | ||
204 | origin p n = OriginFlags ispub n | ||
205 | where | ||
206 | ispub = case p of | ||
207 | SecretKeyPacket {} -> False | ||
208 | _ -> True | ||
209 | |||
210 | mappedPacket :: FilePath -> Packet -> MappedPacket | ||
211 | mappedPacket filename p = MappedPacket | ||
212 | { packet = p | ||
213 | , locations = Map.singleton filename (origin p (-1)) | ||
214 | } | ||
215 | |||
216 | mappedPacketWithHint :: FilePath -> Packet -> Int -> MappedPacket | ||
217 | mappedPacketWithHint filename p hint = MappedPacket | ||
218 | { packet = p | ||
219 | , locations = Map.singleton filename (origin p hint) | ||
220 | } | ||
221 | |||
222 | |||
202 | -- | This type is used to indicate success or failure | 223 | -- | This type is used to indicate success or failure |
203 | -- and in the case of success, return the computed object. | 224 | -- and in the case of success, return the computed object. |
204 | -- The 'FunctorToMaybe' class is implemented to facilitate | 225 | -- The 'FunctorToMaybe' class is implemented to facilitate |
@@ -252,6 +273,11 @@ isKey (PublicKeyPacket {}) = True | |||
252 | isKey (SecretKeyPacket {}) = True | 273 | isKey (SecretKeyPacket {}) = True |
253 | isKey _ = False | 274 | isKey _ = False |
254 | 275 | ||
276 | isSecretKey :: Packet -> Bool | ||
277 | isSecretKey (SecretKeyPacket {}) = True | ||
278 | isSecretKey _ = False | ||
279 | |||
280 | |||
255 | isUserID :: Packet -> Bool | 281 | isUserID :: Packet -> Bool |
256 | isUserID (UserIDPacket {}) = True | 282 | isUserID (UserIDPacket {}) = True |
257 | isUserID _ = False | 283 | isUserID _ = False |
@@ -260,4 +286,12 @@ isTrust :: Packet -> Bool | |||
260 | isTrust (TrustPacket {}) = True | 286 | isTrust (TrustPacket {}) = True |
261 | isTrust _ = False | 287 | isTrust _ = False |
262 | 288 | ||
289 | -- matchpr computes the fingerprint of the given key truncated to | ||
290 | -- be the same lenght as the given fingerprint for comparison. | ||
291 | -- | ||
292 | -- matchpr fp = Data.List.Extra.takeEnd (length fp) | ||
293 | -- | ||
294 | matchpr :: String -> Packet -> String | ||
295 | matchpr fp k = reverse $ zipWith const (reverse (fingerprint k)) fp | ||
296 | |||
263 | 297 | ||