diff options
author | joe <joe@jerkface.net> | 2014-04-12 17:19:46 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-04-12 17:19:46 -0400 |
commit | a8da6f5843f3a5e6f7c975a746dea27adcf3907e (patch) | |
tree | e23abc66e741c336b9c70aca08f34af9bac4309e /KeyRing.hs | |
parent | 23dfd840a059877af0ff2538b2d46d85a0842ed9 (diff) |
moved merge function into KeyRing module
Diffstat (limited to 'KeyRing.hs')
-rw-r--r-- | KeyRing.hs | 218 |
1 files changed, 215 insertions, 3 deletions
@@ -8,9 +8,14 @@ import Control.Monad | |||
8 | import Data.Maybe | 8 | import Data.Maybe |
9 | import Data.Char | 9 | import Data.Char |
10 | import Data.List | 10 | import Data.List |
11 | import Control.Applicative ( (<$>) ) | 11 | import Data.OpenPGP |
12 | import System.Directory ( getHomeDirectory, doesFileExist ) | 12 | import Control.Applicative ( (<$>) ) |
13 | import Control.Arrow ( first, second ) | 13 | import System.Directory ( getHomeDirectory, doesFileExist ) |
14 | import Control.Arrow ( first, second ) | ||
15 | import Data.OpenPGP.Util ( fingerprint ) | ||
16 | import Data.ByteString.Lazy ( ByteString ) | ||
17 | import Text.Show.Pretty as PP ( ppShow ) | ||
18 | import qualified Data.Map as Map | ||
14 | 19 | ||
15 | import DotLock | 20 | import DotLock |
16 | 21 | ||
@@ -146,3 +151,210 @@ getHomeDir protohome = do | |||
146 | lookupEnv var = | 151 | lookupEnv var = |
147 | handleIO_ (return Nothing) $ fmap Just (getEnv var) | 152 | handleIO_ (return Nothing) $ fmap Just (getEnv var) |
148 | #endif | 153 | #endif |
154 | |||
155 | isKey (PublicKeyPacket {}) = True | ||
156 | isKey (SecretKeyPacket {}) = True | ||
157 | isKey _ = False | ||
158 | |||
159 | isUserID (UserIDPacket {}) = True | ||
160 | isUserID _ = False | ||
161 | |||
162 | isTrust (TrustPacket {}) = True | ||
163 | isTrust _ = False | ||
164 | |||
165 | |||
166 | data OriginFlags = OriginFlags { | ||
167 | originallyPublic :: Bool, | ||
168 | originalNum :: Int | ||
169 | } | ||
170 | deriving Show | ||
171 | type OriginMap = Map.Map FilePath OriginFlags | ||
172 | data MappedPacket = MappedPacket | ||
173 | { packet :: Packet | ||
174 | , usage_tag :: Maybe String | ||
175 | , locations :: OriginMap | ||
176 | } | ||
177 | |||
178 | type TrustMap = Map.Map FilePath Packet | ||
179 | type SigAndTrust = ( MappedPacket | ||
180 | , TrustMap ) -- trust packets | ||
181 | |||
182 | type KeyKey = [ByteString] | ||
183 | data SubKey = SubKey MappedPacket [SigAndTrust] | ||
184 | data 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 | |||
189 | type KeyDB = Map.Map KeyKey KeyData | ||
190 | |||
191 | origin :: Packet -> Int -> OriginFlags | ||
192 | origin p n = OriginFlags ispub n | ||
193 | where | ||
194 | ispub = case p of | ||
195 | SecretKeyPacket {} -> False | ||
196 | _ -> True | ||
197 | |||
198 | mappedPacket filename p = MappedPacket | ||
199 | { packet = p | ||
200 | , usage_tag = Nothing | ||
201 | , locations = Map.singleton filename (origin p (-1)) | ||
202 | } | ||
203 | |||
204 | keykey 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 | |||
210 | uidkey (UserIDPacket str) = str | ||
211 | |||
212 | merge :: KeyDB -> FilePath -> Message -> KeyDB | ||
213 | merge 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 | |||
237 | merge_ :: KeyDB -> FilePath -> [(Packet,Packet,(Packet,Map.Map FilePath Packet))] | ||
238 | -> KeyDB | ||
239 | merge_ 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 | |||