File: KeyringParser.hs

package info (click to toggle)
haskell-hopenpgp 2.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,028 kB
  • sloc: haskell: 6,478; sh: 21; makefile: 6
file content (294 lines) | stat: -rw-r--r-- 8,400 bytes parent folder | download
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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
-- KeyringParser.hs: OpenPGP (RFC4880) transferable keys parsing
-- Copyright © 2012-2020  Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).

{-# LANGUAGE CPP #-}

module Codec.Encryption.OpenPGP.KeyringParser
  (
 -- * Parsers
    parseAChunk
  , finalizeParsing
  , anyTK
  , UidOrUat(..)
  , splitUs
  , publicTK
  , secretTK
  , brokenTK
  , pkPayload
  , signature
  , signedUID
  , signedUAt
  , signedOrRevokedPubSubkey
  , brokenPubSubkey
  , rawOrSignedOrRevokedSecSubkey
  , brokenSecSubkey
  , skPayload
  , broken
 -- * Utilities
  , parseTKs
  ) where

import Control.Applicative ((<|>), many)
import Data.Maybe (catMaybes)

import Data.Text (Text)

import Codec.Encryption.OpenPGP.Ontology (isTrustPkt)
import Codec.Encryption.OpenPGP.Types
import Data.Conduit.OpenPGP.Keyring.Instances ()
import Text.ParserCombinators.Incremental.LeftBiasedLocal
  ( Parser
  , completeResults
  , concatMany
  , failure
  , feed
  , feedEof
  , inspect
  , satisfy
  )

parseAChunk ::
     (Monoid s, Show s)
  => Parser s r
  -> s
  -> ([(r, s)], Maybe (Maybe (r -> r), Parser s r))
  -> (([(r, s)], Maybe (Maybe (r -> r), Parser s r)), [r])
parseAChunk _ a ([], Nothing) = error $ "Failure before " ++ show a
parseAChunk op a (cr, Nothing) =
#if MIN_VERSION_incremental_parser(0,4,0)
  either error (\x -> (x, map fst cr)) (inspect (feed (mconcat (map snd cr) <> a) op))
parseAChunk _ a (_, Just (_, p)) = either error (\x -> (x, [])) (inspect (feed a p))
#else
  (inspect (feed (mconcat (map snd cr) <> a) op), map fst cr)
parseAChunk _ a (_, Just (_, p)) = (inspect (feed a p), [])
#endif

finalizeParsing ::
     Monoid s
  => ([(r, s)], Maybe (Maybe (r -> r), Parser s r))
  -> (([(r, s)], Maybe (Maybe (r -> r), Parser s r)), [r])
finalizeParsing ([], Nothing) = error "Unexpected finalization failure"
finalizeParsing (cr, Nothing) = (([], Nothing), map fst cr)
#if MIN_VERSION_incremental_parser(0,4,0)
finalizeParsing (_, Just (_, p)) = either error finalizeParsing (inspect (feedEof p))
#else
finalizeParsing (_, Just (_, p)) = finalizeParsing (inspect (feedEof p))
#endif

anyTK :: Bool -> Parser [Pkt] (Maybe TK)
anyTK True = publicTK True <|> secretTK True
anyTK False = publicTK False <|> secretTK False <|> brokenTK 6 <|> brokenTK 5

data UidOrUat
  = I Text
  | A [UserAttrSubPacket]
  deriving (Show)

splitUs ::
     [(UidOrUat, [SignaturePayload])]
  -> ([(Text, [SignaturePayload])], [([UserAttrSubPacket], [SignaturePayload])])
splitUs us = (is, as)
  where
    is = map unI (filter isI us)
    as = map unA (filter isA us)
    isI (I _, _) = True
    isI _ = False
    isA (A _, _) = True
    isA _ = False
    unI (I x, y) = (x, y)
    unI x = error $ "unI should never be called on " ++ show x
    unA (A x, y) = (x, y)
    unA x = error $ "unA should never be called on " ++ show x

publicTK, secretTK :: Bool -> Parser [Pkt] (Maybe TK)
publicTK intolerant = do
  pkp <- pkPayload
  pkpsigs <-
    concatMany
      (signature intolerant [KeyRevocationSig, SignatureDirectlyOnAKey])
  (uids, uats) <-
    fmap splitUs (many (signedUID intolerant <|> signedUAt intolerant)) -- FIXME: require >=1 uid if intolerant
  subs <- concatMany (pubsub intolerant)
  return $ Just (TK pkp pkpsigs uids uats subs)
  where
    pubsub True = signedOrRevokedPubSubkey True
    pubsub False = signedOrRevokedPubSubkey False <|> brokenPubSubkey

secretTK intolerant = do
  skp <- skPayload
  skpsigs <-
    concatMany
      (signature intolerant [KeyRevocationSig, SignatureDirectlyOnAKey])
  (uids, uats) <-
    fmap splitUs (many (signedUID intolerant <|> signedUAt intolerant)) -- FIXME: require >=1 uid if intolerant?
  subs <- concatMany (secsub intolerant)
  return $ Just (TK skp skpsigs uids uats subs)
  where
    secsub True = rawOrSignedOrRevokedSecSubkey True
    secsub False = rawOrSignedOrRevokedSecSubkey False <|> brokenSecSubkey

brokenTK :: Int -> Parser [Pkt] (Maybe TK)
brokenTK 6 = do
  _ <- broken 6
  _ <- many (signature False [KeyRevocationSig, SignatureDirectlyOnAKey])
  _ <- many (signedUID False <|> signedUAt False)
  _ <- concatMany (signedOrRevokedPubSubkey False <|> brokenPubSubkey)
  return Nothing
brokenTK 5 = do
  _ <- broken 5
  _ <- many (signature False [KeyRevocationSig, SignatureDirectlyOnAKey])
  _ <- many (signedUID False <|> signedUAt False)
  _ <- concatMany (rawOrSignedOrRevokedSecSubkey False <|> brokenSecSubkey)
  return Nothing
brokenTK _ = fail "Unexpected broken packet type"

pkPayload :: Parser [Pkt] (PKPayload, Maybe SKAddendum)
pkPayload = do
  pkpkts <- satisfy isPKP
  case pkpkts of
    [PublicKeyPkt p] -> return (p, Nothing)
    _ -> failure
  where
    isPKP [PublicKeyPkt _] = True
    isPKP _ = False

signature :: Bool -> [SigType] -> Parser [Pkt] [SignaturePayload]
signature intolerant rts =
  if intolerant
    then signature'
    else signature' <|> brokensig'
  where
    signature' = do
      spks <- satisfy (isSP intolerant)
      case spks of
        [SignaturePkt sp] ->
          return $!
          (if intolerant
             then id
             else filter isSP')
            [sp]
        _ -> failure
    brokensig' = const [] <$> broken 2
    isSP True [SignaturePkt sp@SigV3 {}] = isSP' sp
    isSP True [SignaturePkt sp@SigV4 {}] = isSP' sp
    isSP False [SignaturePkt _] = True
    isSP _ _ = False
    isSP' (SigV3 st _ _ _ _ _ _) = st `elem` rts
    isSP' (SigV4 st _ _ _ _ _ _) = st `elem` rts
    isSP' _ = False

signedUID :: Bool -> Parser [Pkt] (UidOrUat, [SignaturePayload])
signedUID intolerant = do
  upkts <- satisfy isUID
  case upkts of
    [UserIdPkt u] -> do
      sigs <-
        concatMany
          (signature
             intolerant
             [ GenericCert
             , PersonaCert
             , CasualCert
             , PositiveCert
             , CertRevocationSig
             ])
      return (I u, sigs)
    _ -> failure
  where
    isUID [UserIdPkt _] = True
    isUID _ = False

signedUAt :: Bool -> Parser [Pkt] (UidOrUat, [SignaturePayload])
signedUAt intolerant = do
  uapkts <- satisfy isUAt
  case uapkts of
    [UserAttributePkt us] -> do
      sigs <-
        concatMany
          (signature
             intolerant
             [ GenericCert
             , PersonaCert
             , CasualCert
             , PositiveCert
             , CertRevocationSig
             ])
      return (A us, sigs)
    _ -> failure
  where
    isUAt [UserAttributePkt _] = True
    isUAt _ = False

signedOrRevokedPubSubkey :: Bool -> Parser [Pkt] [(Pkt, [SignaturePayload])]
signedOrRevokedPubSubkey intolerant = do
  pskpkts <- satisfy isPSKP
  case pskpkts of
    [p] -> do
      sigs <-
        concatMany
          (signature intolerant [SubkeyBindingSig, SubkeyRevocationSig])
      return [(p, sigs)]
    _ -> failure
  where
    isPSKP [PublicSubkeyPkt _] = True
    isPSKP _ = False

brokenPubSubkey :: Parser [Pkt] [(Pkt, [SignaturePayload])]
brokenPubSubkey = do
  _ <- broken 14
  _ <- concatMany (signature False [SubkeyBindingSig, SubkeyRevocationSig])
  return []

rawOrSignedOrRevokedSecSubkey ::
     Bool -> Parser [Pkt] [(Pkt, [SignaturePayload])]
rawOrSignedOrRevokedSecSubkey intolerant = do
  sskpkts <- satisfy isSSKP
  case sskpkts of
    [p] -> do
      sigs <-
        concatMany
          (signature intolerant [SubkeyBindingSig, SubkeyRevocationSig])
      return [(p, sigs)]
    _ -> failure
  where
    isSSKP [SecretSubkeyPkt _ _] = True
    isSSKP _ = False

brokenSecSubkey :: Parser [Pkt] [(Pkt, [SignaturePayload])]
brokenSecSubkey = do
  _ <- broken 7
  _ <- concatMany (signature False [SubkeyBindingSig, SubkeyRevocationSig])
  return []

skPayload :: Parser [Pkt] (PKPayload, Maybe SKAddendum)
skPayload = do
  spkts <- satisfy isSKP
  case spkts of
    [SecretKeyPkt p ska] -> return (p, Just ska)
    _ -> failure
  where
    isSKP [SecretKeyPkt _ _] = True
    isSKP _ = False

broken :: Int -> Parser [Pkt] Pkt
broken t = do
  bpkts <- satisfy isBroken
  case bpkts of
    [bp] -> return bp
    _ -> failure
  where
    isBroken [BrokenPacketPkt _ a _] = t == fromIntegral a
    isBroken _ = False

-- | parse TKs from packets
parseTKs :: Bool -> [Pkt] -> [TK]
parseTKs intolerant ps =
  catMaybes
    (concatMap
       fst
       (completeResults
          (feedEof (feed (filter notTrustPacket ps) (many (anyTK intolerant))))))
  where
    notTrustPacket = not . isTrustPkt