packages feed

hOpenPGP-3.1: Codec/Encryption/OpenPGP/KeyringParser.hs

-- KeyringParser.hs: OpenPGP (RFC9580) transferable keys parsing
-- Copyright © 2012-2026  Clint Adams
-- This software is released under the terms of the Expat license.
-- (See the LICENSE file).
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}

module Codec.Encryption.OpenPGP.KeyringParser
    ( -- * Parsers
      parseAChunk
    , parseAChunkEither
    , finalizeParsing
    , finalizeParsingEither
    , KeyringChunkParseError (..)
    , anyTK
    , anyTKWithWireRep
    , UidOrUat (..)
    , splitUs
    , publicTK
    , publicTKWithWireRep
    , secretTK
    , secretTKWithWireRep
    , brokenTK
    , brokenTKWithWireRep
    , pkPayload
    , pkPayloadWithWireRep
    , signature
    , signatureWithWireRep
    , signedUID
    , signedUIDWithWireRep
    , signedUAt
    , signedUAtWithWireRep
    , signedOrRevokedPubSubkey
    , signedOrRevokedPubSubkeyWithWireRep
    , brokenPubSubkey
    , brokenPubSubkeyWithWireRep
    , rawOrSignedOrRevokedSecSubkey
    , rawOrSignedOrRevokedSecSubkeyWithWireRep
    , brokenSecSubkey
    , brokenSecSubkeyWithWireRep
    , skPayload
    , skPayloadWithWireRep
    , broken
    , brokenWithWireRep

      -- * Utilities
    , parseUnknownTKs
    , parseTKsEither
    , parseTKs
    , parsePublicTKs
    , parseSecretTKs
    , parseTKsWithWireRep
    ) where

import Control.Applicative (many, (<|>))
import Data.Either (rights)
import qualified Data.List.NonEmpty as NE
import Data.Maybe (catMaybes, mapMaybe)
import Data.Text (Text)
import Text.ParserCombinators.Incremental.LeftBiasedLocal
    ( Parser
    , concatMany
    , failure
    , feed
    , feedEof
    , inspect
    , satisfy
    )

import Codec.Encryption.OpenPGP.Ontology (isTrustPkt)
import Codec.Encryption.OpenPGP.Policy
    ( isAllowedPrimaryKeySigType
    , isAllowedSubkeySigType
    , isAllowedUIDSigType
    )
import Codec.Encryption.OpenPGP.SignatureQualities (sigType)
import Codec.Encryption.OpenPGP.Types
import Data.Conduit.OpenPGP.Keyring.Instances ()

data KeyringChunkParseError
    = ChunkFailureBeforeInput String
    | ChunkUnexpectedFinalizationFailure
    | ChunkParserFailure String
    deriving (Eq, Show)

renderChunkParseError :: KeyringChunkParseError -> String
renderChunkParseError (ChunkFailureBeforeInput msg) = msg
renderChunkParseError ChunkUnexpectedFinalizationFailure =
    "Unexpected finalization failure"
renderChunkParseError (ChunkParserFailure msg) = msg

collapseCompleted
    :: Monoid s
    => [(r, s)]
    -> ([r], s)
collapseCompleted rs =
    let (resultsRev, remainder) =
            foldl'
                ( \(accResults, accRemainder) (result, rest) ->
                    (result : accResults, accRemainder <> rest)
                )
                ([], mempty)
                rs
     in (reverse resultsRev, remainder)

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 op a st =
    either
        (error . renderChunkParseError)
        id
        (parseAChunkEither op a st)

parseAChunkEither
    :: (Monoid s, Show s)
    => Parser s r
    -> s
    -> ([(r, s)], Maybe (Maybe (r -> r), Parser s r))
    -> Either
        KeyringChunkParseError
        (([(r, s)], Maybe (Maybe (r -> r), Parser s r)), [r])
parseAChunkEither _ a ([], Nothing) =
    Left (ChunkFailureBeforeInput ("Failure before " ++ show a))
