{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}

-- | Stability: experimental
-- This module implements the
-- [Android Key Attestation Statement Format](https://www.w3.org/TR/webauthn-2/#sctn-android-key-attestation).
module Crypto.WebAuthn.AttestationStatementFormat.AndroidKey
  ( format,
    Format (..),
    TrustLevel (..),
    VerificationError (..),
  )
where

import qualified Codec.CBOR.Term as CBOR
import Control.Exception (Exception)
import Control.Monad (forM, unless, void, when)
import Crypto.Hash (Digest, SHA256, digestFromByteString)
import qualified Crypto.WebAuthn.Cose.Internal.Verify as Cose
import qualified Crypto.WebAuthn.Cose.PublicKey as Cose
import qualified Crypto.WebAuthn.Cose.PublicKeyWithSignAlg as Cose
import qualified Crypto.WebAuthn.Cose.SignAlg as Cose
import Crypto.WebAuthn.Internal.Utils (failure)
import qualified Crypto.WebAuthn.Model.Types as M
import Data.ASN1.Parse (ParseASN1, getNext, getNextContainerMaybe, hasNext, onNextContainer, onNextContainerMaybe, runParseASN1)
import Data.ASN1.Types (ASN1 (IntVal, OctetString), ASN1Class (Context), ASN1ConstructionType (Container, Sequence, Set))
import Data.Aeson (ToJSON, object, toJSON, (.=))
import Data.Bifunctor (first)
import Data.ByteArray (convert)
import Data.ByteString (ByteString)
import Data.HashMap.Strict ((!?))
import Data.List.NonEmpty (NonEmpty ((:|)), toList)
import qualified Data.List.NonEmpty as NE
import Data.Maybe (isJust)
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Text (Text)
import qualified Data.Text as Text
import Data.X509 (Extension (extDecode, extEncode, extHasNestedASN1, extOID))
import qualified Data.X509 as X509

-- | [(spec)](https://source.android.com/security/keystore/attestation#attestation-extension)
-- The X509 extension android uses for attestation information
data ExtAttestation = ExtAttestation
  { ExtAttestation -> Digest SHA256
attestationChallenge :: Digest SHA256,
    ExtAttestation -> AuthorizationList
softwareEnforced :: AuthorizationList,
    ExtAttestation -> AuthorizationList
teeEnforced :: AuthorizationList
  }
  deriving (ExtAttestation -> ExtAttestation -> Bool
(ExtAttestation -> ExtAttestation -> Bool)
-> (ExtAttestation -> ExtAttestation -> Bool) -> Eq ExtAttestation
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ExtAttestation -> ExtAttestation -> Bool
$c/= :: ExtAttestation -> ExtAttestation -> Bool
== :: ExtAttestation -> ExtAttestation -> Bool
$c== :: ExtAttestation -> ExtAttestation -> Bool
Eq, Int -> ExtAttestation -> ShowS
[ExtAttestation] -> ShowS
ExtAttestation -> String
(Int -> ExtAttestation -> ShowS)
-> (ExtAttestation -> String)
-> ([ExtAttestation] -> ShowS)
-> Show ExtAttestation
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ExtAttestation] -> ShowS
$cshowList :: [ExtAttestation] -> ShowS
show :: ExtAttestation -> String
$cshow :: ExtAttestation -> String
showsPrec :: Int -> ExtAttestation -> ShowS
$cshowsPrec :: Int -> ExtAttestation -> ShowS
Show)

-- | [(spec)](https://source.android.com/security/keystore/attestation#schema)
-- A partial @AuthorizationList@ structure
data AuthorizationList = AuthorizationList
  { AuthorizationList -> Maybe (Set Integer)
purpose :: Maybe (Set Integer),
    AuthorizationList -> Maybe ()
allApplications :: Maybe (),
    AuthorizationList -> Maybe Integer
origin :: Maybe Integer
  }
  deriving (AuthorizationList -> AuthorizationList -> Bool
(AuthorizationList -> AuthorizationList -> Bool)
-> (AuthorizationList -> AuthorizationList -> Bool)
-> Eq AuthorizationList
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: AuthorizationList -> AuthorizationList -> Bool
$c/= :: AuthorizationList -> AuthorizationList -> Bool
== :: AuthorizationList -> AuthorizationList -> Bool
$c== :: AuthorizationList -> AuthorizationList -> Bool
Eq, Int -> AuthorizationList -> ShowS
[AuthorizationList] -> ShowS
AuthorizationList -> String
(Int -> AuthorizationList -> ShowS)
-> (AuthorizationList -> String)
-> ([AuthorizationList] -> ShowS)
-> Show AuthorizationList
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [AuthorizationList] -> ShowS
$cshowList :: [AuthorizationList] -> ShowS
show :: AuthorizationList -> String
$cshow :: AuthorizationList -> String
showsPrec :: Int -> AuthorizationList -> ShowS
$cshowsPrec :: Int -> AuthorizationList -> ShowS
Show)

instance Extension ExtAttestation where
  extOID :: ExtAttestation -> OID
extOID = OID -> ExtAttestation -> OID
forall a b. a -> b -> a
const [Integer
1, Integer
3, Integer
6, Integer
1, Integer
4, Integer
1, Integer
11129, Integer
2, Integer
1, Integer
17]
  extHasNestedASN1 :: Proxy ExtAttestation -> Bool
extHasNestedASN1 = Bool -> Proxy ExtAttestation -> Bool
forall a b. a -> b -> a
const Bool
True
  extEncode :: ExtAttestation -> [ASN1]
extEncode = String -> ExtAttestation -> [ASN1]
forall a. HasCallStack => String -> a
error String
"Can not encode the parsed ExtAttestation to a valid [ASN1] because most fields are dropped during parsing."
  extDecode :: [ASN1] -> Either String ExtAttestation
extDecode [ASN1]
asn1 =
    ShowS
-> Either String ExtAttestation -> Either String ExtAttestation
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (String
"Could not decode ASN1 attestation extension: " String -> ShowS
forall a. [a] -> [a] -> [a]
++) (Either String ExtAttestation -> Either String ExtAttestation)
-> Either String ExtAttestation -> Either String ExtAttestation
forall a b. (a -> b) -> a -> b
$
      ParseASN1 ExtAttestation -> [ASN1] -> Either String ExtAttestation
forall a. ParseASN1 a -> [ASN1] -> Either String a
runParseASN1 ParseASN1 ExtAttestation
decodeExtAttestation [ASN1]
asn1
    where
      decodeExtAttestation :: ParseASN1 ExtAttestation
      decodeExtAttestation :: ParseASN1 ExtAttestation
decodeExtAttestation = ASN1ConstructionType
-> ParseASN1 ExtAttestation -> ParseASN1 ExtAttestation
forall a. ASN1ConstructionType -> ParseASN1 a -> ParseASN1 a
onNextContainer ASN1ConstructionType
Sequence (ParseASN1 ExtAttestation -> ParseASN1 ExtAttestation)
-> ParseASN1 ExtAttestation -> ParseASN1 ExtAttestation
forall a b. (a -> b) -> a -> b
$ do
        -- Discard the version as the different attestation versions do not differ in a way that is significant to our purpose.
        ASN1
_attestationVersion <- ParseASN1 ASN1
getNext
        ASN1
_attestationSecurityLevel <- ParseASN1 ASN1
getNext
        ASN1
_keyMasterVersion <- ParseASN1 ASN1
getNext
        ASN1
_keymmasterSecurityLevel <- ParseASN1 ASN1
getNext
        (OctetString ByteString
attestationChallenge) <- ParseASN1 ASN1
getNext
        ASN1
_uniqueId <- ParseASN1 ASN1
getNext
        AuthorizationList
softwareEnforced <- ASN1ConstructionType
-> ParseASN1 AuthorizationList -> ParseASN1 AuthorizationList
forall a. ASN1ConstructionType -> ParseASN1 a -> ParseASN1 a
onNextContainer ASN1ConstructionType
Sequence ParseASN1 AuthorizationList
decodeAttestationList
        AuthorizationList
teeEnforced <- ASN1ConstructionType
-> ParseASN1 AuthorizationList -> ParseASN1 AuthorizationList
forall a. ASN1ConstructionType -> ParseASN1 a -> ParseASN1 a
onNextContainer ASN1ConstructionType
Sequence ParseASN1 AuthorizationList
decodeAttestationList
        Digest SHA256
attestationChallengeHash <- ParseASN1 (Digest SHA256)
-> (Digest SHA256 -> ParseASN1 (Digest SHA256))
-> Maybe (Digest SHA256)
-> ParseASN1 (Digest SHA256)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> ParseASN1 (Digest SHA256)
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Could not create hash from AttestationChallenge: ") Digest SHA256 -> ParseASN1 (Digest SHA256)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Digest SHA256) -> ParseASN1 (Digest SHA256))
-> Maybe (Digest SHA256) -> ParseASN1 (Digest SHA256)
forall a b. (a -> b) -> a -> b
$ ByteString -> Maybe (Digest SHA256)
forall a ba.
(HashAlgorithm a, ByteArrayAccess ba) =>
ba -> Maybe (Digest a)
digestFromByteString ByteString
attestationChallenge
        pure $ Digest SHA256
-> AuthorizationList -> AuthorizationList -> ExtAttestation
ExtAttestation Digest SHA256
attestationChallengeHash AuthorizationList
softwareEnforced AuthorizationList
teeEnforced

      decodeAttestationList :: ParseASN1 AuthorizationList
      decodeAttestationList :: ParseASN1 AuthorizationList
decodeAttestationList = do
        Maybe (Set Integer)
purpose <- ASN1ConstructionType
-> ParseASN1 (Set Integer) -> ParseASN1 (Maybe (Set Integer))
forall a.
ASN1ConstructionType -> ParseASN1 a -> ParseASN1 (Maybe a)
onNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
1) (ASN1ConstructionType
-> ParseASN1 (Set Integer) -> ParseASN1 (Set Integer)
forall a. ASN1ConstructionType -> ParseASN1 a -> ParseASN1 a
onNextContainer ASN1ConstructionType
Set (ParseASN1 (Set Integer) -> ParseASN1 (Set Integer))
-> ParseASN1 (Set Integer) -> ParseASN1 (Set Integer)
forall a b. (a -> b) -> a -> b
$ Set Integer -> ParseASN1 (Set Integer)
decodeIntSet Set Integer
forall a. Set a
Set.empty)
        Maybe [ASN1]
_algorithm <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
2)
        Maybe [ASN1]
