{-# LANGUAGE OverloadedStrings #-} -- | -- Module : Data.ByteString.Base64.Internal -- Copyright : (c) 2019-2026 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy> -- Stability : Experimental -- Portability : portable -- -- Internal module defining the encoding and decoding -- processes and tables. -- module Data.ByteString.Base64.Internal ( validateLastPad ) where import qualified Data.ByteString as BS import Data.ByteString.Internal import Data.Text (Text) import System.IO.Unsafe -- | This function checks that the last char of a bytestring is '=' -- and, if true, fails with a message or completes some io action. -- -- This is necessary to check when decoding permissively (i.e. filling in padding chars). -- Consider the following 4 cases of a string of length l: -- -- l = 0 mod 4: No pad chars are added, since the input is assumed to be good. -- l = 1 mod 4: Never an admissible length in base64 -- l = 2 mod 4: 2 padding chars are added. If padding chars are present in the string, they will fail as to decode as final quanta -- l = 3 mod 4: 1 padding char is added. In this case a string is of the form <body> + <padchar>. If adding the -- pad char "completes"" the string so that it is `l = 0 mod 4`, then this may possibly be forming corrupting data. -- This case is degenerate and should be disallowed. -- -- Hence, permissive decodes should only fill in padding chars when it makes sense to add them. That is, -- if an input is degenerate, it should never succeed when we add padding chars. We need the following invariant to hold: -- -- @ -- B64U.decodeUnpadded <|> B64U.decodePadded ~ B64U.decodePadded -- @ -- validateLastPad :: ByteString -> IO (Either Text ByteString) -> Either Text ByteString validateLastPad :: ByteString -> IO (Either Text ByteString) -> Either Text ByteString validateLastPad ByteString bs IO (Either Text ByteString) io | HasCallStack => ByteString -> Word8 ByteString -> Word8 BS.last ByteString bs Word8 -> Word8 -> Bool forall a. Eq a => a -> a -> Bool == Word8 0x3d = Text -> Either Text ByteString forall a b. a -> Either a b Left Text "Base64-encoded bytestring has invalid padding" | Bool otherwise = IO (Either Text ByteString) -> Either Text ByteString forall a. IO a -> a unsafeDupablePerformIO IO (Either Text ByteString) io {-# INLINE validateLastPad #-}