parseAChunkEither op a (cr, Nothing) =
    let (completed, remainder) = collapseCompleted cr
     in (\x -> (x, completed))
            <$> either
                (Left . ChunkParserFailure)
                Right
                (inspect (feed (remainder <> a) op))
parseAChunkEither _ a (_, Just (_, p)) =
    (\x -> (x, []))
        <$> either (Left . ChunkParserFailure) Right (inspect (feed a p))

finalizeParsing
    :: Monoid s
    => ([(r, s)], Maybe (Maybe (r -> r), Parser s r))
    -> (([(r, s)], Maybe (Maybe (r -> r), Parser s r)), [r])
finalizeParsing st =
    either
        (error . renderChunkParseError)
        id
        (finalizeParsingEither st)

finalizeParsingEither
    :: Monoid s
    => ([(r, s)], Maybe (Maybe (r -> r), Parser s r))
    -> Either
        KeyringChunkParseError
        (([(r, s)], Maybe (Maybe (r -> r), Parser s r)), [r])
finalizeParsingEither ([], Nothing) = Left ChunkUnexpectedFinalizationFailure
finalizeParsingEither (cr, Nothing) =
    let (completed, _) = collapseCompleted cr
     in Right (([], Nothing), completed)
finalizeParsingEither (_, Just (_, p)) =
    either
        (Left . ChunkParserFailure)
        finalizeParsingEither
        (inspect (feedEof p))

anyTK :: Bool -> Parser [Pkt] (Maybe TKUnknown)
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 TKUnknown)
publicTK intolerant = do
    pkp <- pkPayload
    pkpsigs <-
        concatMany
            (signatureWithPredicate intolerant isAllowedPrimaryKeySigType)
    (uids, uats) <-
        fmap
            splitUs
            (many (signedUID intolerant <|> signedUAt intolerant))
    subs <- concatMany (pubsub intolerant)
    return $ Just (TKUnknown pkp pkpsigs uids uats subs)
  where
    pubsub True = signedOrRevokedPubSubkey True
    pubsub False = signedOrRevokedPubSubkey False <|> brokenPubSubkey
secretTK intolerant = do
    skp <- skPayload
    skpsigs <-
        concatMany
            (signatureWithPredicate intolerant isAllowedPrimaryKeySigType)
    (uids, uats) <-
        fmap
            splitUs
            (many (signedUID intolerant <|> signedUAt intolerant))
    subs <- concatMany (secsub intolerant)
    return $ Just (TKUnknown skp skpsigs uids uats subs)
  where
    secsub True = rawOrSignedOrRevokedSecSubkey True
    secsub False = rawOrSignedOrRevokedSecSubkey False <|> brokenSecSubkey

brokenTK :: Int -> Parser [Pkt] (Maybe TKUnknown)
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] (SomePKPayload, Maybe SKAddendum)
pkPayload = do
    pkpkts <- satisfy isPKP
    case pkpkts of
        [pkt] ->
            case pktToPublicKeyPkt pkt of
                Just keyPkt
                    | keyPktRole keyPkt == KeyPktPrimary ->
                        return (keyPktTKKey keyPkt)
                _ -> failure
        _ -> failure
  where
    isPKP [pkt] =
        case pktToPublicKeyPkt pkt of
            Just keyPkt -> keyPktRole keyPkt == KeyPktPrimary
            Nothing -> False
    isPKP _ = False

signature :: Bool -> [SigType] -> Parser [Pkt] [SignaturePayload]
signature intolerant rts = signatureWithPredicate intolerant (\st -> st `elem` rts)

{- | RFC9580-aware signature parser that validates context using a predicate
The predicate operates on SigType to determine if the signature is allowed
in this context (e.g., isAllowedPrimaryKeySigType for primary keys).
-}
signatureWithPredicate
    :: Bool -> (SigType -> Bool) -> Parser [Pkt] [SignaturePayload]
signatureWithPredicate intolerant predicate =
    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] = isSP' sp
    isSP False [SignaturePkt _] = True
    isSP _ _ = False
    isSP' sigPayload = maybe False predicate (sigType sigPayload)