_keySize <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
3)
        Maybe [ASN1]
_digest <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
5)
        Maybe [ASN1]
_padding <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
6)
        Maybe [ASN1]
_ecCurve <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
10)
        Maybe [ASN1]
_rsaPublicExponent <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
200)
        Maybe [ASN1]
_rollbackResistance <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
303)
        Maybe [ASN1]
_activeDateTime <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
400)
        Maybe [ASN1]
_originationExpireDateTime <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
401)
        Maybe [ASN1]
_usageExpireDateTime <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
402)
        Maybe [ASN1]
_noAuthRequired <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
503)
        Maybe [ASN1]
_userAuthType <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
504)
        Maybe [ASN1]
_authTimeout <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
505)
        Maybe [ASN1]
_allowWhileOnBody <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
506)
        Maybe [ASN1]
_trustedUserPresenceRequired <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
507)
        Maybe [ASN1]
_trustedConfirmationRequired <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
508)
        Maybe [ASN1]
_unlockedDeviceRequired <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
509)
        Maybe ()
allApplications <- Maybe [ASN1] -> Maybe ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Maybe [ASN1] -> Maybe ())
-> ParseASN1 (Maybe [ASN1]) -> ParseASN1 (Maybe ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
600)
        Maybe [ASN1]
_applicationId <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
601)
        Maybe [ASN1]
