diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,6 +1,11 @@
+### 0.8.0.0
+
+* [#178](https://github.com/tweag/webauthn/pull/178) Remove orphan instance for ToJSON ByteString.
+  Use newtypes for the binary data including the PNG icons for authenticators and the cryptographic values.
+
 ### 0.7.0.0
 
-* [174](https://github.com/tweag/webauthn/pull/174) Correctly verify packed
+* [#174](https://github.com/tweag/webauthn/pull/174) Correctly verify packed
   attestation when the AAGUID extension of the certitificate is missing. This is
   a backwards-incompatible change for packed attestation responses that
   previously failed due to the missing AAGUID extension. These responses now
diff --git a/src/Crypto/WebAuthn/AttestationStatementFormat/AndroidKey.hs b/src/Crypto/WebAuthn/AttestationStatementFormat/AndroidKey.hs
--- a/src/Crypto/WebAuthn/AttestationStatementFormat/AndroidKey.hs
+++ b/src/Crypto/WebAuthn/AttestationStatementFormat/AndroidKey.hs
@@ -29,7 +29,6 @@
 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
@@ -155,7 +154,7 @@
 
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-android-key-attestation)
 data Statement = Statement
-  { sig :: ByteString,
+  { sig :: Cose.Signature,
     x5c :: NonEmpty X509.SignedCertificate,
     -- | Holds both the "alg" from the statement and the public key from the
     -- X.509 certificate
@@ -237,7 +236,7 @@
 
   asfDecode _ xs =
     case (xs !? "alg", xs !? "sig", xs !? "x5c") of
-      (Just (CBOR.TInt algId), Just (CBOR.TBytes sig), Just (CBOR.TList (NE.nonEmpty -> Just x5cRaw))) -> do
+      (Just (CBOR.TInt algId), Just (CBOR.TBytes (Cose.Signature -> sig)), Just (CBOR.TList (NE.nonEmpty -> Just x5cRaw))) -> do
         alg <- Cose.toCoseSignAlg algId
         x5c@(credCert :| _) <- forM x5cRaw $ \case
           CBOR.TBytes certBytes ->
@@ -260,7 +259,7 @@
 
   asfEncode _ Statement {..} =
     CBOR.TMap
-      [ (CBOR.TString "sig", CBOR.TBytes sig),
+      [ (CBOR.TString "sig", CBOR.TBytes $ Cose.unSignature sig),
         (CBOR.TString "alg", CBOR.TInt $ Cose.fromCoseSignAlg $ Cose.signAlg pubKeyAndAlg),
         ( CBOR.TString "x5c",
           CBOR.TList $
@@ -278,7 +277,7 @@
 
     -- 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 = rawData <> convert (M.unClientDataHash clientDataHash)
+    let signedData = Cose.Message $ rawData <> convert (M.unClientDataHash clientDataHash)
     case Cose.verify pubKeyAndAlg signedData sig of
       Right () -> pure ()
       Left err -> failure $ VerificationFailure err
diff --git a/src/Crypto/WebAuthn/AttestationStatementFormat/FidoU2F.hs b/src/Crypto/WebAuthn/AttestationStatementFormat/FidoU2F.hs
--- a/src/Crypto/WebAuthn/AttestationStatementFormat/FidoU2F.hs
+++ b/src/Crypto/WebAuthn/AttestationStatementFormat/FidoU2F.hs
@@ -18,6 +18,7 @@
 import Control.Monad (unless)
 import Crypto.PubKey.ECC.Types (CurveName (SEC_p256r1))
 import qualified Crypto.WebAuthn.Cose.PublicKeyWithSignAlg as Cose
+import Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
 import Crypto.WebAuthn.Internal.Utils (failure)
 import qualified Crypto.WebAuthn.Model.Types as M
 import Data.Aeson (ToJSON, object, toJSON, (.=))
@@ -66,7 +67,7 @@
   toJSON Statement {..} =
     object
       [ "attestnCert" .= attCert,
-        "sig" .= sig
+        "sig" .= PrettyHexByteString sig
       ]
 
 instance M.AttestationStatementFormat Format where
diff --git a/src/Crypto/WebAuthn/AttestationStatementFormat/Packed.hs b/src/Crypto/WebAuthn/AttestationStatementFormat/Packed.hs
--- a/src/Crypto/WebAuthn/AttestationStatementFormat/Packed.hs
+++ b/src/Crypto/WebAuthn/AttestationStatementFormat/Packed.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE ApplicativeDo #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE ViewPatterns #-}
 
 -- | Stability: experimental
 -- This module implements the
@@ -25,7 +26,6 @@
 import Data.Aeson (ToJSON, object, toJSON, (.=))
 import Data.Bifunctor (first)
 import Data.ByteArray (convert)
-import qualified Data.ByteString as BS
 import Data.HashMap.Strict ((!?))
 import Data.List.NonEmpty (NonEmpty ((:|)), toList)
 import qualified Data.List.NonEmpty as NE
@@ -45,7 +45,7 @@
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-packed-attestation)
 data Statement = Statement
   { alg :: Cose.CoseSignAlg,
-    sig :: BS.ByteString,
+    sig :: Cose.Signature,
     -- The AAGUID extension is optional
     x5c :: Maybe (NE.NonEmpty X509.SignedCertificate, Maybe IdFidoGenCeAAGUID)
   }
@@ -98,7 +98,7 @@
 
   asfDecode _ xs =
     case (xs !? "alg", xs !? "sig", xs !? "x5c") of
-      (Just (CBOR.TInt algId), Just (CBOR.TBytes sig), mx5c) -> do
+      (Just (CBOR.TInt algId), Just (CBOR.TBytes (Cose.Signature -> sig)), mx5c) -> do
         alg <- Cose.toCoseSignAlg algId
         x5c <- case mx5c of
           Nothing -> pure Nothing
@@ -123,7 +123,7 @@
 
   asfEncode _ Statement {..} =
     CBOR.TMap
-      ( [ (CBOR.TString "sig", CBOR.TBytes sig),
+      ( [ (CBOR.TString "sig", CBOR.TBytes $ Cose.unSignature sig),
           (CBOR.TString "alg", CBOR.TInt $ Cose.fromCoseSignAlg alg)
         ]
           ++ case x5c of
@@ -142,7 +142,7 @@
     Statement {alg = stmtAlg, sig = stmtSig, x5c = stmtx5c}
     M.AuthenticatorData {M.adRawData = M.WithRaw rawData, M.adAttestedCredentialData = credData}
     clientDataHash = do
-      let signedData = rawData <> convert (M.unClientDataHash clientDataHash)
+      let signedData = Cose.Message $ rawData <> convert (M.unClientDataHash clientDataHash)
       case stmtx5c of
         -- Self attestation
         Nothing -> do
@@ -165,7 +165,8 @@
               pubKey = X509.certPubKey cert
           -- Verify that sig is a valid signature over the concatenation of authenticatorData and clientDataHash using
           -- the attestation public key in attestnCert with the algorithm specified in alg.
-          case X509.verifySignature (X509.SignatureALG X509.HashSHA256 X509.PubKeyALG_EC) pubKey signedData stmtSig of
+          -- FIXME: This is wrong, we should use alg!
+          case X509.verifySignature (X509.SignatureALG X509.HashSHA256 X509.PubKeyALG_EC) pubKey (Cose.unMessage signedData) (Cose.unSignature stmtSig) of
             X509.SignaturePass -> pure ()
             X509.SignatureFailed err -> failure $ VerificationFailure err
 
diff --git a/src/Crypto/WebAuthn/AttestationStatementFormat/TPM.hs b/src/Crypto/WebAuthn/AttestationStatementFormat/TPM.hs
--- a/src/Crypto/WebAuthn/AttestationStatementFormat/TPM.hs
+++ b/src/Crypto/WebAuthn/AttestationStatementFormat/TPM.hs
@@ -26,6 +26,7 @@
 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.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
 import Crypto.WebAuthn.Internal.Utils (IdFidoGenCeAAGUID (IdFidoGenCeAAGUID), failure)
 import Crypto.WebAuthn.Model.Identifier (AAGUID)
 import qualified Crypto.WebAuthn.Model.Types as M
@@ -124,8 +125,8 @@
 -- | The TPMS_CERTIFY_INFO structure as specified in [TPMv2-Part2](https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf)
 -- section 10.12.3.
 data TPMSCertifyInfo = TPMSCertifyInfo
-  { tpmsciName :: BS.ByteString,
-    tpmsciQualifiedName :: BS.ByteString
+  { tpmsciName :: PrettyHexByteString,
+    tpmsciQualifiedName :: PrettyHexByteString
   }
   deriving (Eq, Show, Generic, ToJSON)
 
@@ -135,8 +136,8 @@
 data TPMSAttest = TPMSAttest
   { tpmsaMagic :: Word32,
     tpmsaType :: Word16,
-    tpmsaQualifiedSigner :: BS.ByteString,
-    tpmsaExtraData :: BS.ByteString,
+    tpmsaQualifiedSigner :: PrettyHexByteString,
+    tpmsaExtraData :: PrettyHexByteString,
     tpmsaClockInfo :: TPMSClockInfo,
     tpmsaFirmwareVersion :: Word64,
     tpmsaAttested :: TPMSCertifyInfo
@@ -170,10 +171,10 @@
 -- [TPMv2-Part2](https://www.trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-2-Structures-01.38.pdf)
 -- section 12.2.3.2.
 data TPMUPublicId
-  = TPM2BPublicKeyRSA BS.ByteString
+  = TPM2BPublicKeyRSA PrettyHexByteString
   | TPMSECCPoint
-      { tpmseX :: BS.ByteString,
-        tpmseY :: BS.ByteString
+      { tpmseX :: PrettyHexByteString,
+        tpmseY :: PrettyHexByteString
       }
   deriving (Eq, Show, Generic, ToJSON)
 
@@ -183,7 +184,7 @@
     tpmtpNameAlg :: TPMAlgId,
     tpmtpNameAlgRaw :: Word16,
     tpmtpObjectAttributes :: TPMAObject,
-    tpmtpAuthPolicy :: BS.ByteString,
+    tpmtpAuthPolicy :: PrettyHexByteString,
     tpmtpParameters :: TPMUPublicParms,
     tpmtpUnique :: TPMUPublicId
   }
@@ -204,6 +205,12 @@
   }
   deriving (Eq, Show)
 
+newtype CertInfoBytes = CertInfoBytes {unCertInfoBytes :: BS.ByteString}
+  deriving newtype (Eq, Show)
+
+newtype PubAreaBytes = PubAreaBytes {unPubAreaBytes :: BS.ByteString}
+  deriving newtype (Eq, Show)
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-tpm-attestation)
 data Statement = Statement
   { x5c :: NE.NonEmpty X509.SignedCertificate,
@@ -214,11 +221,11 @@
     aaguidExt :: Maybe IdFidoGenCeAAGUID,
     extendedKeyUsage :: [X509.ExtKeyUsagePurpose],
     basicConstraintsCA :: Bool,
-    sig :: BS.ByteString,
+    sig :: Cose.Signature,
     certInfo :: TPMSAttest,
-    certInfoRaw :: BS.ByteString,
+    certInfoRaw :: CertInfoBytes,
     pubArea :: TPMTPublic,
-    pubAreaRaw :: BS.ByteString,
+    pubAreaRaw :: PubAreaBytes,
     pubAreaKey :: Cose.PublicKey
   }
   deriving (Eq, Show)
@@ -357,49 +364,57 @@
 
   asfDecode _ xs =
     case (xs !? "ver", xs !? "alg", xs !? "x5c", xs !? "sig", xs !? "certInfo", xs !? "pubArea") of
-      (Just (CBOR.TString "2.0"), Just (CBOR.TInt algId), Just (CBOR.TList (NE.nonEmpty -> Just x5cRaw)), Just (CBOR.TBytes sig), Just (CBOR.TBytes certInfoRaw), Just (CBOR.TBytes pubAreaRaw)) ->
-        do
-          x5c@(signedAikCert :| _) <- forM x5cRaw $ \case
-            CBOR.TBytes certBytes ->
-              first (("Failed to decode signed certificate: " <>) . Text.pack) (X509.decodeSignedCertificate certBytes)
-            cert ->
-              Left $ "Certificate CBOR value is not bytes: " <> Text.pack (show cert)
-          alg <- Cose.toCoseSignAlg algId
-          -- The get interface requires lazy bytestrings but we typically use
-          -- strict bytestrings in the library, so we have to convert between
-          -- them
-          certInfo <- case Get.runGetOrFail getTPMAttest (LBS.fromStrict certInfoRaw) of
-            Left (_, _, err) -> Left $ "Failed to decode certInfo: " <> Text.pack (show err)
-            Right (_, _, res) -> pure res
-          pubArea <- case Get.runGetOrFail getTPMTPublic (LBS.fromStrict pubAreaRaw) of
-            Left (_, _, err) -> Left $ "Failed to decode pubArea: " <> Text.pack (show err)
-            Right (_, _, res) -> pure res
-          pubAreaKey <- extractPublicKey pubArea
+      ( Just (CBOR.TString "2.0"),
+        Just (CBOR.TInt algId),
+        Just (CBOR.TList (NE.nonEmpty -> Just x5cRaw)),
+        Just (CBOR.TBytes (Cose.Signature -> sig)),
+        Just (CBOR.TBytes (CertInfoBytes -> certInfoRaw)),
+        Just (CBOR.TBytes (PubAreaBytes -> pubAreaRaw))
+        ) ->
+          do
+            x5c@(signedAikCert :| _) <- forM x5cRaw $ \case
+              CBOR.TBytes certBytes ->
+                first (("Failed to decode signed certificate: " <>) . Text.pack) (X509.decodeSignedCertificate certBytes)
+              cert ->
+                Left $ "Certificate CBOR value is not bytes: " <> Text.pack (show cert)
+            alg <- Cose.toCoseSignAlg algId
+            -- The get interface requires lazy bytestrings but we typically use
+            -- strict bytestrings in the library, so we have to convert between
+            -- them
+            certInfo <- decodeCertInfoBytes certInfoRaw
+            pubArea <- decodePubAreaBytes pubAreaRaw
+            pubAreaKey <- extractPublicKey pubArea
 
-          let aikCert = X509.getCertificate signedAikCert
+            let aikCert = X509.getCertificate signedAikCert
 
-          aikCertPubKey <- Cose.fromX509 $ X509.certPubKey aikCert
-          aikPubKeyAndAlg <- Cose.makePublicKeyWithSignAlg aikCertPubKey alg
+            aikCertPubKey <- Cose.fromX509 $ X509.certPubKey aikCert
+            aikPubKeyAndAlg <- Cose.makePublicKeyWithSignAlg aikCertPubKey alg
 
-          subjectAlternativeName <- case X509.extensionGetE (X509.certExtensions aikCert) of
-            Just (Right ext) -> pure ext
-            Just (Left err) -> Left $ "Failed to decode certificate subject alternative name extension: " <> Text.pack err
-            Nothing -> Left "Certificate subject alternative name extension is missing"
-          aaguidExt <- case X509.extensionGetE (X509.certExtensions aikCert) of
-            Just (Right ext) -> pure $ Just ext
-            Just (Left err) -> Left $ "Failed to decode certificate aaguid extension: " <> Text.pack err
-            Nothing -> pure Nothing
-          X509.ExtExtendedKeyUsage extendedKeyUsage <- case X509.extensionGetE (X509.certExtensions aikCert) of
-            Just (Right ext) -> pure ext
-            Just (Left err) -> Left $ "Failed to decode certificate extended key usage extension: " <> Text.pack err
-            Nothing -> Left "Certificate extended key usage extension is missing"
-          X509.ExtBasicConstraints basicConstraintsCA _ <- case X509.extensionGetE (X509.certExtensions aikCert) of
-            Just (Right ext) -> pure ext
-            Just (Left err) -> Left $ "Failed to decode certificate basic constraints extension: " <> Text.pack err
-            Nothing -> Left "Certificate basic constraints extension is missing"
-          Right $ Statement {..}
+            subjectAlternativeName <- case X509.extensionGetE (X509.certExtensions aikCert) of
+              Just (Right ext) -> pure ext
+              Just (Left err) -> Left $ "Failed to decode certificate subject alternative name extension: " <> Text.pack err
+              Nothing -> Left "Certificate subject alternative name extension is missing"
+            aaguidExt <- case X509.extensionGetE (X509.certExtensions aikCert) of
+              Just (Right ext) -> pure $ Just ext
+              Just (Left err) -> Left $ "Failed to decode certificate aaguid extension: " <> Text.pack err
+              Nothing -> pure Nothing
+            X509.ExtExtendedKeyUsage extendedKeyUsage <- case X509.extensionGetE (X509.certExtensions aikCert) of
+              Just (Right ext) -> pure ext
+              Just (Left err) -> Left $ "Failed to decode certificate extended key usage extension: " <> Text.pack err
+              Nothing -> Left "Certificate extended key usage extension is missing"
+            X509.ExtBasicConstraints basicConstraintsCA _ <- case X509.extensionGetE (X509.certExtensions aikCert) of
+              Just (Right ext) -> pure ext
+              Just (Left err) -> Left $ "Failed to decode certificate basic constraints extension: " <> Text.pack err
+              Nothing -> Left "Certificate basic constraints extension is missing"
+            Right $ Statement {..}
       _ -> Left $ "CBOR map didn't have expected value types (ver: \"2.0\", alg: int, x5c: non-empty list, sig: bytes, certInfo: bytes, pubArea: bytes): " <> Text.pack (show xs)
     where
+      decodeCertInfoBytes :: CertInfoBytes -> Either Text TPMSAttest
+      decodeCertInfoBytes (CertInfoBytes bytes) =
+        case Get.runGetOrFail getTPMAttest (LBS.fromStrict bytes) of
+          Left (_, _, err) -> Left $ "Failed to decode certInfo: " <> Text.pack (show err)
+          Right (_, _, res) -> pure res
+
       getTPMAttest :: Get.Get TPMSAttest
       getTPMAttest = do
         tpmsaMagic <- Get.getWord32be
@@ -427,11 +442,17 @@
         tpmsciQualifiedName <- getTPMByteString
         pure TPMSCertifyInfo {..}
 
-      getTPMByteString :: Get.Get BS.ByteString
+      getTPMByteString :: Get.Get PrettyHexByteString
       getTPMByteString = do
         size <- Get.getWord16be
-        Get.getByteString (fromIntegral size)
+        PrettyHexByteString <$> Get.getByteString (fromIntegral size)
 
+      decodePubAreaBytes :: PubAreaBytes -> Either Text TPMTPublic
+      decodePubAreaBytes (PubAreaBytes bytes) =
+        case Get.runGetOrFail getTPMTPublic (LBS.fromStrict bytes) of
+          Left (_, _, err) -> Left $ "Failed to decode certInfo: " <> Text.pack (show err)
+          Right (_, _, res) -> pure res
+
       getTPMTPublic :: Get.Get TPMTPublic
       getTPMTPublic = do
         tpmtpType <- toTPMAlgId =<< Get.getWord16be
@@ -479,7 +500,7 @@
         TPMTPublic
           { tpmtpType = TPMAlgRSA,
             tpmtpParameters = TPMSRSAParms {..},
-            tpmtpUnique = TPM2BPublicKeyRSA nb
+            tpmtpUnique = TPM2BPublicKeyRSA (PrettyHexByteString nb)
           } =
           Cose.checkPublicKey
             Cose.PublicKeyRSA
@@ -490,7 +511,7 @@
         TPMTPublic
           { tpmtpType = TPMAlgECC,
             tpmtpParameters = TPMSECCParms {..},
-            tpmtpUnique = TPMSECCPoint {..}
+            tpmtpUnique = TPMSECCPoint {tpmseX = PrettyHexByteString tpmseX, tpmseY = PrettyHexByteString tpmseY}
           } =
           Cose.checkPublicKey
             Cose.PublicKeyECDSA
@@ -507,9 +528,9 @@
         ( CBOR.TString "x5c",
           CBOR.TList $ map (CBOR.TBytes . X509.encodeSignedObject) $ NE.toList x5c
         ),
-        (CBOR.TString "sig", CBOR.TBytes sig),
-        (CBOR.TString "certInfo", CBOR.TBytes certInfoRaw),
-        (CBOR.TString "pubArea", CBOR.TBytes pubAreaRaw)
+        (CBOR.TString "sig", CBOR.TBytes $ Cose.unSignature sig),
+        (CBOR.TString "certInfo", CBOR.TBytes $ unCertInfoBytes certInfoRaw),
+        (CBOR.TString "pubArea", CBOR.TBytes $ unPubAreaBytes pubAreaRaw)
       ]
 
   type AttStmtVerificationError Format = VerificationError
@@ -546,7 +567,7 @@
       -- the hash algorithm employed in "alg".
       case hashWithCorrectAlgorithm (Cose.signAlg aikPubKeyAndAlg) attToBeSigned of
         Just attHash -> do
-          let extraData = tpmsaExtraData certInfo
+          let PrettyHexByteString extraData = tpmsaExtraData certInfo
           unless (attHash == extraData) . failure $ HashMismatch attHash extraData
           pure ()
         Nothing -> failure HashFunctionUnknown
@@ -557,8 +578,8 @@
       -- nameAlg field of pubArea using the procedure specified in
       -- [TPMv2-Part1] section 16.
       let mPubAreaHash = case tpmtpNameAlg pubArea of
-            TPMAlgSHA1 -> Right $ BA.convert $ hashWith SHA1 pubAreaRaw
-            TPMAlgSHA256 -> Right $ BA.convert $ hashWith SHA256 pubAreaRaw
+            TPMAlgSHA1 -> Right $ BA.convert $ hashWith SHA1 $ unPubAreaBytes pubAreaRaw
+            TPMAlgSHA256 -> Right $ BA.convert $ hashWith SHA256 $ unPubAreaBytes pubAreaRaw
             TPMAlgECC -> Left TPMAlgECC
             TPMAlgRSA -> Left TPMAlgRSA
 
@@ -569,7 +590,7 @@
                   Put.putWord16be (tpmtpNameAlgRaw pubArea)
                   Put.putByteString pubAreaHash
 
-          let name = tpmsciName (tpmsaAttested certInfo)
+          let PrettyHexByteString name = tpmsciName (tpmsaAttested certInfo)
           unless (name == pubName) . failure $ NameMismatch pubName name
           pure ()
         Left alg -> failure $ NameAlgorithmInvalid alg
@@ -585,7 +606,7 @@
 
       -- 4.8 Verify the sig is a valid signature over certInfo using the
       -- attestation public key in aikCert with the algorithm specified in alg.
-      case Cose.verify aikPubKeyAndAlg certInfoRaw sig of
+      case Cose.verify aikPubKeyAndAlg (Cose.Message $ unCertInfoBytes certInfoRaw) sig of
         Right () -> pure ()
         Left err -> failure $ VerificationFailure err
 
diff --git a/src/Crypto/WebAuthn/Cose/Internal/Verify.hs b/src/Crypto/WebAuthn/Cose/Internal/Verify.hs
--- a/src/Crypto/WebAuthn/Cose/Internal/Verify.hs
+++ b/src/Crypto/WebAuthn/Cose/Internal/Verify.hs
@@ -25,6 +25,8 @@
     fromX509,
 
     -- * Signature verification
+    Cose.Message (..),
+    Cose.Signature (..),
     verify,
 
     -- * Hash Conversions to cryptonite types
@@ -46,7 +48,6 @@
 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.ToJSONOrphans ()
 import qualified Data.ASN1.BinaryEncoding as ASN1
 import qualified Data.ASN1.Encoding as ASN1
 import qualified Data.ASN1.Types as ASN1
@@ -63,7 +64,7 @@
   Cose.checkPublicKey
     Cose.PublicKeyEdDSA
       { eddsaCurve = Cose.CoseCurveEd25519,
-        eddsaX = convert key
+        eddsaX = Cose.EdDSAKeyBytes $ convert key
       }
 fromX509 (X509.PubKeyEC X509.PubKeyEC_Named {..}) = do
   let curve = ECC.getCurveByName pubkeyEC_name
@@ -87,7 +88,8 @@
 -- 'Cose.PublicKeyWithSignAlg' Returns an error if the signature algorithm
 -- doesn't match. Also returns an error if the signature wasn't valid or for
 -- other errors.
-verify :: Cose.PublicKeyWithSignAlg -> BS.ByteString -> BS.ByteString -> Either Text ()
+-- FIXME: https://w3c.github.io/webauthn/#sctn-signature-attestation-types kind of documents this, but not for all formats. This is notably not really related to COSE, but rather webauthn's own definitions. The spec should be made less ambiguous, file upstream issues and refactor this code
+verify :: Cose.PublicKeyWithSignAlg -> Cose.Message -> Cose.Signature -> Either Text ()
 verify
   Cose.PublicKeyWithSignAlg
     { publicKey = Cose.PublicKey Cose.PublicKeyEdDSA {eddsaCurve = Cose.CoseCurveEd25519, ..},
@@ -95,13 +97,13 @@
     }
   msg
   sig = do
-    key <- case Ed25519.publicKey eddsaX of
+    key <- case Ed25519.publicKey $ Cose.unEdDSAKeyBytes eddsaX of
       CryptoFailed err -> Left $ "Failed to create Ed25519 public key: " <> Text.pack (show err)
       CryptoPassed res -> pure res
-    sig <- case Ed25519.signature sig of
+    sig <- case Ed25519.signature (Cose.unSignature sig) of
       CryptoFailed err -> Left $ "Failed to create Ed25519 signature: " <> Text.pack (show err)
       CryptoPassed res -> pure res
-    if Ed25519.verify key msg sig
+    if Ed25519.verify key (Cose.unMessage msg) sig
       then Right ()
       else Left "EdDSA Signature invalid"
 verify
@@ -124,14 +126,14 @@
     -- > For COSEAlgorithmIdentifier -7 (ES256), and other ECDSA-based algorithms,
     -- the `sig` value MUST be encoded as an ASN.1 DER Ecdsa-Sig-Value, as defined
     -- in [RFC3279](https://www.w3.org/TR/webauthn-2/#biblio-rfc3279) section 2.2.3.
-    sig <- case ASN1.decodeASN1' ASN1.DER sig of
+    sig <- case ASN1.decodeASN1' ASN1.DER (Cose.unSignature sig) of
       Left err -> Left $ "Failed to decode ECDSA DER value: " <> Text.pack (show err)
       -- Ecdsa-Sig-Value in https://datatracker.ietf.org/doc/html/rfc3279#section-2.2.3
       Right [ASN1.Start ASN1.Sequence, ASN1.IntVal r, ASN1.IntVal s, ASN1.End ASN1.Sequence] ->
         pure $ ECDSA.Signature r s
       Right asns -> Left $ "Unexpected ECDSA ASN.1 structure: " <> Text.pack (show asns)
 
-    if ECDSA.verify hash key sig msg
+    if ECDSA.verify hash key sig (Cose.unMessage msg)
       then Right ()
       else Left "ECDSA Signature invalid"
 verify
@@ -153,7 +155,7 @@
               public_n = rsaN,
               public_e = rsaE
             }
-    if RSA.verify (Just hash) key msg sig
+    if RSA.verify (Just hash) key (Cose.unMessage msg) (Cose.unSignature sig)
       then Right ()
       else Left "RSA Signature invalid"
 verify key _ _ = error $ "PublicKeyWithSignAlg invariant violated for public key " <> show key <> ". This should not occur unless the PublicKeyWithSignAlg module has a bug"
diff --git a/src/Crypto/WebAuthn/Cose/PublicKey.hs b/src/Crypto/WebAuthn/Cose/PublicKey.hs
--- a/src/Crypto/WebAuthn/Cose/PublicKey.hs
+++ b/src/Crypto/WebAuthn/Cose/PublicKey.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE StandaloneDeriving #-}
 
 -- | Stability: experimental
 -- This module contains a partial implementation of the
@@ -10,6 +11,7 @@
     UncheckedPublicKey (..),
     checkPublicKey,
     PublicKey (PublicKey),
+    EdDSAKeyBytes (..),
 
     -- * COSE Elliptic Curves
     CoseCurveEdDSA (..),
@@ -24,15 +26,19 @@
 import qualified Crypto.PubKey.ECC.Prim as ECC
 import qualified Crypto.PubKey.ECC.Types as ECC
 import qualified Crypto.PubKey.Ed25519 as Ed25519
-import Crypto.WebAuthn.Internal.ToJSONOrphans ()
+import Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
 import Data.Aeson (ToJSON)
 import qualified Data.ByteString as BS
-import qualified Data.ByteString.Base16 as Base16
 import Data.Text (Text)
 import qualified Data.Text as Text
-import qualified Data.Text.Encoding as Text
 import GHC.Generics (Generic)
 
+-- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-7.2)
+-- This contains the public key bytes.
+newtype EdDSAKeyBytes = EdDSAKeyBytes {unEdDSAKeyBytes :: BS.ByteString}
+  deriving newtype (Eq)
+  deriving (Show, ToJSON) via PrettyHexByteString
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#credentialpublickey)
 -- A structured representation of a [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-7)
 -- limited to what is know to be necessary for Webauthn public keys for the
@@ -65,7 +71,7 @@
         eddsaCurve :: CoseCurveEdDSA,
         -- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-7.2)
         -- This contains the public key bytes.
-        eddsaX :: BS.ByteString
+        eddsaX :: EdDSAKeyBytes
       }
   | -- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-2.1)
     -- ECDSA Signature Algorithm
@@ -113,13 +119,23 @@
         -- GCD(e,\\lambda(n)) = 1, where \\lambda(n) = LCM(r_1 - 1, ..., r_u - 1)
         rsaE :: Integer
       }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON UncheckedPublicKey
+
 -- | Same as 'UncheckedPublicKey', but checked to be valid using
 -- 'checkPublicKey'.
 newtype PublicKey = CheckedPublicKey UncheckedPublicKey
-  deriving newtype (Eq, Show, ToJSON)
+  deriving newtype (Eq, Show)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON PublicKey
+
 -- | Returns the 'UncheckedPublicKey' for a t'PublicKey'
 pattern PublicKey :: UncheckedPublicKey -> PublicKey
 pattern PublicKey k <- CheckedPublicKey k
@@ -139,9 +155,9 @@
           <> " bytes, it has "
           <> Text.pack (show actualSize)
           <> " bytes instead: "
-          <> Text.decodeUtf8 (Base16.encode eddsaX)
+          <> Text.pack (show eddsaX)
   where
-    actualSize = BS.length eddsaX
+    actualSize = BS.length $ unEdDSAKeyBytes eddsaX
     expectedSize = coordinateSizeEdDSA eddsaCurve
 checkPublicKey key@PublicKeyECDSA {..}
   | ECC.isPointValid curve point = Right $ CheckedPublicKey key
@@ -161,8 +177,13 @@
   = -- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-7.1)
     -- Ed25519 for use w/ EdDSA only
     CoseCurveEd25519
-  deriving (Eq, Show, Enum, Bounded, Generic, ToJSON)
+  deriving (Eq, Show, Enum, Bounded, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CoseCurveEdDSA
+
 -- | Returns the size of a coordinate point for a specific EdDSA curve in bytes.
 coordinateSizeEdDSA :: CoseCurveEdDSA -> Int
 coordinateSizeEdDSA CoseCurveEd25519 = Ed25519.publicKeySize
@@ -178,7 +199,12 @@
   | -- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-7.1)
     -- NIST P-521 also known as secp521r1
     CoseCurveP521
-  deriving (Eq, Show, Enum, Bounded, Generic, ToJSON)
+  deriving (Eq, Show, Enum, Bounded, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CoseCurveECDSA
 
 -- | Converts a 'Cose.CoseCurveECDSA' to an 'ECC.CurveName'. The inverse
 -- function is 'fromCryptCurveECDSA'
diff --git a/src/Crypto/WebAuthn/Cose/PublicKeyWithSignAlg.hs b/src/Crypto/WebAuthn/Cose/PublicKeyWithSignAlg.hs
--- a/src/Crypto/WebAuthn/Cose/PublicKeyWithSignAlg.hs
+++ b/src/Crypto/WebAuthn/Cose/PublicKeyWithSignAlg.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE TypeSynonymInstances #-}
 
 -- | Stability: experimental
@@ -11,6 +12,8 @@
     PublicKeyWithSignAlg (PublicKeyWithSignAlg, Crypto.WebAuthn.Cose.PublicKeyWithSignAlg.publicKey, signAlg),
     CosePublicKey,
     makePublicKeyWithSignAlg,
+    Message (..),
+    Signature (..),
   )
 where
 
@@ -22,7 +25,8 @@
 import qualified Crypto.WebAuthn.Cose.Internal.Registry as R
 import qualified Crypto.WebAuthn.Cose.PublicKey as P
 import qualified Crypto.WebAuthn.Cose.SignAlg as A
-import Crypto.WebAuthn.Internal.ToJSONOrphans ()
+import Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
+import Data.Aeson (ToJSON)
 import qualified Data.Aeson as Aeson
 import qualified Data.ByteString as BS
 import Data.Functor (($>))
@@ -43,8 +47,13 @@
     -- acdCredentialPublicKeyBytes. This would then require parametrizing
     -- 'PublicKeyWithSignAlg' with 'raw :: Bool'
   }
-  deriving (Eq, Show, Generic, Aeson.ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance Aeson.ToJSON PublicKeyWithSignAlg
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#credentialpublickey)
 -- A structured and checked representation of a
 -- [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-7), limited
@@ -53,8 +62,21 @@
 -- field.
 type CosePublicKey = PublicKeyWithSignAlg
 
+-- | A wrapper for the bytes of a message that should be verified.
+-- This is used for both assertion and assertion.
+newtype Message = Message {unMessage :: BS.ByteString}
+  deriving newtype (Eq, Show)
+  deriving (ToJSON) via PrettyHexByteString
+
+-- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-signature-attestation-types)
+-- A wrapper for the bytes of a signature that can be used to verify a 'Message'.
+-- The encoding is specific to webauthn and depends on the 'A.CoseSignAlg' used.
+newtype Signature = Signature {unSignature :: BS.ByteString}
+  deriving newtype (Eq, Show)
+  deriving (ToJSON) via PrettyHexByteString
+
 -- | Deconstructs a 'makePublicKeyWithSignAlg' into its t'P.PublicKey' and
--- 'A.CoseSignAlg'. Since 'makePublicKeyWithSignAlg' can only be constructed
+-- 'A.CoseSignAlg'. Since t'PublicKeyWithSignAlg' can only be constructed
 -- using 'makePublicKeyWithSignAlg', we can be sure that the signature scheme
 -- of t'P.PublicKey' and 'A.CoseSignAlg' matches.
 pattern PublicKeyWithSignAlg :: P.PublicKey -> A.CoseSignAlg -> PublicKeyWithSignAlg
@@ -90,7 +112,7 @@
         <> encode R.CoseKeyTypeParameterOKPCrv
         <> encode (fromCurveEdDSA eddsaCurve)
         <> encode R.CoseKeyTypeParameterOKPX
-        <> encodeBytes eddsaX
+        <> encodeBytes (P.unEdDSAKeyBytes eddsaX)
     P.PublicKey P.PublicKeyECDSA {..} ->
       common R.CoseKeyTypeEC2
         <> encode R.CoseKeyTypeParameterEC2Crv
@@ -195,7 +217,7 @@
             decodeExpected R.CoseKeyTypeParameterOKPCrv
             eddsaCurve <- toCurveEdDSA <$> decode
             decodeExpected R.CoseKeyTypeParameterOKPX
-            eddsaX <- decodeBytesCanonical
+            eddsaX <- P.EdDSAKeyBytes <$> decodeBytesCanonical
             pure P.PublicKeyEdDSA {..}
 
           decodeECDSAKey :: Decoder s P.UncheckedPublicKey
diff --git a/src/Crypto/WebAuthn/Cose/SignAlg.hs b/src/Crypto/WebAuthn/Cose/SignAlg.hs
--- a/src/Crypto/WebAuthn/Cose/SignAlg.hs
+++ b/src/Crypto/WebAuthn/Cose/SignAlg.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE StandaloneDeriving #-}
 
 -- | Stability: experimental
 -- This module contains definitions for [COSE registry](https://www.iana.org/assignments/cose/cose.xhtml)
@@ -95,8 +96,13 @@
     --
     -- Security considerations are [here](https://www.rfc-editor.org/rfc/rfc8812.html#section-5)
     CoseSignAlgRSA CoseHashAlgRSA
-  deriving (Eq, Show, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CoseSignAlg
+
 -- | Hash algorithms that can be used with the ECDSA signature algorithm
 data CoseHashAlgECDSA
   = -- | SHA-256
@@ -105,8 +111,13 @@
     CoseHashAlgECDSASHA384
   | -- | SHA-512
     CoseHashAlgECDSASHA512
-  deriving (Eq, Show, Ord, Enum, Bounded, Generic, ToJSON)
+  deriving (Eq, Show, Ord, Enum, Bounded, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CoseHashAlgECDSA
+
 -- | Hash algorithms that can be used with the RSA signature algorithm
 data CoseHashAlgRSA
   = -- | SHA-1 (deprecated)
@@ -117,7 +128,12 @@
     CoseHashAlgRSASHA384
   | -- | SHA-512
     CoseHashAlgRSASHA512
-  deriving (Eq, Show, Ord, Enum, Bounded, Generic, ToJSON)
+  deriving (Eq, Show, Ord, Enum, Bounded, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CoseHashAlgRSA
 
 -- | [(spec)](https://datatracker.ietf.org/doc/html/draft-ietf-cose-rfc8152bis-algs-12#section-2.2)
 -- [Cose Algorithm registry](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
diff --git a/src/Crypto/WebAuthn/Internal/ToJSONOrphans.hs b/src/Crypto/WebAuthn/Internal/ToJSONOrphans.hs
--- a/src/Crypto/WebAuthn/Internal/ToJSONOrphans.hs
+++ b/src/Crypto/WebAuthn/Internal/ToJSONOrphans.hs
@@ -3,7 +3,7 @@
 
 -- | Stability: internal
 -- This module contain some useful orphan 'ToJSON' instances for pretty-printing values from third-party libraries
-module Crypto.WebAuthn.Internal.ToJSONOrphans () where
+module Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (..)) where
 
 import Crypto.Hash (Digest)
 import qualified Crypto.PubKey.ECC.Types as ECC
@@ -23,9 +23,17 @@
 import qualified Data.X509 as X509
 import qualified Data.X509.Validation as X509
 
-instance ToJSON BS.ByteString where
-  toJSON = String . Text.decodeUtf8 . Base16.encode
+-- | This type holds a bytestring and has no restrictions to its contents. Its main purpose is to simplify debugging:
+-- its 'Aeson.ToJSON' and 'Show' instances convert it to base16 (hexadecimal).
+newtype PrettyHexByteString = PrettyHexByteString BS.ByteString
+  deriving newtype (Eq)
 
+instance ToJSON PrettyHexByteString where
+  toJSON (PrettyHexByteString bytes) = String . Text.decodeUtf8 . Base16.encode $ bytes
+
+instance Show PrettyHexByteString where
+  show (PrettyHexByteString bytes) = Text.unpack . Text.decodeUtf8 . Base16.encode $ bytes
+
 instance ToJSON (Digest h) where
   toJSON = String . Text.decodeUtf8 . Base16.encode . convert
 
@@ -55,7 +63,7 @@
   toJSON X509.ExtensionRaw {..} =
     object
       [ "extRawOID" .= oidToJSON extRawOID,
-        "extRawContent" .= extRawContent
+        "extRawContent" .= PrettyHexByteString extRawContent
       ]
 
 instance ToJSON ECC.CurveName where
diff --git a/src/Crypto/WebAuthn/Metadata/Service/Types.hs b/src/Crypto/WebAuthn/Metadata/Service/Types.hs
--- a/src/Crypto/WebAuthn/Metadata/Service/Types.hs
+++ b/src/Crypto/WebAuthn/Metadata/Service/Types.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE StandaloneDeriving #-}
 
 -- | Stability: experimental
 -- This module contains additional Haskell-specific type definitions for the
@@ -87,8 +88,13 @@
     -- rogueListURL, rogueListHash. TODO, but not currently used in the
     -- BLOB and difficult to implement since it involves JWT
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON (MetadataEntry p)
+
 -- | Same as 'MetadataEntry', but with its type parameter erased
 data SomeMetadataEntry = forall p. SingI p => SomeMetadataEntry (MetadataEntry p)
 
@@ -114,4 +120,9 @@
     -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-service-v3.0-ps-20210518.html#dom-statusreport-certificationrequirementsversion)
     srCertificationRequirementsVersion :: Maybe Text
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON StatusReport
diff --git a/src/Crypto/WebAuthn/Metadata/Statement/Decode.hs b/src/Crypto/WebAuthn/Metadata/Statement/Decode.hs
--- a/src/Crypto/WebAuthn/Metadata/Statement/Decode.hs
+++ b/src/Crypto/WebAuthn/Metadata/Statement/Decode.hs
@@ -23,7 +23,6 @@
 import qualified Crypto.WebAuthn.Model as M
 import Crypto.WebAuthn.Model.Identifier (AAGUID (AAGUID), AuthenticatorIdentifier (AuthenticatorIdentifierFido2, AuthenticatorIdentifierFidoU2F), SubjectKeyIdentifier (SubjectKeyIdentifier))
 import Data.Bifunctor (first)
-import qualified Data.ByteString as BS
 import qualified Data.ByteString.Base16 as Base16
 import qualified Data.ByteString.Base64 as Base64
 import Data.List.NonEmpty (NonEmpty)
@@ -117,10 +116,10 @@
         transform _ = Nothing
 
     -- Decodes the PNG bytes of an [icon](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-icon) field of a metadata statement
-    decodeIcon :: IDL.DOMString -> Either Text BS.ByteString
+    decodeIcon :: IDL.DOMString -> Either Text StatementTypes.PNGBytes
     decodeIcon dataUrl = case Text.stripPrefix "data:image/png;base64," dataUrl of
       Nothing -> Left $ "Icon decoding failed because there is no \"data:image/png;base64,\" prefix: " <> dataUrl
       Just suffix ->
         -- TODO: Use non-lenient decoding, it's only needed because of a spec violation,
         -- see <https://github.com/tweag/haskell-fido2/issues/68>
-        Right $ Base64.decodeLenient (encodeUtf8 suffix)
+        Right $ StatementTypes.PNGBytes $ Base64.decodeLenient (encodeUtf8 suffix)
diff --git a/src/Crypto/WebAuthn/Metadata/Statement/Types.hs b/src/Crypto/WebAuthn/Metadata/Statement/Types.hs
--- a/src/Crypto/WebAuthn/Metadata/Statement/Types.hs
+++ b/src/Crypto/WebAuthn/Metadata/Statement/Types.hs
@@ -1,13 +1,17 @@
+{-# LANGUAGE StandaloneDeriving #-}
+
 -- | Stability: experimental
 -- This module contains additional Haskell-specific type definitions for the
 -- [FIDO Metadata Statement](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html)
 -- specification
 module Crypto.WebAuthn.Metadata.Statement.Types
   ( MetadataStatement (..),
+    PNGBytes (..),
     WebauthnAttestationType (..),
   )
 where
 
+import Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
 import qualified Crypto.WebAuthn.Metadata.FidoRegistry as Registry
 import qualified Crypto.WebAuthn.Metadata.Statement.WebIDL as StatementIDL
 import qualified Crypto.WebAuthn.Metadata.UAF as UAF
@@ -68,16 +72,31 @@
     -- msEcdaaTrustAnchors, not needed for the subset we implement, FIDO 2 and FIDO U2F
 
     -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-icon)
-    msIcon :: Maybe BS.ByteString,
+    msIcon :: Maybe PNGBytes,
     -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-supportedextensions)
     msSupportedExtensions :: Maybe (NonEmpty StatementIDL.ExtensionDescriptor),
     -- | [(spec)](https://fidoalliance.org/specs/mds/fido-metadata-statement-v3.0-ps-20210518.html#dom-metadatastatement-authenticatorgetinfo)
     msAuthenticatorGetInfo :: Maybe StatementIDL.AuthenticatorGetInfo
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON MetadataStatement
+
+-- | A wrapper for the bytes of a PNG images.
+newtype PNGBytes = PNGBytes {unPNGBytes :: BS.ByteString}
+  deriving newtype (Eq)
+  deriving (Show, ToJSON) via PrettyHexByteString
+
 -- | Values of 'Registry.AuthenticatorAttestationType' but limited to the ones possible with Webauthn, see https://www.w3.org/TR/webauthn-2/#sctn-attestation-types
 data WebauthnAttestationType
   = WebauthnAttestationBasic
   | WebauthnAttestationAttCA
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON WebauthnAttestationType
diff --git a/src/Crypto/WebAuthn/Model/Identifier.hs b/src/Crypto/WebAuthn/Model/Identifier.hs
--- a/src/Crypto/WebAuthn/Model/Identifier.hs
+++ b/src/Crypto/WebAuthn/Model/Identifier.hs
@@ -25,8 +25,13 @@
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#aaguid)
 newtype AAGUID = AAGUID {unAAGUID :: UUID}
   deriving (Eq, Show)
-  deriving newtype (Hashable, ToJSON)
+  deriving newtype (Hashable)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON AAGUID
+
 -- | 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)
@@ -51,6 +56,9 @@
 
 deriving instance Eq (AuthenticatorIdentifier p)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AuthenticatorIdentifier p) where
   toJSON (AuthenticatorIdentifierFido2 aaguid) =
     object
@@ -70,8 +78,10 @@
 newtype SubjectKeyIdentifier = SubjectKeyIdentifier {unSubjectKeyIdentifier :: Digest SHA1}
   deriving (Eq, Show)
 
-instance ToJSON SubjectKeyIdentifier where
-  toJSON = toJSON @BS.ByteString . convert . unSubjectKeyIdentifier
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON SubjectKeyIdentifier
 
 instance Hashable SubjectKeyIdentifier where
   hashWithSalt = hashUsing @BS.ByteString (convert . unSubjectKeyIdentifier)
diff --git a/src/Crypto/WebAuthn/Model/Kinds.hs b/src/Crypto/WebAuthn/Model/Kinds.hs
--- a/src/Crypto/WebAuthn/Model/Kinds.hs
+++ b/src/Crypto/WebAuthn/Model/Kinds.hs
@@ -79,6 +79,9 @@
 
 deriving instance Eq (SCeremonyKind c)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (SCeremonyKind c) where
   toJSON SRegistration = "Registration"
   toJSON SAuthentication = "Authentication"
@@ -109,6 +112,9 @@
 
 deriving instance Eq (SProtocolKind p)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (SProtocolKind p) where
   toJSON SFidoU2F = "FidoU2F"
   toJSON SFido2 = "Fido2"
diff --git a/src/Crypto/WebAuthn/Model/Types.hs b/src/Crypto/WebAuthn/Model/Types.hs
--- a/src/Crypto/WebAuthn/Model/Types.hs
+++ b/src/Crypto/WebAuthn/Model/Types.hs
@@ -29,9 +29,9 @@
 --   the initials of the constructor name.
 -- * Every type should have a 'ToJSON' instance for pretty-printing purposes.
 --   This JSON encoding doesn't correspond to any encoding used for
---   sending/receiving these structures, it's only used for pretty-printing,
+--   sending\/receiving these structures, it's only used for pretty-printing,
 --   which is why it doesn't need to be standardized. For encoding these
---   structures from/to JSON for sending/receiving, see the
+--   structures from\/to JSON for sending/receiving, see the
 --   'Crypto.WebAuthn.Model.WebIDL' module
 -- #defaultFields#
 -- * Fields of the WebAuthn standard that are optional (for writing) but have
@@ -128,7 +128,7 @@
 import Crypto.Random (MonadRandom, getRandomBytes)
 import qualified Crypto.WebAuthn.Cose.PublicKeyWithSignAlg as Cose
 import qualified Crypto.WebAuthn.Cose.SignAlg as Cose
-import Crypto.WebAuthn.Internal.ToJSONOrphans ()
+import Crypto.WebAuthn.Internal.ToJSONOrphans (PrettyHexByteString (PrettyHexByteString))
 import Crypto.WebAuthn.Model.Identifier (AAGUID)
 import Crypto.WebAuthn.Model.Kinds
   ( AttestationKind (Unverifiable, Verifiable),
@@ -162,9 +162,12 @@
 
 deriving instance Show (RawField raw)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (RawField raw) where
   toJSON NoRaw = "<none>"
-  toJSON (WithRaw bytes) = toJSON bytes
+  toJSON (WithRaw bytes) = toJSON $ PrettyHexByteString bytes
 
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#enumdef-publickeycredentialtype)
 -- This enumeration defines the valid credential types. It is an extension point;
@@ -177,6 +180,9 @@
 data CredentialType = CredentialTypePublicKey
   deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON CredentialType where
   toJSON CredentialTypePublicKey = "CredentialTypePublicKey"
 
@@ -220,8 +226,13 @@
     -- stored. Draft version 3 of the standard [fixes
     -- this](https://github.com/w3c/webauthn/pull/1654).
     AuthenticatorTransportUnknown Text
-  deriving (Eq, Show, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON AuthenticatorTransport
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#enumdef-authenticatorattachment)
 -- This enumeration’s values describe [authenticators](https://www.w3.org/TR/webauthn-2/#authenticator)'
 -- [attachment modalities](https://www.w3.org/TR/webauthn-2/#authenticator-attachment-modality).
@@ -239,8 +250,13 @@
   | -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dom-authenticatorattachment-cross-platform)
     -- This value indicates [cross-platform attachment](https://www.w3.org/TR/webauthn-2/#cross-platform-attachment).
     AuthenticatorAttachmentCrossPlatform
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON AuthenticatorAttachment
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#enumdef-residentkeyrequirement)
 -- This enumeration’s values describe the [Relying Party](https://www.w3.org/TR/webauthn-2/#relying-party)'s
 -- requirements for [client-side discoverable credentials](https://www.w3.org/TR/webauthn-2/#client-side-discoverable-credential)
@@ -269,8 +285,13 @@
     -- and is prepared to receive an error if a
     -- [client-side discoverable credential](https://www.w3.org/TR/webauthn-2/#client-side-discoverable-credential) cannot be created.
     ResidentKeyRequirementRequired
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON ResidentKeyRequirement
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#enum-userVerificationRequirement)
 -- A [WebAuthn Relying Party](https://www.w3.org/TR/webauthn-2/#webauthn-relying-party) may
 -- require [user verification](https://www.w3.org/TR/webauthn-2/#user-verification) for some
@@ -296,8 +317,13 @@
     -- does not want [user verification](https://www.w3.org/TR/webauthn-2/#user-verification) employed
     -- during the operation (e.g., in the interest of minimizing disruption to the user interaction flow).
     UserVerificationRequirementDiscouraged
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON UserVerificationRequirement
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#enum-attestation-convey)
 -- [WebAuthn Relying Parties](https://www.w3.org/TR/webauthn-2/#webauthn-relying-party) may use
 -- [AttestationConveyancePreference](https://www.w3.org/TR/webauthn-2/#enumdef-attestationconveyancepreference)
@@ -352,8 +378,13 @@
     -- and [attestation statement](https://www.w3.org/TR/webauthn-2/#attestation-statement), unaltered,
     -- to the [Relying Party](https://www.w3.org/TR/webauthn-2/#relying-party).
     AttestationConveyancePreferenceEnterprise
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON AttestationConveyancePreference
+
 -- | An X.509 certificate chain that can be used to verify an attestation
 -- statement
 data AttestationChain (p :: ProtocolKind) where
@@ -367,6 +398,9 @@
 
 deriving instance Show (AttestationChain p)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AttestationChain p) where
   toJSON (Fido2Chain chain) = toJSON chain
   toJSON (FidoU2FCert cert) = toJSON [cert]
@@ -423,8 +457,13 @@
     -- presented to [Relying Parties](https://www.w3.org/TR/webauthn-2/#relying-party)
     -- do not provide uniquely identifiable information, e.g., that might be used for tracking purposes.
     VerifiableAttestationTypeAnonCA
-  deriving (Eq, Show, Bounded, Enum, Ord, Generic, ToJSON)
+  deriving (Eq, Show, Bounded, Enum, Ord, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON VerifiableAttestationType
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-attestation-types)
 -- WebAuthn supports several [attestation types](https://www.w3.org/TR/webauthn-2/#attestation-type),
 -- defining the semantics of [attestation statements](https://www.w3.org/TR/webauthn-2/#attestation-statement)
@@ -458,6 +497,9 @@
 
 deriving instance Show (AttestationType k)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AttestationType k) where
   toJSON AttestationTypeNone =
     object
@@ -496,8 +538,13 @@
 -- uses DOMString, while the latter uses USVString. Is this a bug in the spec or is there an actual difference?
 newtype RpId = RpId {unRpId :: Text}
   deriving (Eq, Show, Ord)
-  deriving newtype (IsString, ToJSON)
+  deriving newtype (IsString)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON RpId
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialentity-name)
 -- A [human-palatable](https://www.w3.org/TR/webauthn-2/#human-palatability)
 -- identifier for the [Relying Party](https://www.w3.org/TR/webauthn-2/#relying-party),
@@ -512,8 +559,13 @@
 -- about how this metadata is encoded.
 newtype RelyingPartyName = RelyingPartyName {unRelyingPartyName :: Text}
   deriving (Eq, Show)
-  deriving newtype (IsString, ToJSON)
+  deriving newtype (IsString)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON RelyingPartyName
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#user-handle)
 -- The user handle is specified by a [Relying Party](https://www.w3.org/TR/webauthn-2/#relying-party),
 -- as the value of 'id', and used to [map](https://www.w3.org/TR/webauthn-2/#authenticator-credentials-map)
@@ -525,8 +577,12 @@
 -- with a maximum size of 64 bytes, and is not meant to be displayed to the user.
 newtype UserHandle = UserHandle {unUserHandle :: BS.ByteString}
   deriving (Eq, Show, Ord)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving via PrettyHexByteString instance ToJSON UserHandle
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#user-handle)
 -- A user handle is an opaque [byte sequence](https://infra.spec.whatwg.org/#byte-sequence)
 -- with a maximum size of 64 bytes, and is not meant to be displayed to the user.
@@ -547,8 +603,13 @@
 -- about how this metadata is encoded.
 newtype UserAccountDisplayName = UserAccountDisplayName {unUserAccountDisplayName :: Text}
   deriving (Eq, Show)
-  deriving newtype (IsString, ToJSON)
+  deriving newtype (IsString)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON UserAccountDisplayName
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dom-publickeycredentialentity-name)
 -- A [human-palatable](https://www.w3.org/TR/webauthn-2/#human-palatability) identifier for a user account.
 -- It is intended only for display, i.e., aiding the user in determining the difference between user accounts with
@@ -566,16 +627,25 @@
 --   about how this metadata is encoded.
 newtype UserAccountName = UserAccountName {unUserAccountName :: Text}
   deriving (Eq, Show)
-  deriving newtype (IsString, ToJSON)
+  deriving newtype (IsString)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON UserAccountName
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#credential-id)
 -- A probabilistically-unique [byte sequence](https://infra.spec.whatwg.org/#byte-sequence)
 -- identifying a [public key credential](https://www.w3.org/TR/webauthn-2/#public-key-credential-source)
 -- source and its [authentication assertions](https://www.w3.org/TR/webauthn-2/#authentication-assertion).
 newtype CredentialId = CredentialId {unCredentialId :: BS.ByteString}
   deriving (Eq, Show, Ord)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving via PrettyHexByteString instance ToJSON CredentialId
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#credential-id)
 -- Generates a random 'CredentialId' using 16 random bytes.
 -- This is only useful for authenticators, not for relying parties.
@@ -589,8 +659,12 @@
 -- security consideration.
 newtype Challenge = Challenge {unChallenge :: BS.ByteString}
   deriving (Eq, Show, Ord)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving via PrettyHexByteString instance ToJSON Challenge
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-cryptographic-challenges)
 -- In order to prevent replay attacks, the challenges MUST contain enough entropy
 -- to make guessing them infeasible. Challenges SHOULD therefore be at least 16 bytes long.
@@ -602,8 +676,12 @@
 -- This is treated as a hint, and MAY be overridden by the [client](https://www.w3.org/TR/webauthn-2/#client).
 newtype Timeout = Timeout {unTimeout :: Word32}
   deriving (Eq, Show)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON Timeout
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#assertion-signature)
 -- An assertion signature is produced when the
 -- [authenticatorGetAssertion](https://www.w3.org/TR/webauthn-2/#authenticatorgetassertion)
@@ -624,39 +702,65 @@
 -- format is illustrated in [Figure 4, below](https://www.w3.org/TR/webauthn-2/#fig-signature).
 newtype AssertionSignature = AssertionSignature {unAssertionSignature :: BS.ByteString}
   deriving (Eq, Show)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving via PrettyHexByteString instance ToJSON AssertionSignature
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#rpidhash)
 -- SHA-256 hash of the [RP ID](https://www.w3.org/TR/webauthn-2/#rp-id) the
 -- [credential](https://www.w3.org/TR/webauthn-2/#public-key-credential) is
 -- [scoped](https://www.w3.org/TR/webauthn-2/#scope) to.
 newtype RpIdHash = RpIdHash {unRpIdHash :: Digest SHA256}
   deriving (Eq, Show)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON RpIdHash
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#collectedclientdata-hash-of-the-serialized-client-data)
 -- This is the hash (computed using SHA-256) of the [JSON-compatible serialization of client data](https://www.w3.org/TR/webauthn-2/#collectedclientdata-json-compatible-serialization-of-client-data),
 -- as constructed by the client.
 newtype ClientDataHash = ClientDataHash {unClientDataHash :: Digest SHA256}
   deriving (Eq, Show)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON ClientDataHash
+
 -- | [(spec)](https://html.spec.whatwg.org/multipage/origin.html#concept-origin)
 newtype Origin = Origin {unOrigin :: Text}
   deriving (Eq, Show)
-  deriving newtype (IsString, ToJSON)
+  deriving newtype (IsString)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON Origin
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#signcount)
 -- [Signature counter](https://www.w3.org/TR/webauthn-2/#signature-counter)
 newtype SignatureCounter = SignatureCounter {unSignatureCounter :: Word32}
   deriving (Eq, Show)
-  deriving newtype (Num, Ord, ToJSON)
+  deriving newtype (Num, Ord)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving newtype instance ToJSON SignatureCounter
+
 -- | The encoding of a 'Cose.CosePublicKey'
 newtype PublicKeyBytes = PublicKeyBytes {unPublicKeyBytes :: BS.ByteString}
   deriving (Eq, Show)
-  deriving newtype (ToJSON)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving via PrettyHexByteString instance ToJSON PublicKeyBytes
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#iface-authentication-extensions-client-inputs)
 -- This is a dictionary containing the [client extension input](https://www.w3.org/TR/webauthn-2/#client-extension-input)
 -- values for zero or more [WebAuthn Extensions](https://www.w3.org/TR/webauthn-2/#webauthn-extensions).
@@ -701,6 +805,9 @@
   }
   deriving (Eq, Show)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON AuthenticatorExtensionOutputs where
   toJSON _ = object []
 
@@ -718,8 +825,13 @@
     -- intended only for display. For example, "ACME Corporation", "Wonderful Widgets, Inc." or "ОАО Примертех".
     creName :: RelyingPartyName
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CredentialRpEntity
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dictionary-user-credential-params)
 -- The 'CredentialUserEntity' dictionary is used to supply additional
 -- user account attributes when creating a new credential.
@@ -746,8 +858,13 @@
     -- accounts with similar 'cueDisplayName's. For example, "alexm", "alex.mueller@example.com" or "+14255551234".
     cueName :: UserAccountName
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CredentialUserEntity
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dictionary-credential-params)
 -- This dictionary is used to supply additional parameters when creating a new credential.
 data CredentialParameters = CredentialParameters
@@ -760,8 +877,13 @@
     -- key pair to be generated, e.g., RSA or Elliptic Curve.
     cpAlg :: Cose.CoseSignAlg
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CredentialParameters
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dictdef-publickeycredentialdescriptor)
 -- This dictionary contains the attributes that are specified by a caller when referring to a
 -- [public key credential](https://www.w3.org/TR/webauthn-2/#public-key-credential) as an input parameter to the
@@ -782,8 +904,13 @@
     -- of the [public key credential](https://www.w3.org/TR/webauthn-2/#public-key-credential) the caller is referring to.
     cdTransports :: Maybe [AuthenticatorTransport]
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CredentialDescriptor
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#dictdef-authenticatorselectioncriteria)
 -- [WebAuthn Relying Parties](https://www.w3.org/TR/webauthn-2/#webauthn-relying-party)
 -- may use the 'AuthenticatorSelectionCriteria' dictionary to specify their
@@ -808,8 +935,13 @@
     -- The default value of this field is 'Crypto.WebAuthn.Model.Defaults.ascUserVerificationDefault'.
     ascUserVerification :: UserVerificationRequirement
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON AuthenticatorSelectionCriteria
+
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#flags)
 data AuthenticatorDataFlags = AuthenticatorDataFlags
   { -- | [(spec)](https://www.w3.org/TR/webauthn-2/#concept-user-present)
@@ -821,8 +953,13 @@
     -- the user is said to be "[verified](https://www.w3.org/TR/webauthn-2/#concept-user-verified)".
     adfUserVerified :: Bool
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON AuthenticatorDataFlags
+
 -- | A type encompassing the credential options, both for
 -- [creation](https://www.w3.org/TR/webauthn-2/#dictionary-makecredentialoptions)
 -- and
@@ -954,6 +1091,9 @@
 
 deriving instance Show (CredentialOptions c)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (CredentialOptions c) where
   toJSON CredentialOptionsRegistration {..} =
     object
@@ -1027,6 +1167,9 @@
   }
   deriving (Eq, Show)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance SingI c => ToJSON (CollectedClientData (c :: CeremonyKind) raw) where
   toJSON CollectedClientData {..} =
     object
@@ -1061,6 +1204,9 @@
 
 deriving instance Show (AttestedCredentialData c raw)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AttestedCredentialData c raw) where
   toJSON AttestedCredentialData {..} =
     object
@@ -1122,8 +1268,13 @@
     -- | Raw encoded data for verification purposes
     adRawData :: RawField raw
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON (AuthenticatorData c raw)
+
 -- | The result from verifying an attestation statement.
 -- Either the result is verifiable, in which case @k ~ 'Verifiable'@, the
 -- 'AttestationType' contains a verifiable certificate chain.
@@ -1344,6 +1495,9 @@
 
 deriving instance Show (AttestationObject raw)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AttestationObject raw) where
   toJSON AttestationObject {..} =
     object
@@ -1446,6 +1600,9 @@
 
 deriving instance Show (AuthenticatorResponse c raw)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AuthenticatorResponse c raw) where
   toJSON AuthenticatorResponseRegistration {..} =
     object
@@ -1504,4 +1661,9 @@
     -- by the extension’s [client extension processing](https://www.w3.org/TR/webauthn-2/#client-extension-processing).
     cClientExtensionResults :: AuthenticationExtensionsClientOutputs
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON (Credential c raw)
diff --git a/src/Crypto/WebAuthn/Operation/Authentication.hs b/src/Crypto/WebAuthn/Operation/Authentication.hs
--- a/src/Crypto/WebAuthn/Operation/Authentication.hs
+++ b/src/Crypto/WebAuthn/Operation/Authentication.hs
@@ -348,11 +348,11 @@
   -- 20. Using credentialPublicKey, verify that sig is a valid signature over
   -- the binary concatenation of authData and hash.
   let pubKeyBytes = LBS.fromStrict $ M.unPublicKeyBytes $ cePublicKeyBytes entry
-      message = rawData <> convert (M.unClientDataHash hash)
+      message = Cose.Message $ rawData <> convert (M.unClientDataHash hash)
   case CBOR.deserialiseFromBytes decode pubKeyBytes of
     Left err -> failure $ AuthenticationSignatureDecodingError err
     Right (_, coseKey) ->
-      case Cose.verify coseKey message (M.unAssertionSignature sig) of
+      case Cose.verify coseKey message (Cose.Signature $ M.unAssertionSignature sig) of
         Right () -> pure ()
         Left err -> failure $ AuthenticationSignatureInvalid err
 
diff --git a/src/Crypto/WebAuthn/Operation/CredentialEntry.hs b/src/Crypto/WebAuthn/Operation/CredentialEntry.hs
--- a/src/Crypto/WebAuthn/Operation/CredentialEntry.hs
+++ b/src/Crypto/WebAuthn/Operation/CredentialEntry.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE StandaloneDeriving #-}
+
 -- | Stability: experimental
 -- This module represents all the information the Relying Party must store in
 -- the database for every credential.
@@ -19,4 +21,9 @@
     ceSignCounter :: M.SignatureCounter,
     ceTransports :: [M.AuthenticatorTransport]
   }
-  deriving (Eq, Show, Generic, ToJSON)
+  deriving (Eq, Show, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON CredentialEntry
diff --git a/src/Crypto/WebAuthn/Operation/Registration.hs b/src/Crypto/WebAuthn/Operation/Registration.hs
--- a/src/Crypto/WebAuthn/Operation/Registration.hs
+++ b/src/Crypto/WebAuthn/Operation/Registration.hs
@@ -186,6 +186,9 @@
 
 deriving instance Eq (AuthenticatorModel k)
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON (AuthenticatorModel k) where
   toJSON UnknownAuthenticator =
     object
@@ -234,6 +237,9 @@
 
 deriving instance Show SomeAttestationStatement
 
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
 instance ToJSON SomeAttestationStatement where
   toJSON SomeAttestationStatement {..} =
     object
@@ -250,7 +256,12 @@
     -- | Information about the attestation statement
     rrAttestationStatement :: SomeAttestationStatement
   }
-  deriving (Show, Generic, ToJSON)
+  deriving (Show, Generic)
+
+-- | An arbitrary and potentially unstable JSON encoding, only intended for
+-- logging purposes. To actually encode and decode structures, use the
+-- "Crypto.WebAuthn.Encoding" modules
+deriving instance ToJSON RegistrationResult
 
 -- | [(spec)](https://www.w3.org/TR/webauthn-2/#sctn-registering-a-new-credential)
 -- The resulting 'rrEntry' of this call should be stored in a database by the
diff --git a/tests/PublicKeySpec.hs b/tests/PublicKeySpec.hs
--- a/tests/PublicKeySpec.hs
+++ b/tests/PublicKeySpec.hs
@@ -40,7 +40,7 @@
 prop_signverify seed Key.KeyPair {..} msg = do
   let signAlg = Cose.signAlg cosePubKey
       sig = runSeededMonadRandom seed $ Key.sign signAlg privKey msg
-      valid = Cose.verify cosePubKey msg sig
+      valid = Cose.verify cosePubKey (Cose.Message msg) (Cose.Signature sig)
    in case valid of
         Left _ -> False
         Right () -> True
diff --git a/tests/Spec/Key.hs b/tests/Spec/Key.hs
--- a/tests/Spec/Key.hs
+++ b/tests/Spec/Key.hs
@@ -66,7 +66,7 @@
       unchecked =
         Cose.PublicKeyEdDSA
           { eddsaCurve = Cose.CoseCurveEd25519,
-            eddsaX = convert pubKey'
+            eddsaX = Cose.EdDSAKeyBytes $ convert pubKey'
           }
       pubKey = fromRight (error "unreachable") $ Cose.checkPublicKey unchecked
       cosePubKey = fromRight (error "unreachable") $ Cose.makePublicKeyWithSignAlg pubKey Cose.CoseSignAlgEdDSA
@@ -157,8 +157,8 @@
 
 toX509 :: Cose.UncheckedPublicKey -> X509.PubKey
 toX509 Cose.PublicKeyEdDSA {eddsaCurve = Cose.CoseCurveEd25519, ..} =
-  let key = case Ed25519.publicKey eddsaX of
-        CryptoFailed err -> error $ "Failed to create a cryptonite Ed25519 public key of a bytestring with size " <> show (BS.length eddsaX) <> ": " <> show err
+  let key = case Ed25519.publicKey $ Cose.unEdDSAKeyBytes eddsaX of
+        CryptoFailed err -> error $ "Failed to create a cryptonite Ed25519 public key of a bytestring with size " <> show (BS.length $ Cose.unEdDSAKeyBytes eddsaX) <> ": " <> show err
         CryptoPassed res -> res
    in X509.PubKeyEd25519 key
 toX509 Cose.PublicKeyECDSA {..} =
diff --git a/webauthn.cabal b/webauthn.cabal
--- a/webauthn.cabal
+++ b/webauthn.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.4
 name: webauthn
-version: 0.7.0.0
+version: 0.8.0.0
 license: Apache-2.0
 license-file: LICENSE
 copyright:
@@ -69,15 +69,15 @@
   import: sanity
   hs-source-dirs: src
   build-depends:
-    base                  >= 4.13.0 && < 4.18,
-    aeson                 >= 1.4.7 && < 2.2,
+    base                  >= 4.13.0 && < 4.20,
+    aeson                 >= 1.4.7 && < 2.3,
     asn1-encoding         >= 0.9.6 && < 0.10,
     asn1-parse            >= 0.9.5 && < 0.10,
     asn1-types            >= 0.3.4 && < 0.4,
     base16-bytestring     >= 1.0.0 && < 1.1,
     base64-bytestring     >= 1.2.1 && < 1.3,
     binary                >= 0.8.7 && < 0.9,
-    bytestring            >= 0.10.10 && < 0.12,
+    bytestring            >= 0.10.10 && < 0.13,
     cborg                 >= 0.2.4 && < 0.3,
     containers            >= 0.6.2 && < 0.7,
     cryptonite            >= 0.27 && < 0.31,
@@ -91,9 +91,9 @@
     mtl                   >= 2.2.2 && < 2.4,
     serialise             >= 0.2.3 && < 0.3,
     singletons            >= 2.6 && < 3.2,
-    text                  >= 1.2.4 && < 2.1,
-    these                 >= 1.1 && < 1.2,
-    time                  >= 1.9.3 && < 1.12,
+    text                  >= 1.2.4 && < 2.2,
+    these                 >= 1.1 && < 1.3,
+    time                  >= 1.9.3 && < 1.14,
     unordered-containers  >= 0.2.11 && < 0.3,
     uuid                  >= 1.3.13 && < 1.4,
     validation            >= 1.1 && < 1.3,
