summaryrefslogtreecommitdiff
path: root/keys.hs
blob: e5966ca7eb8a49ab02facd59c756c2d70a8ad860 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
{-# LANGUAGE ViewPatterns #-}
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 nonkeys = (top ++ filter isSubkeySignature embedded,othersigs)
 where
    verified = do
        sig <- signatures (Message nonkeys)
        let v = verify (Message keys) sig
        guard (not . null $ signatures_over v)
        return v
    (top,othersigs) = 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

grip k = drop (24+8) $ fingerprint k

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 <- fst $ verifyBindings keys pkts
    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 (k:_) hs = k
        defaultkind []    hs = maybe "subkey"
                                     id
                                     ( listToMaybe 
                                     . mapMaybe (fmap usageString . keyflags)
                                     $ hs)
        kinds = map (\(_,_,k,h,_)->defaultkind k h) as
        kindwidth = maximum $ map length kinds
        kindcol = min 20 kindwidth
        code (c,_,_,_,_) = -c
        ownerkey (_,(a,_),_,_,_) = a
        sameMaster (ownerkey->a) (ownerkey->b) = fingerprint_material a==fingerprint_material b
        gs = groupBy sameMaster (sortBy (comparing code) 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 hashed ++ repeat ' '
            " "++grip top ++ ar ++ formkind++" "++ fingerprint sub ++ "\n"
               -- ++ ppShow hashed
    "master-key " ++ fingerprint top ++ "\n" ++ subkeys ++ "\n"
    

{-
, KeyFlagsPacket
    { certify_keys = False
    , sign_data = True
    , encrypt_communication = True
    , encrypt_storage = True
    , split_key = False
    , authentication = True
    , group_key = False
    }
-}
data PGPKeyFlags =
    Special
    | Vouch
    | Sign
    | VouchSign
    | Communication
    | VouchCommunication
    | SignCommunication
    | VouchSignCommunication
    | Storage
    | VouchStorage
    | SignStorage
    | VouchSignStorage
    | Encrypt
    | VouchEncrypt
    | SignEncrypt
    | VouchSignEncrypt
 deriving (Eq,Show,Read,Enum)

usageString flgs = 
 case flgs of
    Special -> "special"
    Vouch -> "vouch"
    Sign -> "sign"
    VouchSign -> "vouch-sign"
    Communication -> "communication"
    VouchCommunication -> "vouch-communication"
    SignCommunication -> "sign-communication"
    VouchSignCommunication -> "vouch-sign-communication"
    Storage -> "storage"
    VouchStorage -> "vouch-storage"
    SignStorage -> "sign-storage"
    VouchSignStorage -> "vouch-sign-storage"
    Encrypt -> "encrypt"
    VouchEncrypt -> "vouch-encrypt"
    SignEncrypt -> "sign-encrypt"
    VouchSignEncrypt -> "vouch-sign-encrypt"


keyflags flgs@(KeyFlagsPacket {}) =
    Just . toEnum $
        (   bit 0x1 certify_keys 
        .|. bit 0x2 sign_data
        .|. bit 0x4 encrypt_communication
        .|. bit 0x8 encrypt_storage )     :: Maybe PGPKeyFlags
 where
    bit v f = if f flgs then v else 0
keyflags _ = Nothing


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