_creationDateTime <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
701)
        Maybe Integer
origin <-
          ASN1ConstructionType
-> ParseASN1 Integer -> ParseASN1 (Maybe Integer)
forall a.
ASN1ConstructionType -> ParseASN1 a -> ParseASN1 (Maybe a)
onNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
702) (ParseASN1 Integer -> ParseASN1 (Maybe Integer))
-> ParseASN1 Integer -> ParseASN1 (Maybe Integer)
forall a b. (a -> b) -> a -> b
$
            ParseASN1 ASN1
getNext ParseASN1 ASN1 -> (ASN1 -> ParseASN1 Integer) -> ParseASN1 Integer
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
              IntVal Integer
i -> Integer -> ParseASN1 Integer
forall (f :: * -> *) a. Applicative f => a -> f a
pure Integer
i
              ASN1
_ -> String -> ParseASN1 Integer
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Unexpected non-IntVal"
        Maybe [ASN1]
_rollbackResistant <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
703)
        Maybe [ASN1]
_rootOfTrust <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
704)
        Maybe [ASN1]
_osVersion <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
705)
        Maybe [ASN1]
_osPatchLevel <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
706)
        Maybe [ASN1]
_attestationApplicationId <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
709)
        Maybe [ASN1]
_attestationIdBrand <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
710)
        Maybe [ASN1]
_attestationIdDevice <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
711)
        Maybe [ASN1]
_attestationIdProduct <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
712)
        Maybe [ASN1]
_attestationIdSerial <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
713)
        Maybe [ASN1]
_attestationIdImei <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
714)
        Maybe [ASN1]
_attestationIdMeid <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
715)
        Maybe [ASN1]
_attestationIdManufacturer <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
716)
        Maybe [ASN1]
_attestationIdModel <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
717)
        Maybe [ASN1]
_vendorPatchLevel <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
718)
        Maybe [ASN1]
_bootPatchLevel <- ASN1ConstructionType -> ParseASN1 (Maybe [ASN1])
getNextContainerMaybe (ASN1Class -> Int -> ASN1ConstructionType
Container ASN1Class
Context Int
719)
        pure $ Maybe (Set Integer)
-> Maybe () -> Maybe Integer -> AuthorizationList
AuthorizationList Maybe (Set Integer)
purpose Maybe ()
allApplications Maybe Integer
origin

      decodeIntSet :: Set Integer -> ParseASN1 (Set Integer)
      decodeIntSet :: Set Integer -> ParseASN1 (Set Integer)
decodeIntSet Set Integer
set = do
        Bool
next <- ParseASN1 Bool
hasNext
        if Bool
next
          then do
            IntVal Integer
elem <- ParseASN1 ASN1
getNext
            Set Integer -> ParseASN1 (Set Integer)
decodeIntSet (Integer -> Set Integer -> Set Integer
forall a. Ord a => a -> Set a -> Set a
Set.insert Integer
elem Set Integer
set)
          else Set Integer -> ParseASN1 (Set Integer)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Set Integer
set

-- | The required Trust level for Android Key attestation.
data TrustLevel
  = -- | Trust has to be ensured on the software level. This is weaker than TEE
    -- enforced trust.
    SoftwareEnforced
  | -- | Hardware backed attestation, this requires that the Trusted Executing
    -- Environment enforced the attestation.
    TeeEnforced

-- | The Android Key Format. Allow configuration of the required level of
-- trust.
newtype Format = Format
  { Format -> TrustLevel
requiredTrustLevel :: TrustLevel
  }

instance Show Format where
  show :: Format -> String
