packages feed

webauthn-0.1.0.0: src/Crypto/WebAuthn/Model/Identifier.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}

-- | Stability: experimental
-- This module concerns identification of authenticatiors, notably 'AAGUID',
-- 'SubjectKeyIdentifier' and a generic identifier type 'AuthenticatorIdentifier'
module Crypto.WebAuthn.Model.Identifier
  ( AuthenticatorIdentifier (..),
    AAGUID (..),
    SubjectKeyIdentifier (..),
  )
where

import Crypto.Hash (Digest, SHA1)
import Crypto.WebAuthn.Internal.ToJSONOrphans ()
import qualified Crypto.WebAuthn.Model.Kinds as M
import Data.Aeson (KeyValue ((.=)), ToJSON (toJSON), Value (String), object)
import Data.ByteArray (convert)
import qualified Data.ByteString as BS
import Data.Hashable (Hashable (hashWithSalt), hashUsing)
import Data.UUID (UUID)

-- | [(spec)](https://www.w3.org/TR/webauthn-2/#aaguid)
newtype AAGUID = AAGUID {unAAGUID :: UUID}
  deriving (Eq, Show)
  deriving newtype (Hashable, ToJSON)

-- | A way to identify an authenticator
data AuthenticatorIdentifier (p :: M.ProtocolKind) where
  -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-aaguid)
  -- A known FIDO2 [authenticator](https://www.w3.org/TR/webauthn-2/#authenticator),
  -- identified by a 'AAGUID'. Note that the 'AAGUID' may be zero, meaning that
  -- we were able to verify that the [public key credential](https://www.w3.org/TR/webauthn-2/#public-key-credential).
  -- was generated by a trusted [authenticator](https://www.w3.org/TR/webauthn-2/#authenticator),
  -- but we don't know which model it is.
  AuthenticatorIdentifierFido2 ::
    {idAaguid :: AAGUID} ->
    AuthenticatorIdentifier 'M.Fido2
  -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-attestationcertificatekeyidentifiers)
  -- A known FIDO U2F [authenticator](https://www.w3.org/TR/webauthn-2/#authenticator),
  -- identified by a 'SubjectKeyIdentifier'. Clients that don't implement CTAP2
  -- (which is used to communicate with FIDO2 authenticators) will use U2F to
  -- communicate with the authenticator instead, which doesn't have support for 'AAGUID's.
  AuthenticatorIdentifierFidoU2F ::
    {idSubjectKeyIdentifier :: SubjectKeyIdentifier} ->
    AuthenticatorIdentifier 'M.FidoU2F

deriving instance Show (AuthenticatorIdentifier p)

deriving instance Eq (AuthenticatorIdentifier p)

instance ToJSON (AuthenticatorIdentifier p) where
  toJSON (AuthenticatorIdentifierFido2 aaguid) =
    object
      [ "tag" .= String "AuthenticatorIdentifierFido2",
        "idAaguid" .= aaguid
      ]
  toJSON (AuthenticatorIdentifierFidoU2F subjectKeyIdentifier) =
    object
      [ "tag" .= String "AuthenticatorIdentifierFidoU2F",
        "idSubjectKeyIdentifier" .= subjectKeyIdentifier
      ]

-- | [(spec)](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2)
-- This type represents method 1 of computing the identifier, as used in the
-- [attestationCertificateKeyIdentifiers](https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#dom-metadatablobpayloadentry-attestationcertificatekeyidentifiers)
-- field of the [Metadata Service](https://fidoalliance.org/metadata/)
newtype SubjectKeyIdentifier = SubjectKeyIdentifier {unSubjectKeyIdentifier :: Digest SHA1}
  deriving (Eq, Show)

instance ToJSON SubjectKeyIdentifier where
  toJSON = toJSON @BS.ByteString . convert . unSubjectKeyIdentifier

instance Hashable SubjectKeyIdentifier where
  hashWithSalt = hashUsing @BS.ByteString (convert . unSubjectKeyIdentifier)