signedUID :: Bool -> Parser [Pkt] (UidOrUat, [SignaturePayload])
signedUID intolerant = do
    upkts <- satisfy isUID
    case upkts of
        [UserIdPkt u] -> do
            sigs <-
                concatMany
                    (signatureWithPredicate intolerant isAllowedUIDSigType)
            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
                    (signatureWithPredicate intolerant isAllowedUIDSigType)
            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
                    (signatureWithPredicate intolerant isAllowedSubkeySigType)
            return [(p, sigs)]
        _ -> failure
  where
    isPSKP [pkt] =
        case pktToPublicKeyPkt pkt of
            Just keyPkt -> keyPktRole keyPkt == KeyPktSubkey
            Nothing -> False
    isPSKP _ = False

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

rawOrSignedOrRevokedSecSubkey
    :: Bool -> Parser [Pkt] [(Pkt, [SignaturePayload])]
rawOrSignedOrRevokedSecSubkey intolerant = do
    sskpkts <- satisfy isSSKP
    case sskpkts of
        [p] -> do
            sigs <-
                concatMany
                    (signatureWithPredicate intolerant isAllowedSubkeySigType)
            return [(p, sigs)]
        _ -> failure
  where
    isSSKP [pkt] =
        case pktToSecretKeyPkt pkt of
            Just keyPkt -> keyPktRole keyPkt == KeyPktSubkey
            Nothing -> False
    isSSKP _ = False

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

skPayload :: Parser [Pkt] (SomePKPayload, Maybe SKAddendum)
skPayload = do
    spkts <- satisfy isSKP
    case spkts of
        [pkt] ->
            case pktToSecretKeyPkt pkt of
                Just keyPkt
                    | keyPktRole keyPkt == KeyPktPrimary ->
                        return (keyPktTKKey keyPkt)
                _ -> failure
        _ -> failure
  where
    isSKP [pkt] =
        case pktToSecretKeyPkt pkt of
            Just keyPkt -> keyPktRole keyPkt == KeyPktPrimary
            Nothing -> False
    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
parseUnknownTKs :: Bool -> [Pkt] -> [TKUnknown]
parseUnknownTKs intolerant ps =
    catMaybes $
        runIncrementalParser
            (anyTK intolerant)
            (map (: []) (filter notTrustPacket ps))
  where
    notTrustPacket = not . isTrustPkt

parseTKsEither
    :: Bool -> [Pkt] -> [Either TKConversionError SomeTK]
parseTKsEither intolerant =
    map fromUnknownToTKEither . parseUnknownTKs intolerant

parseTKs :: Bool -> [Pkt] -> [SomeTK]
parseTKs intolerant packets = rights (parseTKsEither intolerant packets)