show = Text -> String
Text.unpack (Text -> String) -> (Format -> Text) -> Format -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Format -> Text
forall a. AttestationStatementFormat a => a -> Text
M.asfIdentifier

-- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-android-key-attestation)
data Statement = Statement
  { Statement -> ByteString
sig :: ByteString,
    Statement -> NonEmpty SignedCertificate
x5c :: NonEmpty X509.SignedCertificate,
    -- | Holds both the "alg" from the statement and the public key from the
    -- X.509 certificate
    Statement -> PublicKeyWithSignAlg
pubKeyAndAlg :: Cose.PublicKeyWithSignAlg,
    -- | Holds the parsed attestation extension of the above X509 certificate,
    -- prevents having to parse it in the AndroidKey.verify function
    Statement -> ExtAttestation
attExt :: ExtAttestation
  }
  deriving (Statement -> Statement -> Bool
(Statement -> Statement -> Bool)
-> (Statement -> Statement -> Bool) -> Eq Statement
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Statement -> Statement -> Bool
$c/= :: Statement -> Statement -> Bool
== :: Statement -> Statement -> Bool
$c== :: Statement -> Statement -> Bool
Eq, Int -> Statement -> ShowS
[Statement] -> ShowS
Statement -> String
(Int -> Statement -> ShowS)
-> (Statement -> String)
-> ([Statement] -> ShowS)
-> Show Statement
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Statement] -> ShowS
$cshowList :: [Statement] -> ShowS
show :: Statement -> String
$cshow :: Statement -> String
showsPrec :: Int -> Statement -> ShowS
$cshowsPrec :: Int -> Statement -> ShowS
Show)

instance ToJSON Statement where
  toJSON :: Statement -> Value
toJSON Statement {ByteString
NonEmpty SignedCertificate
PublicKeyWithSignAlg
ExtAttestation
attExt :: ExtAttestation
pubKeyAndAlg :: PublicKeyWithSignAlg
x5c :: NonEmpty SignedCertificate
sig :: ByteString
attExt :: Statement -> ExtAttestation
pubKeyAndAlg :: Statement -> PublicKeyWithSignAlg
x5c :: Statement -> NonEmpty SignedCertificate
sig :: Statement -> ByteString
..} =
    [Pair] -> Value
object
      [ Key
"alg" Key -> CoseSignAlg -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= PublicKeyWithSignAlg -> CoseSignAlg
Cose.signAlg PublicKeyWithSignAlg
pubKeyAndAlg,
        Key
"sig" Key -> ByteString -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ByteString
sig,
        Key
"x5c" Key -> NonEmpty SignedCertificate -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= NonEmpty SignedCertificate
x5c
      ]

-- | Verification errors specific to Android Key attestation
data VerificationError
  = -- | The public key in the certificate is different from the on in the
    -- attested credential data
    PublicKeyMismatch
      { -- | The public key part of the credential data
        VerificationError -> PublicKey
credentialDataPublicKey :: Cose.PublicKey,
        -- | The public key extracted from the signed certificate
        VerificationError -> PublicKey
certificatePublicKey :: Cose.PublicKey
      }
  | -- | The challenge field of the certificate extension does not match the
    -- clientDataHash
    -- (first: challenge from certificate extension, second: clientDataHash)
    HashMismatch
      { -- | The challenge part of the
        -- [@attestation-extension@](https://source.android.com/security/keystore/attestation#attestation-extension)
        VerificationError -> Digest SHA256
certificateChallenge :: Digest SHA256,
        -- | The client data hash
        VerificationError -> Digest SHA256
clientDataHash :: Digest SHA256
      }
  | -- | The "attestation" extension is scoped to all applications instead of just the RpId
    AndroidKeyAllApplicationsFieldFound
  | -- | The origin field(s) were not equal to KM_ORIGIN_GENERATED (0)
    -- (first: tee-enforced origin, second: software-enforced origin (if allowed by the specified Format))
    AndroidKeyOriginFieldInvalid
      { -- | The origin enforced by the trusted execution environment
        VerificationError -> Maybe Integer
teeEnforcedOrigin :: Maybe Integer,
        -- | The origin enforced by software. NOTE: This field is explicitly
        -- set to `Nothing` if the `Format` specified `TeeEnforced` as the
        -- `requiredTrustLevel`.
        VerificationError -> Maybe Integer
softwareEnforcedOrigin :: Maybe Integer
      }
  | -- | The purpose field(s) were not equal to the singleton set containing
    -- KM_PURPOSE_SIGN (2)
    -- (first: tee-enforced purpose, second: software-enforced purpose (if allowed by the specified Format))
    AndroidKeyPurposeFieldInvalid
      { -- | The purpose enforced by the trusted execution environment
        VerificationError -> Maybe (Set Integer)
teeEnforcedPurpose :: Maybe (Set Integer),
        -- | The purpose enforced by software. NOTE: This field is explicitly
        -- set to `Nothing` if the `Format` specified `TeeEnforced` as the
        -- `requiredTrustLevel`.
        VerificationError -> Maybe (Set Integer)
softwareEnforcedPurpose :: Maybe (Set Integer)
      }
  | -- | The Public key cannot verify the signature over the authenticatorData
    -- and the clientDataHash.
    VerificationFailure Text
  deriving (Int -> VerificationError -> ShowS
[VerificationError] -> ShowS
VerificationError -> String
(Int -> VerificationError -> ShowS)
-> (VerificationError -> String)
-> ([VerificationError] -> ShowS)
-> Show VerificationError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VerificationError] -> ShowS
$cshowList :: [VerificationError] -> ShowS
show :: VerificationError -> String
$cshow :: VerificationError -> String
showsPrec :: Int -> VerificationError -> ShowS
$cshowsPrec :: Int -> VerificationError -> ShowS
Show, Show VerificationError
Typeable VerificationError
Typeable VerificationError
-> Show VerificationError
-> (VerificationError -> SomeException)
-> (SomeException -> Maybe VerificationError)
-> (VerificationError -> String)
-> Exception VerificationError
SomeException -> Maybe VerificationError
VerificationError -> String
VerificationError -> SomeException
forall e.
Typeable e
-> Show e
-> (e -> SomeException)
-> (SomeException -> Maybe e)
-> (e -> String)
-> Exception e
displayException :: VerificationError -> String
$cdisplayException :: VerificationError -> String
fromException :: SomeException -> Maybe VerificationError
$cfromException :: SomeException -> Maybe VerificationError
toException :: VerificationError -> SomeException
$ctoException :: VerificationError -> SomeException
Exception)

