summaryrefslogtreecommitdiff
path: root/keys.hs
blob: cdd35923a8c1eda268aa1ccd6b7f09f56d44c870 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
module Main where

import Data.Binary
import Data.OpenPGP
import qualified Data.ByteString.Lazy as L
import Control.Monad
import Text.Show.Pretty
import Data.List
import Data.OpenPGP.CryptoAPI
import Data.Ord
import Data.Maybe
import Data.Bits

getPackets :: IO [Packet]
getPackets = do
    input <- L.getContents
    case decodeOrFail input of
        Right (_,_,Message pkts) -> return pkts
        Left  (_,_,_)            -> return []


isKey (PublicKeyPacket {}) = True
isKey (SecretKeyPacket {}) = True
isKey _                    = False

isUserID (UserIDPacket {}) = True
isUserID _                 = False

isEmbeddedSignature (EmbeddedSignaturePacket {}) = True
isEmbeddedSignature _                            = False

issuer (IssuerPacket issuer) = Just issuer
issuer _                     = Nothing
backsig (EmbeddedSignaturePacket s) = Just s
backsig _                           = Nothing

isSubkeySignature (SubkeySignature {}) = True
isSubkeySignature _                    = False

usage (NotationDataPacket
        { human_readable = True
        , notation_name  = "usage@"
        , notation_value = u
        }) = Just u
usage _    = Nothing

verifyBindings keys []      = []
verifyBindings keys nonkeys = top ++ filter isSubkeySignature embedded
 where
    verified = do
        sig <- signatures (Message nonkeys)
        let v = verify (Message keys) sig
        guard (not . null $ signatures_over v)
        return v
    (top,_) = partition isSubkeySignature verified
    embedded = do
        sub <- top
        let sigover = signatures_over sub
            unhashed = sigover >>= unhashed_subpackets
            subsigs = mapMaybe backsig unhashed
        sig <- signatures (Message ([topkey sub,subkey sub]++subsigs))
        let v = verify (Message [subkey sub]) sig
        guard (not . null $ signatures_over v)
        return v

smallpr k = drop 24 $ fingerprint k

disjoint_fp ks = transpose grouped
 where
    grouped    = groupBy samepr . sortBy (comparing smallpr) $ ks
    samepr a b = smallpr a == smallpr b

getBindings ::
  [Packet]
  -> [(Word8,
       (Packet, Packet),
       [String],
       [SignatureSubpacket],
       [Packet])]
getBindings pkts = do
    let (keys,nonkeys) = partition isKey pkts
    keys <- disjoint_fp (keys)
    b <- verifyBindings keys pkts -- nonkeys
    i <- map signature_issuer (signatures_over b)
    i <- maybeToList i
    who <- maybeToList $ find_key fingerprint (Message keys) i
    let (code,claimants) = 
            case () of
                _ | who == topkey b -> (1,[])
                _ | who == subkey b -> (2,[])
                _                   -> (0,[who])
    let hashed = signatures_over b >>= hashed_subpackets
        kind = guard (code==1) >> hashed >>= maybeToList . usage
    return (code,(topkey b,subkey b), kind, hashed,claimants)

accBindings ::
  Bits t =>
  [(t, (Packet, Packet), [a], [a1], [a2])]
  -> [(t, (Packet, Packet), [a], [a1], [a2])]
accBindings bs = as
  where
    gs = groupBy samePair . sortBy (comparing bindingPair) $ bs
    as = map (foldl1 combine) gs
    bindingPair (_,p,_,_,_) = pub2 p
      where
        pub2 (a,b) = (pub a, pub b)
        pub a = fingerprint_material a
    samePair a b = bindingPair a == bindingPair b
    combine (ac,p,akind,ahashed,aclaimaints)
            (bc,_,bkind,bhashed,bclaimaints)
        = (ac .|. bc,p,akind++bkind,ahashed++bhashed,aclaimaints++bclaimaints)

listKeys pkts = do
    let bs = getBindings pkts
        as = accBindings bs
        defaultkind []    = "subkey"
        defaultkind (k:_) = k
        kinds = map (\(_,_,k,_,_)->defaultkind k) as
        kindwidth = maximum $ map length kinds
        kindcol = min 20 kindwidth
        sameMaster (_,(a,_),_,_,_) (_,(b,_),_,_,_) = fingerprint_material a==fingerprint_material b
        gs = groupBy sameMaster as
    subs <- gs
    let (code,(top,sub), kind, hashed,claimants):_ = subs
        subkeys = do
            (code,(top,sub), kind, hashed,claimants) <- subs
            let ar = case code of
                        0 -> " ??? "
                        1 -> " --> "
                        2 -> " <-- "
                        3 -> " <-> "
                formkind = take kindcol $ defaultkind kind ++ repeat ' '
            " "++smallpr top ++ ar ++ formkind++" "++ fingerprint sub ++"\n"
    "gpg " ++ fingerprint top ++ "\n" ++ subkeys ++ "\n"
    


main = do
    pkts <- getPackets
    putStrLn $ listKeys pkts
    return ()