summaryrefslogtreecommitdiff
path: root/lib/KeyRing.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2016-08-29 01:15:25 -0400
committerjoe <joe@jerkface.net>2016-08-29 01:15:25 -0400
commit1eff837423de69ece2a85430a7ad433b7c1a504a (patch)
treec2c7d6e83e9589de72b29924f6cb2354107d0d0e /lib/KeyRing.hs
parent7a579e7b82a2f5707af77f4a7101ce72e57635ac (diff)
Better gpg-agent support.
Diffstat (limited to 'lib/KeyRing.hs')
-rw-r--r--lib/KeyRing.hs143
1 files changed, 33 insertions, 110 deletions
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 #-}
26module KeyRing 27module 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_ )
186import Foreign.Storable 188import Foreign.Storable
187#endif 189#endif
188import System.FilePath ( takeDirectory ) 190import System.FilePath ( takeDirectory )
189import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr, stderr) 191import System.IO (hPutStrLn,withFile,IOMode(..), Handle, hPutStr, stderr, hClose)
190import Data.IORef 192import Data.IORef
191import System.Posix.IO ( fdToHandle ) 193import System.Posix.IO ( fdToHandle )
192import qualified Data.Traversable as Traversable 194import 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--
632matchpr :: String -> Packet -> String
633matchpr fp k = reverse $ zipWith const (reverse (fingerprint k)) fp
634
635keyFlags :: t -> [Packet] -> [SignatureSubpacket] 629keyFlags :: t -> [Packet] -> [SignatureSubpacket]
636keyFlags wkun uids = keyFlags0 wkun (filter isSignaturePacket uids) 630keyFlags 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
1183writeStamped :: InputFileContext -> InputFile -> Posix.EpochTime -> String -> IO () 1177writeStamped :: InputFileContext -> InputFile -> Posix.EpochTime -> String -> IO ()
1184writeStamped ctx f stamp str = writeStamped0 ctx f stamp (either hPutStr writeFile) str 1178writeStamped ctx f stamp str = writeStamped0 ctx f stamp (either (\h x-> hPutStr h x >> hClose h) writeFile) str
1185 1179
1186getInputFileTime :: InputFileContext -> InputFile -> IO CTime 1180getInputFileTime :: InputFileContext -> InputFile -> IO CTime
1187getInputFileTime ctx (Pipe fdr fdw) = do 1181getInputFileTime 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)]))
1238importSecretKey transcode db' tup = do 1232importSecretKey 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
1348isSecretKey :: Packet -> Bool
1349isSecretKey (SecretKeyPacket {}) = True
1350isSecretKey _ = 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)]))
1370buildKeyDB ctx grip0 keyring = do 1360buildKeyDB 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
1595torhash :: Packet -> String 1534torhash :: Packet -> String
1596torhash key = fromMaybe "" $ derToBase32 <$> derRSA key 1535torhash key = fromMaybe "" $ derToBase32 <$> derRSA key
1597 1536
1537torUIDFromKey :: Packet -> String
1538torUIDFromKey key = "Anonymous <root@" ++ take 16 (torhash key) ++ ".onion>"
1539
1598derToBase32 :: ByteString -> String 1540derToBase32 :: ByteString -> String
1599derToBase32 = map toLower . base32 . sha1 1541derToBase32 = map toLower . base32 . sha1
1600 where 1542 where
@@ -1827,19 +1769,19 @@ readSecretPEMFile fname = do
1827doImport 1769doImport
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)]))
1832doImport transcode db (fname,subspec,ms,typ -> typ,_) = do 1774doImport 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)]))
1878doImportG transcode db m0 tags fname key = do 1820doImportG 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
1884insertSubkey transcode kk (KeyData top topsigs uids subs) tags fname key0 = do 1826insertSubkey 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
3144type KeyDB = Map.Map KeyKey KeyData 3086type KeyDB = Map.Map KeyKey KeyData
3145 3087
3146origin :: Packet -> Int -> OriginFlags
3147origin p n = OriginFlags ispub n
3148 where
3149 ispub = case p of
3150 SecretKeyPacket {} -> False
3151 _ -> True
3152
3153mappedPacket :: FilePath -> Packet -> MappedPacket
3154mappedPacket filename p = MappedPacket
3155 { packet = p
3156 , locations = Map.singleton filename (origin p (-1))
3157 }
3158
3159mappedPacketWithHint :: FilePath -> Packet -> Int -> MappedPacket
3160mappedPacketWithHint filename p hint = MappedPacket
3161 { packet = p
3162 , locations = Map.singleton filename (origin p hint)
3163 }
3164
3165uidkey :: Packet -> String 3088uidkey :: Packet -> String
3166uidkey (UserIDPacket str) = str 3089uidkey (UserIDPacket str) = str
3167 3090