-- | [(spec)](https://android.googlesource.com/platform/hardware/libhardware/+/master/include/hardware/keymaster_defs.h)
kmOriginGenerated :: Integer
kmOriginGenerated :: Integer
kmOriginGenerated = Integer
0

-- | [(spec)](https://android.googlesource.com/platform/hardware/libhardware/+/master/include/hardware/keymaster_defs.h)
kmPurposeSign :: Integer
kmPurposeSign :: Integer
kmPurposeSign = Integer
2

instance M.AttestationStatementFormat Format where
  type AttStmt Format = Statement

  asfIdentifier :: Format -> Text
asfIdentifier Format
_ = Text
"android-key"

  asfDecode :: Format -> HashMap Text Term -> Either Text (AttStmt Format)
asfDecode Format
_ HashMap Text Term
xs =
    case (HashMap Text Term
xs HashMap Text Term -> Text -> Maybe Term
forall k v. (Eq k, Hashable k) => HashMap k v -> k -> Maybe v
!? Text
"alg", HashMap Text Term
xs HashMap Text Term -> Text -> Maybe Term
forall k v. (Eq k, Hashable k) => HashMap k v -> k -> Maybe v
!? Text
"sig", HashMap Text Term
xs HashMap Text Term -> Text -> Maybe Term
forall k v. (Eq k, Hashable k) => HashMap k v -> k -> Maybe v
!? Text
"x5c") of
      (Just (CBOR.TInt Int
algId), Just (CBOR.TBytes ByteString
sig), Just (CBOR.TList ([Term] -> Maybe (NonEmpty Term)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty -> Just NonEmpty Term
x5cRaw))) -> do
        CoseSignAlg
alg <- Int -> Either Text CoseSignAlg
forall a. (Eq a, Num a, Show a) => a -> Either Text CoseSignAlg
Cose.toCoseSignAlg Int
algId
        x5c :: NonEmpty SignedCertificate
x5c@(SignedCertificate
credCert :| [SignedCertificate]
_) <- NonEmpty Term
-> (Term -> Either Text SignedCertificate)
-> Either Text (NonEmpty SignedCertificate)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM NonEmpty Term
x5cRaw ((Term -> Either Text SignedCertificate)
 -> Either Text (NonEmpty SignedCertificate))
-> (Term -> Either Text SignedCertificate)
-> Either Text (NonEmpty SignedCertificate)
forall a b. (a -> b) -> a -> b
$ \case
          CBOR.TBytes ByteString
certBytes ->
            (String -> Text)
-> Either String SignedCertificate -> Either Text SignedCertificate
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ((Text
"Failed to decode signed certificate: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack) (ByteString -> Either String SignedCertificate
X509.decodeSignedCertificate ByteString
certBytes)
          Term
cert ->
            Text -> Either Text SignedCertificate
forall a b. a -> Either a b
Left (Text -> Either Text SignedCertificate)
-> Text -> Either Text SignedCertificate
forall a b. (a -> b) -> a -> b
$ Text
"Certificate CBOR value is not bytes: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack (Term -> String
forall a. Show a => a -> String
show Term
cert)

        let cert :: Certificate
cert = SignedCertificate -> Certificate
X509.getCertificate SignedCertificate
credCert
        ExtAttestation
attExt <- case Extensions -> Maybe (Either String ExtAttestation)
forall a. Extension a => Extensions -> Maybe (Either String a)
X509.extensionGetE (Certificate -> Extensions
X509.certExtensions Certificate
cert) of
          Just (Right ExtAttestation
ext) -> ExtAttestation -> Either Text ExtAttestation
forall (f :: * -> *) a. Applicative f => a -> f a
pure ExtAttestation
ext
          Just (Left String
err) -> Text -> Either Text ExtAttestation
forall a b. a -> Either a b
Left (Text -> Either Text ExtAttestation)
-> Text -> Either Text ExtAttestation
forall a b. (a -> b) -> a -> b
$ Text
"Failed to decode certificate attestation extension: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack String
err
          Maybe (Either String ExtAttestation)
Nothing -> Text -> Either Text ExtAttestation
forall a b. a -> Either a b
Left Text
"Certificate attestation extension is missing"

        PublicKey
pubKey <- PubKey -> Either Text PublicKey
Cose.fromX509 (PubKey -> Either Text PublicKey)
-> PubKey -> Either Text PublicKey
forall a b. (a -> b) -> a -> b
$ Certificate -> PubKey
X509.certPubKey Certificate
cert

        PublicKeyWithSignAlg
pubKeyAndAlg <- PublicKey -> CoseSignAlg -> Either Text PublicKeyWithSignAlg
Cose.makePublicKeyWithSignAlg PublicKey
pubKey CoseSignAlg
alg

        pure Statement :: ByteString
-> NonEmpty SignedCertificate
-> PublicKeyWithSignAlg
-> ExtAttestation
-> Statement
Statement {ByteString
NonEmpty SignedCertificate
PublicKeyWithSignAlg
ExtAttestation
pubKeyAndAlg :: PublicKeyWithSignAlg
attExt :: ExtAttestation
x5c :: NonEmpty SignedCertificate
sig :: ByteString
attExt :: ExtAttestation
pubKeyAndAlg :: PublicKeyWithSignAlg
x5c :: NonEmpty SignedCertificate
sig :: ByteString
..}
      (Maybe Term, Maybe Term, Maybe Term)
_ -> Text -> Either Text Statement
forall a b. a -> Either a b
Left (Text -> Either Text Statement) -> Text -> Either Text Statement
forall a b. (a -> b) -> a -> b
$ Text
"CBOR map didn't have expected value types (alg: int, sig: bytes, x5c: nonempty list): " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack (HashMap Text Term -> String
forall a. Show a => a -> String
show HashMap Text Term
xs)

  asfEncode :: Format -> AttStmt Format -> Term
asfEncode Format
_ Statement {ByteString
NonEmpty SignedCertificate
PublicKeyWithSignAlg
ExtAttestation
attExt :: ExtAttestation
pubKeyAndAlg :: PublicKeyWithSignAlg
x5c :: NonEmpty SignedCertificate
sig :: ByteString
attExt :: Statement -> ExtAttestation
pubKeyAndAlg :: Statement -> PublicKeyWithSignAlg
x5c :: Statement -> NonEmpty SignedCertificate
sig :: Statement -> ByteString
..} =
    [(Term, Term)] -> Term
CBOR.TMap
      [ (Text -> Term
CBOR.TString Text
"sig", ByteString -> Term
CBOR.TBytes ByteString
sig),
        (Text -> Term
CBOR.TString Text
"alg", Int -> Term
CBOR.TInt (Int -> Term) -> Int -> Term
forall a b. (a -> b) -> a -> b
$ CoseSignAlg -> Int
forall p. Num p => CoseSignAlg -> p
Cose.fromCoseSignAlg (CoseSignAlg -> Int) -> CoseSignAlg -> Int
forall a b. (a -> b) -> a -> b
$ PublicKeyWithSignAlg -> CoseSignAlg
Cose.signAlg PublicKeyWithSignAlg
pubKeyAndAlg),
        ( Text -> Term
CBOR.TString Text
"x5c",
          [Term] -> Term
CBOR.TList ([Term] -> Term) -> [Term] -> Term
forall a b. (a -> b) -> a -> b
$
            (SignedCertificate -> Term) -> [SignedCertificate] -> [Term]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Term
CBOR.TBytes (ByteString -> Term)
-> (SignedCertificate -> ByteString) -> SignedCertificate -> Term
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SignedCertificate -> ByteString
forall a.
(Show a, Eq a, ASN1Object a) =>
SignedExact a -> ByteString
X509.encodeSignedObject) ([SignedCertificate] -> [Term]) -> [SignedCertificate] -> [Term]
forall a b. (a -> b) -> a -> b
$ NonEmpty SignedCertificate -> [SignedCertificate]
forall a. NonEmpty a -> [a]
toList NonEmpty SignedCertificate
x5c
        )
      ]

  type AttStmtVerificationError Format = VerificationError

  asfVerify :: Format
-> DateTime
-> AttStmt Format
-> AuthenticatorData 'Registration 'True
-> ClientDataHash
-> Validation
     (NonEmpty (AttStmtVerificationError Format)) SomeAttestationType
asfVerify Format {TrustLevel
requiredTrustLevel :: TrustLevel
requiredTrustLevel :: Format -> TrustLevel
..} DateTime
_ Statement {ByteString
NonEmpty SignedCertificate
PublicKeyWithSignAlg
ExtAttestation
attExt :: ExtAttestation
pubKeyAndAlg :: PublicKeyWithSignAlg
x5c :: NonEmpty SignedCertificate
sig :: ByteString
attExt :: Statement -> ExtAttestation
pubKeyAndAlg :: Statement -> PublicKeyWithSignAlg
x5c :: Statement -> NonEmpty SignedCertificate
sig :: Statement -> ByteString
..} M.AuthenticatorData {adRawData :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> RawField raw
adRawData = M.WithRaw ByteString
rawData, Maybe AuthenticatorExtensionOutputs
AttestedCredentialData 'Registration 'True
AuthenticatorDataFlags
SignatureCounter
RpIdHash
adExtensions :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> Maybe AuthenticatorExtensionOutputs
adAttestedCredentialData :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> AttestedCredentialData c raw
adSignCount :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> SignatureCounter
adFlags :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> AuthenticatorDataFlags
adRpIdHash :: forall (c :: CeremonyKind) (raw :: Bool).
AuthenticatorData c raw -> RpIdHash
adExtensions :: Maybe AuthenticatorExtensionOutputs
adAttestedCredentialData :: AttestedCredentialData 'Registration 'True
adSignCount :: SignatureCounter
adFlags :: AuthenticatorDataFlags
adRpIdHash :: RpIdHash
..} ClientDataHash
clientDataHash = do
    -- 1. Verify that attStmt is valid CBOR conforming to the syntax defined above and perform CBOR decoding on it to
    -- extract the contained fields.
    -- NOTE: The validity of the data is already checked during decoding.

    -- 2. Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash using the
    -- public key in the first certificate in x5c with the algorithm specified in alg.
    let signedData :: ByteString
signedData = ByteString
rawData ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Digest SHA256 -> ByteString
forall bin bout.
(ByteArrayAccess bin, ByteArray bout) =>
bin -> bout
convert (ClientDataHash -> Digest SHA256
M.unClientDataHash ClientDataHash
clientDataHash)
    case PublicKeyWithSignAlg -> ByteString -> ByteString -> Either Text ()
Cose.verify PublicKeyWithSignAlg
pubKeyAndAlg ByteString
signedData ByteString
sig of
      Right () -> () -> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
      Left Text
err -> VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Text -> VerificationError
VerificationFailure Text
err

    -- 3. Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the
    -- attestedCredentialData in authenticatorData.
    let credentialPublicKey :: PublicKey
credentialPublicKey = PublicKeyWithSignAlg -> PublicKey
Cose.publicKey (AttestedCredentialData 'Registration 'True -> PublicKeyWithSignAlg
forall (raw :: Bool).
AttestedCredentialData 'Registration raw -> PublicKeyWithSignAlg
M.acdCredentialPublicKey AttestedCredentialData 'Registration 'True
adAttestedCredentialData)
        pubKey :: PublicKey
pubKey = PublicKeyWithSignAlg -> PublicKey
Cose.publicKey PublicKeyWithSignAlg
pubKeyAndAlg
    Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (PublicKey
credentialPublicKey PublicKey -> PublicKey -> Bool
forall a. Eq a => a -> a -> Bool
== PublicKey
pubKey) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ PublicKey -> PublicKey -> VerificationError
PublicKeyMismatch PublicKey
credentialPublicKey PublicKey
pubKey

    -- 4. Verify that the attestationChallenge field in the attestation certificate extension data is identical to
    -- clientDataHash.
    -- See https://source.android.com/security/keystore/attestation for the ASN1 description
    let attChallenge :: Digest SHA256
attChallenge = ExtAttestation -> Digest SHA256
attestationChallenge ExtAttestation
attExt
    let clientDataHashDigest :: Digest SHA256
clientDataHashDigest = ClientDataHash -> Digest SHA256
M.unClientDataHash ClientDataHash
clientDataHash
    Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Digest SHA256
attChallenge Digest SHA256 -> Digest SHA256 -> Bool
forall a. Eq a => a -> a -> Bool
== Digest SHA256
clientDataHashDigest) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Digest SHA256 -> Digest SHA256 -> VerificationError
HashMismatch Digest SHA256
attChallenge Digest SHA256
clientDataHashDigest

    -- 5. Verify the following using the appropriate authorization list from the attestation certificate extension data:

    -- 5.a The AuthorizationList.allApplications field is not present on either
    -- authorization list (softwareEnforced nor teeEnforced), since
    -- PublicKeyCredential MUST be scoped to the RP ID.
    let software :: AuthorizationList