parsePublicTKs :: Bool -> [Pkt] -> [TK 'PublicTK]
parsePublicTKs intolerant packets =
    mapMaybe someTKToPublicTK (parseTKs intolerant packets)

parseSecretTKs :: Bool -> [Pkt] -> [TK 'SecretTK]
parseSecretTKs intolerant packets =
    mapMaybe someTKToSecretTK (parseTKs intolerant packets)

anyTKWithWireRep
    :: Bool -> Parser [PktWithWireRep] (Maybe TKWithWireRep)
anyTKWithWireRep True = publicTKWithWireRep True <|> secretTKWithWireRep True
anyTKWithWireRep False =
    publicTKWithWireRep False
        <|> secretTKWithWireRep False
        <|> brokenTKWithWireRep 6
        <|> brokenTKWithWireRep 5

publicTKWithWireRep
    , secretTKWithWireRep
        :: Bool -> Parser [PktWithWireRep] (Maybe TKWithWireRep)
publicTKWithWireRep intolerant = do
    (pkp, pkps) <- pkPayloadWithWireRep
    (pkpsigs, pkpsigrefs) <-
        concatMany
            ( signatureWithWireRepPredicate
                intolerant
                isAllowedPrimaryKeySigType
            )
    uidResults <-
        many
            ( signedUIDWithWireRep intolerant
                <|> signedUAtWithWireRep intolerant
            )
    subResults <- concatMany (pubsub intolerant)
    let semanticUs = fmap fst uidResults
        (uids, uats) = splitUs semanticUs
        uidrefs = concatMap snd uidResults
        subs = fmap fst subResults
        subrefs = concatMap snd subResults
        tk = TKUnknown pkp pkpsigs uids uats subs
        refs = pkps ++ pkpsigrefs ++ uidrefs ++ subrefs
    return $ Just (mkTKWithWireRep tk refs)
  where
    pubsub True = signedOrRevokedPubSubkeyWithWireRep True
    pubsub False =
        signedOrRevokedPubSubkeyWithWireRep False
            <|> brokenPubSubkeyWithWireRep
secretTKWithWireRep intolerant = do
    (skp, skps) <- skPayloadWithWireRep
    (skpsigs, skpsigrefs) <-
        concatMany
            ( signatureWithWireRepPredicate
                intolerant
                isAllowedPrimaryKeySigType
            )
    uidResults <-
        many
            ( signedUIDWithWireRep intolerant
                <|> signedUAtWithWireRep intolerant
            )
    subResults <- concatMany (secsub intolerant)
    let semanticUs = fmap fst uidResults
        (uids, uats) = splitUs semanticUs
        uidrefs = concatMap snd uidResults
        subs = fmap fst subResults
        subrefs = concatMap snd subResults
        tk = TKUnknown skp skpsigs uids uats subs
        refs = skps ++ skpsigrefs ++ uidrefs ++ subrefs
    return $ Just (mkTKWithWireRep tk refs)
  where
    secsub True = rawOrSignedOrRevokedSecSubkeyWithWireRep True
    secsub False =
        rawOrSignedOrRevokedSecSubkeyWithWireRep False
            <|> brokenSecSubkeyWithWireRep

brokenTKWithWireRep
    :: Int -> Parser [PktWithWireRep] (Maybe TKWithWireRep)
brokenTKWithWireRep 6 = do
    _ <- brokenWithWireRep 6
    _ <-
        many
            (signatureWithWireRepPredicate False isAllowedPrimaryKeySigType)
    _ <-
        many (signedUIDWithWireRep False <|> signedUAtWithWireRep False)
    _ <-
        concatMany
            ( signedOrRevokedPubSubkeyWithWireRep False
                <|> brokenPubSubkeyWithWireRep
            )
    return Nothing
brokenTKWithWireRep 5 = do
    _ <- brokenWithWireRep 5
    _ <-
        many
            (signatureWithWireRepPredicate False isAllowedPrimaryKeySigType)
    _ <-
        many (signedUIDWithWireRep False <|> signedUAtWithWireRep False)
    _ <-
        concatMany
            ( rawOrSignedOrRevokedSecSubkeyWithWireRep False
                <|> brokenSecSubkeyWithWireRep
            )
    return Nothing
brokenTKWithWireRep _ = fail "Unexpected broken packet type"

pkPayloadWithWireRep
    :: Parser
        [PktWithWireRep]
        ((SomePKPayload, Maybe SKAddendum), [PktWithWireRep])
pkPayloadWithWireRep = do
    pkpkts <- satisfy isPKPWS
    case pkpkts of
        [pktWithSource] ->
            case pktToPublicKeyPkt (_pktValue pktWithSource) of
                Just keyPkt
                    | keyPktRole keyPkt == KeyPktPrimary ->
                        return (keyPktTKKey keyPkt, [pktWithSource])
                _ -> failure
        _ -> failure
  where
    isPKPWS [pktWithSource] =
        case pktToPublicKeyPkt (_pktValue pktWithSource) of
            Just keyPkt -> keyPktRole keyPkt == KeyPktPrimary
            _ -> False
    isPKPWS _ = False

signatureWithWireRep
    :: Bool
    -> [SigType]
    -> Parser [PktWithWireRep] ([SignaturePayload], [PktWithWireRep])
signatureWithWireRep intolerant rts =
    signatureWithWireRepPredicate intolerant (\st -> st `elem` rts)

-- | RFC9580-aware signature parser with wire representation support
signatureWithWireRepPredicate
    :: Bool
    -> (SigType -> Bool)
    -> Parser [PktWithWireRep] ([SignaturePayload], [PktWithWireRep])
signatureWithWireRepPredicate intolerant predicate =
    if intolerant
        then signature'
        else signature' <|> brokensig'
  where
    signature' = do
        spks <- satisfy (isSPWS intolerant)
        case spks of
            [pktWithSource] ->
                case _pktValue pktWithSource of
                    SignaturePkt sp ->
                        let sigs =
                                ( if intolerant
                                    then id
                                    else filter isSP'
                                )
                                    [sp]
                         in return (sigs, if null sigs then [] else [pktWithSource])
                    _ -> failure
            _ -> failure
    brokensig' = const ([], []) <$> brokenWithWireRep 2
    isSPWS True [pktWithSource] =
        case _pktValue pktWithSource of
            SignaturePkt sp -> isSP' sp
            _ -> False
    isSPWS False [pktWithSource] =
        case _pktValue pktWithSource of
            SignaturePkt _ -> True
            _ -> False
    isSPWS _ _ = False
    isSP' sigPayload = maybe False predicate (sigType sigPayload)

signedUIDWithWireRep
    :: Bool
    -> Parser
        [PktWithWireRep]
        ((UidOrUat, [SignaturePayload]), [PktWithWireRep])
signedUIDWithWireRep intolerant = do
    upkts <- satisfy isUIDWS
    case upkts of
        [pktWithSource] ->
            case _pktValue pktWithSource of
                UserIdPkt u -> do
                    (sigs, sigrefs) <-
                        concatMany
                            (signatureWithWireRepPredicate intolerant isAllowedUIDSigType)
                    return ((I u, sigs), pktWithSource : sigrefs)
                _ -> failure
        _ -> failure
  where
    isUIDWS [pktWithSource] =
        case _pktValue pktWithSource of
            UserIdPkt _ -> True
            _ -> False
    isUIDWS _ = False

signedUAtWithWireRep
    :: Bool
    -> Parser
        [PktWithWireRep]
        ((UidOrUat, [SignaturePayload]), [PktWithWireRep])
signedUAtWithWireRep intolerant = do
    uapkts <- satisfy isUAtWS
    case uapkts of
        [pktWithSource] ->
            case _pktValue pktWithSource of
                UserAttributePkt us -> do
                    (sigs, sigrefs) <-
                        concatMany
                            (signatureWithWireRepPredicate intolerant isAllowedUIDSigType)
                    return ((A us, sigs), pktWithSource : sigrefs)
                _ -> failure
        _ -> failure
  where
    isUAtWS [pktWithSource] =
        case _pktValue pktWithSource of
            UserAttributePkt _ -> True
            _ -> False
    isUAtWS _ = False

signedOrRevokedPubSubkeyWithWireRep
    :: Bool
    -> Parser
        [PktWithWireRep]
        [((Pkt, [SignaturePayload]), [PktWithWireRep])]
signedOrRevokedPubSubkeyWithWireRep intolerant = do
    pskpkts <- satisfy isPSKPWS
    case pskpkts of
        [pktWithSource] -> do
            (sigs, sigrefs) <-
                concatMany
                    (signatureWithWireRepPredicate intolerant isAllowedSubkeySigType)
            return
                [((_pktValue pktWithSource, sigs), pktWithSource : sigrefs)]
        _ -> failure
  where
    isPSKPWS [pktWithSource] =
        case pktToPublicKeyPkt (_pktValue pktWithSource) of
            Just keyPkt -> keyPktRole keyPkt == KeyPktSubkey
            _ -> False
    isPSKPWS _ = False

brokenPubSubkeyWithWireRep
    :: Parser
        [PktWithWireRep]
        [((Pkt, [SignaturePayload]), [PktWithWireRep])]
brokenPubSubkeyWithWireRep = do
    _ <- brokenWithWireRep 14
    _ <-
        concatMany
            (signatureWithWireRepPredicate False isAllowedSubkeySigType)
    return []

rawOrSignedOrRevokedSecSubkeyWithWireRep
    :: Bool
    -> Parser
        [PktWithWireRep]
        [((Pkt, [SignaturePayload]), [PktWithWireRep])]
rawOrSignedOrRevokedSecSubkeyWithWireRep intolerant = do
    sskpkts <- satisfy isSSKPWS
    case sskpkts of
        [pktWithSource] -> do
            (sigs, sigrefs) <-
                concatMany
                    (signatureWithWireRepPredicate intolerant isAllowedSubkeySigType)
            return
                [((_pktValue pktWithSource, sigs), pktWithSource : sigrefs)]
        _ -> failure
  where
    isSSKPWS [pktWithSource] =
        case pktToSecretKeyPkt (_pktValue pktWithSource) of
            Just keyPkt -> keyPktRole keyPkt == KeyPktSubkey
            _ -> False
    isSSKPWS _ = False

brokenSecSubkeyWithWireRep
    :: Parser
        [PktWithWireRep]
        [((Pkt, [SignaturePayload]), [PktWithWireRep])]
brokenSecSubkeyWithWireRep = do
    _ <- brokenWithWireRep 7
    _ <-
        concatMany
            (signatureWithWireRepPredicate False isAllowedSubkeySigType)
    return []

skPayloadWithWireRep
    :: Parser
        [PktWithWireRep]
        ((SomePKPayload, Maybe SKAddendum), [PktWithWireRep])
skPayloadWithWireRep = do
    spkts <- satisfy isSKPWS
    case spkts of
        [pktWithSource] ->
            case pktToSecretKeyPkt (_pktValue pktWithSource) of
                Just keyPkt
                    | keyPktRole keyPkt == KeyPktPrimary ->
                        return (keyPktTKKey keyPkt, [pktWithSource])
                _ -> failure
        _ -> failure
  where
    isSKPWS [pktWithSource] =
        case pktToSecretKeyPkt (_pktValue pktWithSource) of
            Just keyPkt -> keyPktRole keyPkt == KeyPktPrimary
            _ -> False
    isSKPWS _ = False

brokenWithWireRep
    :: Int -> Parser [PktWithWireRep] PktWithWireRep
brokenWithWireRep t = do
    bpkts <- satisfy isBrokenWS
    case bpkts of
        [bp] -> return bp
        _ -> failure
  where
    isBrokenWS [pktWithSource] =
        case _pktValue pktWithSource of
            BrokenPacketPkt _ a _ -> t == fromIntegral a
            _ -> False
    isBrokenWS _ = False

parseTKsWithWireRep
    :: Bool -> [PktWithWireRep] -> [TKWithWireRep]
parseTKsWithWireRep intolerant ps =
    catMaybes $
        runIncrementalParser
            (anyTKWithWireRep intolerant)
            (map (: []) (filter notTrustPacketWithWireRep ps))
  where
    notTrustPacketWithWireRep = not . isTrustPkt . _pktValue

runIncrementalParser
    :: (Monoid s, Show s)
    => Parser s r
    -> [s]
    -> [r]
runIncrementalParser parser chunks = go ([], Just (Nothing, parser)) chunks
  where
    go st [] = snd (finalizeParsing st)
    go st (chunk : rest) =
        let (st', out) = parseAChunk parser chunk st
         in out <> go st' rest

mkTKWithWireRep :: TKUnknown -> [PktWithWireRep] -> TKWithWireRep
mkTKWithWireRep tk refs =
    case refs of
        [] ->
            error "mkTKWithWireRep requires at least one packet reference"
        (pktWithSource : _) ->
            TKWithWireRep
                (NE.singleton (wireRepOfPkt pktWithSource))
                (spanByteRanges (map _pktRange refs))
                refs
                tk