software = ExtAttestation -> AuthorizationList
softwareEnforced ExtAttestation
attExt
    let tee :: AuthorizationList
tee = ExtAttestation -> AuthorizationList
teeEnforced ExtAttestation
attExt
    Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe () -> Bool
forall a. Maybe a -> Bool
isJust (AuthorizationList -> Maybe ()
allApplications AuthorizationList
software) Bool -> Bool -> Bool
|| Maybe () -> Bool
forall a. Maybe a -> Bool
isJust (AuthorizationList -> Maybe ()
allApplications AuthorizationList
tee)) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure VerificationError
AndroidKeyAllApplicationsFieldFound

    -- 5.b For the following, use only the teeEnforced authorization list if the
    -- RP wants to accept only keys from a trusted execution environment,
    -- otherwise use the union of teeEnforced and softwareEnforced.
    -- 5.b.1 The value in the AuthorizationList.origin field is equal to KM_ORIGIN_GENERATED.
    -- 5.b.2 The value in the AuthorizationList.purpose field is equal to KM_PURPOSE_SIGN.
    -- NOTE: This statement is ambiguous as the purpose field is a set. Existing libraries take the same approach, checking if KM_PURPOSE_SIGN is the only member.
    let targetSet :: Set Integer
targetSet = Integer -> Set Integer
forall a. a -> Set a
Set.singleton Integer
kmPurposeSign
    case TrustLevel
requiredTrustLevel of
      TrustLevel
SoftwareEnforced -> do
        Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (AuthorizationList -> Maybe Integer
origin AuthorizationList
software Maybe Integer -> Maybe Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
kmOriginGenerated Bool -> Bool -> Bool
|| AuthorizationList -> Maybe Integer
origin AuthorizationList
tee Maybe Integer -> Maybe Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
kmOriginGenerated) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Maybe Integer -> Maybe Integer -> VerificationError
AndroidKeyOriginFieldInvalid (AuthorizationList -> Maybe Integer
origin AuthorizationList
tee) (AuthorizationList -> Maybe Integer
origin AuthorizationList
software)
        Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Set Integer -> Maybe (Set Integer)
forall a. a -> Maybe a
Just Set Integer
targetSet Maybe (Set Integer) -> Maybe (Set Integer) -> Bool
forall a. Eq a => a -> a -> Bool
== AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
software Bool -> Bool -> Bool
|| Set Integer -> Maybe (Set Integer)
forall a. a -> Maybe a
Just Set Integer
targetSet Maybe (Set Integer) -> Maybe (Set Integer) -> Bool
forall a. Eq a => a -> a -> Bool
== AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
tee) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Maybe (Set Integer) -> Maybe (Set Integer) -> VerificationError
AndroidKeyPurposeFieldInvalid (AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
tee) (AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
software)
        pure ()
      TrustLevel
TeeEnforced -> do
        Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (AuthorizationList -> Maybe Integer
origin AuthorizationList
tee Maybe Integer -> Maybe Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
kmOriginGenerated) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Maybe Integer -> Maybe Integer -> VerificationError
AndroidKeyOriginFieldInvalid (AuthorizationList -> Maybe Integer
origin AuthorizationList
tee) Maybe Integer
forall a. Maybe a
Nothing
        Bool
-> Validation (NonEmpty VerificationError) ()
-> Validation (NonEmpty VerificationError) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Set Integer -> Maybe (Set Integer)
forall a. a -> Maybe a
Just Set Integer
targetSet Maybe (Set Integer) -> Maybe (Set Integer) -> Bool
forall a. Eq a => a -> a -> Bool
== AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
tee) (Validation (NonEmpty VerificationError) ()
 -> Validation (NonEmpty VerificationError) ())
-> (VerificationError
    -> Validation (NonEmpty VerificationError) ())
-> VerificationError
-> Validation (NonEmpty VerificationError) ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VerificationError -> Validation (NonEmpty VerificationError) ()
forall e a. e -> Validation (NonEmpty e) a
failure (VerificationError -> Validation (NonEmpty VerificationError) ())
-> VerificationError -> Validation (NonEmpty VerificationError) ()
forall a b. (a -> b) -> a -> b
$ Maybe (Set Integer) -> Maybe (Set Integer) -> VerificationError
AndroidKeyPurposeFieldInvalid (AuthorizationList -> Maybe (Set Integer)
purpose AuthorizationList
tee) Maybe (Set Integer)
forall a. Maybe a
Nothing
        pure ()

    -- 6. If successful, return implementation-specific values representing attestation type Basic and attestation trust
    -- path x5c.
    pure $
      AttestationType ('Verifiable 'Fido2) -> SomeAttestationType
forall (k :: AttestationKind).
AttestationType k -> SomeAttestationType
M.SomeAttestationType (AttestationType ('Verifiable 'Fido2) -> SomeAttestationType)
-> AttestationType ('Verifiable 'Fido2) -> SomeAttestationType
forall a b. (a -> b) -> a -> b
$
        VerifiableAttestationType
-> AttestationChain 'Fido2 -> AttestationType ('Verifiable 'Fido2)
forall (p :: ProtocolKind).
VerifiableAttestationType
-> AttestationChain p -> AttestationType ('Verifiable p)
M.AttestationTypeVerifiable VerifiableAttestationType
M.VerifiableAttestationTypeBasic (NonEmpty SignedCertificate -> AttestationChain 'Fido2
M.Fido2Chain NonEmpty SignedCertificate
x5c)

  asfTrustAnchors :: Format -> VerifiableAttestationType -> CertificateStore
asfTrustAnchors Format
_ VerifiableAttestationType
_ = CertificateStore
forall a. Monoid a => a
mempty

-- | The default Android Key format configuration. Requires the attestation to
-- be backed by a Trusted Executing Environment (TEE).
format :: M.SomeAttestationStatementFormat
format :: SomeAttestationStatementFormat
format = Format -> SomeAttestationStatementFormat
forall a.
AttestationStatementFormat a =>
a -> SomeAttestationStatementFormat
M.SomeAttestationStatementFormat (Format -> SomeAttestationStatementFormat)
-> Format -> SomeAttestationStatementFormat
forall a b. (a -> b) -> a -> b
$ TrustLevel -> Format
Format TrustLevel
TeeEnforced