base64 0.4.2.1 → 0.4.2.2
raw patch · 21 files changed
+1195/−198 lines, 21 filesdep −quickcheck-instancesdep ~deepseqPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependencies removed: quickcheck-instances
Dependency ranges changed: deepseq
API changes (from Hackage documentation)
+ Data.Text.Encoding.Base64.Error: instance Control.DeepSeq.NFData e => Control.DeepSeq.NFData (Data.Text.Encoding.Base64.Error.Base64Error e)
+ Data.Text.Encoding.Base64.Error: instance GHC.Exception.Type.Exception e => GHC.Exception.Type.Exception (Data.Text.Encoding.Base64.Error.Base64Error e)
+ Data.Text.Encoding.Base64.Error: instance GHC.Generics.Generic (Data.Text.Encoding.Base64.Error.Base64Error e)
Files
- CHANGELOG.md +9/−1
- README.md +1/−1
- base64.cabal +15/−15
- src/Data/ByteString/Base64.hs +82/−16
- src/Data/ByteString/Base64/Internal.hs +29/−0
- src/Data/ByteString/Base64/Internal/Head.hs +9/−10
- src/Data/ByteString/Base64/Internal/W16/Loop.hs +13/−10
- src/Data/ByteString/Base64/URL.hs +110/−22
- src/Data/ByteString/Lazy/Base64.hs +72/−7
- src/Data/ByteString/Lazy/Base64/URL.hs +92/−7
- src/Data/ByteString/Short/Base64.hs +69/−7
- src/Data/ByteString/Short/Base64/URL.hs +93/−8
- src/Data/Text/Encoding/Base64.hs +67/−11
- src/Data/Text/Encoding/Base64/Error.hs +30/−6
- src/Data/Text/Encoding/Base64/URL.hs +91/−16
- src/Data/Text/Lazy/Encoding/Base64.hs +67/−10
- src/Data/Text/Lazy/Encoding/Base64/URL.hs +92/−17
- src/Data/Text/Short/Encoding/Base64.hs +68/−12
- src/Data/Text/Short/Encoding/Base64/URL.hs +90/−14
- test/Internal.hs +35/−6
- test/Main.hs +61/−2
CHANGELOG.md view
@@ -1,8 +1,16 @@ # Revision history for base64 +## 0.4.2.2++* Add `NFData`, `Exception`, and `Generic` instances for `Base64Error` + `@since` annotations for new instances. ([#28](https://github.com/emilypi/base64/pull/28))+* Doc improvements and add `-XTrustworty` and `-XSafe` annotations where needed. ([#27](https://github.com/emilypi/base64/pull/27))+* Improve URL canonicity validation and correctness checking (now supports correct checking for unpadded Base64url) ([#26](https://github.com/emilypi/base64/pull/26))+* Fixed perf regressions in decode+* Test coverage is at 98%+ ## 0.4.2.1 -* [Security fix]: reject non-canonical base64 encoded values - ([#25](https://github.com/emilypi/base64/pull/25))+* Security fix: reject non-canonical base64 encoded values - ([#25](https://github.com/emilypi/base64/pull/25)) * Perf improvements
README.md view
@@ -25,4 +25,4 @@ - Checks for both validity and correctness of Base64 and Base64url encodings - Rejects non-canonical encodings that do not roundtrip in other base64 libraries like `ZE==`. -There are no dependencies aside from those bundled with GHC, and the `ghc-byteorder` re-export.+There are no dependencies aside from those bundled with GHC, `text-short`, and the `ghc-byteorder` re-export.
base64.cabal view
@@ -1,20 +1,20 @@-cabal-version: 2.0-name: base64-version: 0.4.2.1-synopsis: Fast RFC 4648-compliant Base64 encoding+cabal-version: 2.0+name: base64+version: 0.4.2.2+synopsis: Fast RFC 4648-compliant Base64 encoding description: RFC 4648-compliant padded and unpadded base64 and base64url encoding and decoding. This library provides performant encoding and decoding primitives, as well as support for textual values. -homepage: https://github.com/emilypi/base64-bug-reports: https://github.com/emilypi/base64/issues-license: BSD3-license-file: LICENSE-author: Emily Pillmore-maintainer: emilypi@cohomolo.gy-copyright: (c) 2019-2020 Emily Pillmore-category: Data-build-type: Simple+homepage: https://github.com/emilypi/base64+bug-reports: https://github.com/emilypi/base64/issues+license: BSD3+license-file: LICENSE+author: Emily Pillmore+maintainer: emilypi@cohomolo.gy+copyright: (c) 2019-2020 Emily Pillmore+category: Data+build-type: Simple extra-doc-files: CHANGELOG.md README.md@@ -61,6 +61,7 @@ build-depends: base >=4.10 && <5 , bytestring ^>=0.10+ , deepseq >=1.4.3.0 && <1.4.5.0 , ghc-byteorder ^>=4.11.0.0 , text ^>=1.2 , text-short ^>=0.1@@ -76,12 +77,11 @@ other-modules: Internal main-is: Main.hs build-depends:- base >=4.10 && <5+ base >=4.10 && <5 , base64 , base64-bytestring , bytestring , QuickCheck- , quickcheck-instances , random-bytestring , tasty , tasty-hunit
src/Data/ByteString/Base64.hs view
@@ -1,23 +1,28 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Base64 -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.ByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64+-- encoding format. This includes lenient decoding variants, as well as+-- internal and external validation for canonicity. -- module Data.ByteString.Base64-( encodeBase64+( -- * Encoding+ encodeBase64 , encodeBase64'+ -- * Decoding , decodeBase64 , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -38,33 +43,54 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: ByteString -> Text encodeBase64 = T.decodeUtf8 . encodeBase64'-{-# INLINE encodeBase64 #-}+{-# inline encodeBase64 #-} -- | Encode a 'ByteString' value as a Base64 'ByteString' value with padding. -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64' "Sun"+-- "U3Vu"+-- encodeBase64' :: ByteString -> ByteString encodeBase64' = encodeBase64_ base64Table-{-# INLINE encodeBase64' #-}+{-# inline encodeBase64' #-} -- | Decode a padded Base64-encoded 'ByteString' value. -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: ByteString -> Either Text ByteString-decodeBase64 bs@(PS _ _ l)+decodeBase64 bs@(PS _ _ !l) | l == 0 = Right bs | r == 1 = Left "Base64-encoded bytestring has invalid size" | r /= 0 = Left "Base64-encoded bytestring requires padding" | otherwise = unsafeDupablePerformIO $ decodeBase64_ dlen decodeB64Table bs where- q = l `quot` 4- r = l `rem` 4- dlen = q * 3-{-# INLINE decodeBase64 #-}+ !q = l `quot` 4+ !r = l `rem` 4+ !dlen = q * 3+{-# inline decodeBase64 #-} -- | Leniently decode an unpadded Base64-encoded 'ByteString' value. This function -- will not generate parse errors. If input data contains padding chars,@@ -72,15 +98,41 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: ByteString -> ByteString decodeBase64Lenient = decodeBase64Lenient_ decodeB64Table-{-# INLINE decodeBase64Lenient #-}+{-# inline decodeBase64Lenient #-} -- | Tell whether a 'ByteString' value is base64 encoded. --+-- This function will also detect non-canonical encodings such as @ZE==@, which are+-- externally valid Base64url-encoded values, but are internally inconsistent "impossible"+-- values.+--+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: ByteString -> Bool isBase64 bs = isValidBase64 bs && isRight (decodeBase64 bs)-{-# INLINE isBase64 #-}+{-# inline isBase64 #-} -- | Tell whether a 'ByteString' value is a valid Base64 format. --@@ -88,6 +140,20 @@ -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ByteString' value, use 'isBase64'. --+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False+-- isValidBase64 :: ByteString -> Bool isValidBase64 = validateBase64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"-{-# INLINE isValidBase64 #-}+{-# inline isValidBase64 #-}
src/Data/ByteString/Base64/Internal.hs view
@@ -18,6 +18,7 @@ -- module Data.ByteString.Base64.Internal ( validateBase64+, validateBase64Url , validateLastPad ) where @@ -52,6 +53,34 @@ if f w then go (plusPtr p 1) end else return False {-# INLINE validateBase64 #-}++validateBase64Url :: ByteString -> ByteString -> Bool+validateBase64Url !alphabet bs@(PS _ _ l)+ | l == 0 = True+ | r == 0 = f bs+ | r == 2 = f (BS.append bs "==")+ | r == 3 = f (BS.append bs "=")+ | otherwise = False++ where+ r = l `rem` 4++ f (PS fp o n) = accursedUnutterablePerformIO $+ withForeignPtr fp $ \p -> go (plusPtr p o) (plusPtr p (n + o))++ go !p !end+ | p == end = return True+ | otherwise = do+ w <- peek p++ let check a+ | a == 0x3d, plusPtr p 1 == end = True+ | a == 0x3d, plusPtr p 2 == end = True+ | a == 0x3d = False+ | otherwise = BS.elem a alphabet++ if check w then go (plusPtr p 1) end else return False+{-# INLINE validateBase64Url #-} -- | This function checks that the last char of a bytestring is '=' -- and, if true, fails with a message or completes some io action.
src/Data/ByteString/Base64/Internal/Head.hs view
@@ -44,13 +44,13 @@ encodeBase64_ :: EncodingTable -> ByteString -> ByteString-encodeBase64_ (EncodingTable aptr efp) (PS sfp soff slen) =+encodeBase64_ (EncodingTable !aptr !efp) (PS !sfp !soff !slen) = unsafeDupablePerformIO $ do dfp <- mallocPlainForeignPtrBytes dlen withForeignPtr dfp $ \dptr -> withForeignPtr sfp $ \sptr -> withForeignPtr efp $ \eptr -> do- let end = plusPtr sptr (soff + slen)+ let !end = plusPtr sptr (soff + slen) innerLoop eptr (castPtr (plusPtr sptr soff))@@ -58,7 +58,7 @@ end (loopTail dfp aptr dptr (castPtr end)) where- dlen = 4 * ((slen + 2) `div` 3)+ !dlen = 4 * ((slen + 2) `div` 3) encodeBase64Nopad_ :: EncodingTable -> ByteString -> ByteString encodeBase64Nopad_ (EncodingTable !aptr !efp) (PS !sfp !soff !slen) =@@ -92,17 +92,16 @@ -> ForeignPtr Word8 -> ByteString -> IO (Either Text ByteString)-decodeBase64_ dlen dtfp (PS sfp soff slen) =+decodeBase64_ !dlen !dtfp (PS !sfp !soff !slen) = withForeignPtr dtfp $ \dtable -> withForeignPtr sfp $ \sptr -> do dfp <- mallocPlainForeignPtrBytes dlen- withForeignPtr dfp $ \dptr ->- decodeLoop- dtable+ withForeignPtr dfp $ \dptr -> do+ let !end = plusPtr sptr (soff + slen)+ decodeLoop dtable (plusPtr sptr soff)- dptr- (plusPtr sptr (soff + slen))- dfp+ dptr end dfp+{-# inline decodeBase64_ #-} decodeBase64Lenient_ :: ForeignPtr Word8 -> ByteString -> ByteString decodeBase64Lenient_ !dtfp (PS !sfp !soff !slen) = unsafeDupablePerformIO $
src/Data/ByteString/Base64/Internal/W16/Loop.hs view
@@ -53,9 +53,9 @@ !j <- w32 <$> peek (plusPtr src 1) !k <- w32 <$> peek (plusPtr src 2) - let !w = (shiftL i 16) .|. (shiftL j 8) .|. k+ let !w = (unsafeShiftL i 16) .|. (unsafeShiftL j 8) .|. k - !x <- peekElemOff etable (fromIntegral (shiftR w 12))+ !x <- peekElemOff etable (fromIntegral (unsafeShiftR w 12)) !y <- peekElemOff etable (fromIntegral (w .&. 0xfff)) poke dst x@@ -76,22 +76,25 @@ -> IO (Either Text ByteString) decodeLoop !dtable !sptr !dptr !end !dfp = go dptr sptr where+ err :: Ptr Word8 -> IO (Either Text ByteString) err p = return . Left . T.pack $ "invalid character at offset: " ++ show (p `minusPtr` sptr) + padErr :: Ptr Word8 -> IO (Either Text ByteString) padErr p = return . Left . T.pack $ "invalid padding at offset: " ++ show (p `minusPtr` sptr) + canonErr :: Ptr Word8 -> IO (Either Text ByteString) canonErr p = return . Left . T.pack $ "non-canonical encoding detected at offset: " ++ show (p `minusPtr` sptr) look :: Ptr Word8 -> IO Word32 look !p = do- i <- peekByteOff @Word8 p 0- v <- peekByteOff @Word8 dtable (fromIntegral i)+ !i <- peekByteOff @Word8 p 0+ !v <- peekByteOff @Word8 dtable (fromIntegral i) return (fromIntegral v) go !dst !src@@ -103,17 +106,17 @@ finalChunk dst src a b c d | otherwise = do- !a <- look src- !b <- look (src `plusPtr` 1)- !c <- look (src `plusPtr` 2)- !d <- look (src `plusPtr` 3)+ a <- look src+ b <- look (src `plusPtr` 1)+ c <- look (src `plusPtr` 2)+ d <- look (src `plusPtr` 3) decodeChunk dst src a b c d -- | Decodes chunks of 4 bytes at a time, recombining into -- 3 bytes. Note that in the inner loop stage, no padding -- characters are admissible. --- decodeChunk !dst !src !a !b !c !d+ decodeChunk !dst !src a b c d | a == 0x63 = padErr src | b == 0x63 = padErr (plusPtr src 1) | c == 0x63 = padErr (plusPtr src 2)@@ -138,7 +141,7 @@ -- 3 bytes. Note that in this stage, we can have padding chars -- but only in the final 2 positions. --- finalChunk !dst !src !a !b !c !d+ finalChunk !dst !src a b c d | a == 0x63 = padErr src | b == 0x63 = padErr (plusPtr src 1) | c == 0x63 && d /= 0x63 = err (plusPtr src 3) -- make sure padding is coherent.
src/Data/ByteString/Base64/URL.hs view
@@ -1,28 +1,32 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Base64.URL -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64-URL encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.ByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient decoding+-- variants, as well as internal and external validation for canonicity. -- module Data.ByteString.Base64.URL-( encodeBase64+( -- * Encoding+ encodeBase64 , encodeBase64'-, decodeBase64 , encodeBase64Unpadded , encodeBase64Unpadded'+ -- * Decoding+, decodeBase64 , decodeBase64Unpadded , decodeBase64Padded , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -44,6 +48,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: ByteString -> Text encodeBase64 = T.decodeUtf8 . encodeBase64' {-# INLINE encodeBase64 #-}@@ -52,6 +61,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64' "<<?>>"+-- "PDw_Pj4="+-- encodeBase64' :: ByteString -> ByteString encodeBase64' = encodeBase64_ base64UrlTable @@ -63,17 +77,31 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: ByteString -> Either Text ByteString-decodeBase64 bs@(PS _ _ l)+decodeBase64 bs@(PS _ _ !l) | l == 0 = Right bs | r == 0 = unsafeDupablePerformIO $ decodeBase64_ dlen decodeB64UrlTable bs | r == 2 = unsafeDupablePerformIO $ decodeBase64_ dlen decodeB64UrlTable (BS.append bs "==") | r == 3 = validateLastPad bs $ decodeBase64_ dlen decodeB64UrlTable (BS.append bs "=") | otherwise = Left "Base64-encoded bytestring has invalid size" where- q = l `quot` 4- r = l `rem` 4- dlen = q * 3+ !q = l `quot` 4+ !r = l `rem` 4+ !dlen = q * 3 {-# INLINE decodeBase64 #-} -- | Encode a 'ByteString' value as Base64url 'Text' without padding. Note that for Base64url,@@ -82,6 +110,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: ByteString -> Text encodeBase64Unpadded = T.decodeUtf8 . encodeBase64Unpadded' {-# INLINE encodeBase64Unpadded #-}@@ -92,6 +125,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded' "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded' :: ByteString -> ByteString encodeBase64Unpadded' = encodeBase64Nopad_ base64UrlTable @@ -104,17 +142,25 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: ByteString -> Either Text ByteString-decodeBase64Unpadded bs@(PS _ _ l)+decodeBase64Unpadded bs@(PS _ _ !l) | l == 0 = Right bs | r == 0 = validateLastPad bs $ decodeBase64_ dlen decodeB64UrlTable bs | r == 2 = validateLastPad bs $ decodeBase64_ dlen decodeB64UrlTable (BS.append bs "==") | r == 3 = validateLastPad bs $ decodeBase64_ dlen decodeB64UrlTable (BS.append bs "=") | otherwise = Left "Base64-encoded bytestring has invalid size" where- q = l `quot` 4- r = l `rem` 4- dlen = q * 3+ !q = l `quot` 4+ !r = l `rem` 4+ !dlen = q * 3 {-# INLINE decodeBase64Unpadded #-} -- | Decode a padded Base64url-encoded 'ByteString' value. Input strings are@@ -126,16 +172,24 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: ByteString -> Either Text ByteString-decodeBase64Padded bs@(PS _ _ l)+decodeBase64Padded bs@(PS _ _ !l) | l == 0 = Right bs | r == 1 = Left "Base64-encoded bytestring has invalid size" | r /= 0 = Left "Base64-encoded bytestring requires padding" | otherwise = unsafeDupablePerformIO $ decodeBase64_ dlen decodeB64UrlTable bs where- q = l `quot` 4- r = l `rem` 4- dlen = q * 3+ !q = l `quot` 4+ !r = l `rem` 4+ !dlen = q * 3 {-# INLINE decodeBase64Padded #-} -- | Leniently decode an unpadded Base64url-encoded 'ByteString'. This function@@ -144,12 +198,35 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: ByteString -> ByteString decodeBase64Lenient = decodeBase64Lenient_ decodeB64UrlTable {-# INLINE decodeBase64Lenient #-} --- | Tell whether a 'ByteString' is Base64url-encoded.+-- | Tell whether a 'ByteString' is encoded in padded /or/ unpadded Base64url format. --+-- This function will also detect non-canonical encodings such as @ZE==@, which are+-- externally valid Base64url-encoded values, but are internally inconsistent "impossible"+-- values.+--+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: ByteString -> Bool isBase64Url bs = isValidBase64Url bs && isRight (decodeBase64 bs) {-# INLINE isBase64Url #-}@@ -160,6 +237,17 @@ -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ByteString' value, use 'isBase64Url'. --+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False+-- isValidBase64Url :: ByteString -> Bool-isValidBase64Url = validateBase64 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"+isValidBase64Url = validateBase64Url "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" {-# INLINE isValidBase64Url #-}
src/Data/ByteString/Lazy/Base64.hs view
@@ -1,23 +1,27 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Lazy.Base64 -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.Lazy.ByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64+-- encoding format. This includes lenient decoding variants, as well as+-- internal and external validation for canonicity. -- module Data.ByteString.Lazy.Base64-( encodeBase64+( -- * Encoding+ encodeBase64 , encodeBase64'+ -- * Decoding , decodeBase64 , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -39,6 +43,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: ByteString -> TL.Text encodeBase64 = TL.decodeUtf8 . encodeBase64' {-# INLINE encodeBase64 #-}@@ -47,6 +56,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64' "Sun"+-- "U3Vu"+-- encodeBase64' :: ByteString -> ByteString encodeBase64' = fromChunks . fmap B64.encodeBase64'@@ -58,6 +72,17 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: ByteString -> Either T.Text ByteString decodeBase64 = fmap (fromChunks . (:[])) . B64.decodeBase64@@ -71,6 +96,17 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: ByteString -> ByteString decodeBase64Lenient = fromChunks . fmap B64.decodeBase64Lenient@@ -81,6 +117,21 @@ -- | Tell whether a 'ByteString' value is base64 encoded. --+-- This function will also detect non-canonical encodings such as @ZE==@, which are+-- externally valid Base64url-encoded values, but are internally inconsistent "impossible"+-- values.+--+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: ByteString -> Bool isBase64 bs = isValidBase64 bs && isRight (decodeBase64 bs) {-# INLINE isBase64 #-}@@ -90,6 +141,20 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ByteString' value, use 'isBase64'.+--+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False -- isValidBase64 :: ByteString -> Bool isValidBase64 = go . toChunks
src/Data/ByteString/Lazy/Base64/URL.hs view
@@ -1,27 +1,32 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Lazy.Base64.URL -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.Lazy.ByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient+-- decoding variants, as well as internal and external validation for canonicity. -- module Data.ByteString.Lazy.Base64.URL-( encodeBase64+( -- * Encoding+ encodeBase64 , encodeBase64' , encodeBase64Unpadded , encodeBase64Unpadded'+ -- * Decoding , decodeBase64-, decodeBase64Padded , decodeBase64Unpadded+, decodeBase64Padded , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -44,6 +49,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: ByteString -> TL.Text encodeBase64 = TL.decodeUtf8 . encodeBase64' {-# INLINE encodeBase64 #-}@@ -52,6 +62,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64' "<<?>>"+-- "PDw_Pj4="+-- encodeBase64' :: ByteString -> ByteString encodeBase64' = fromChunks . fmap B64U.encodeBase64'@@ -66,6 +81,20 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: ByteString -> Either T.Text ByteString decodeBase64 = fmap (fromChunks . (:[])) . B64U.decodeBase64@@ -79,6 +108,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: ByteString -> TL.Text encodeBase64Unpadded = TL.decodeUtf8 . encodeBase64Unpadded' {-# INLINE encodeBase64Unpadded #-}@@ -89,6 +123,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded' "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded' :: ByteString -> ByteString encodeBase64Unpadded' = fromChunks . fmap B64U.encodeBase64Unpadded'@@ -104,6 +143,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: ByteString -> Either T.Text ByteString decodeBase64Unpadded = fmap (fromChunks . (:[])) . B64U.decodeBase64Unpadded@@ -120,6 +167,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: ByteString -> Either T.Text ByteString decodeBase64Padded = fmap (fromChunks . (:[])) . B64U.decodeBase64Padded@@ -133,6 +188,14 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: ByteString -> ByteString decodeBase64Lenient = fromChunks . fmap B64U.decodeBase64Lenient@@ -143,6 +206,17 @@ -- | Tell whether a 'ByteString' is Base64url-encoded. --+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: ByteString -> Bool isBase64Url bs = isValidBase64Url bs && isRight (decodeBase64 bs) {-# INLINE isBase64Url #-}@@ -152,6 +226,17 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ByteString' value, use 'isBase64Url'.+--+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False -- isValidBase64Url :: ByteString -> Bool isValidBase64Url = go . toChunks
src/Data/ByteString/Short/Base64.hs view
@@ -1,21 +1,26 @@+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Short.Base64 -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.Short.ShortByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64+-- encoding format. This includes lenient decoding variants, as well as+-- internal and external validation for canonicity. -- module Data.ByteString.Short.Base64-( encodeBase64+( -- * Encoding+ encodeBase64 , encodeBase64'+ -- * Decoding , decodeBase64 , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -27,10 +32,15 @@ import Data.Text.Short (ShortText) import Data.Text.Short.Unsafe (fromShortByteStringUnsafe) --- | Encode a 'ShortByteString' value as Base64 'Text' with padding.+-- | Encode a 'ShortByteString' value as Base64 'ShortText' with padding. -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: ShortByteString -> ShortText encodeBase64 = fromShortByteStringUnsafe . encodeBase64' {-# INLINE encodeBase64 #-}@@ -39,6 +49,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64' "Sun"+-- "U3Vu"+-- encodeBase64' :: ShortByteString -> ShortByteString encodeBase64' = toShort . B64.encodeBase64' . fromShort {-# INLINE encodeBase64' #-}@@ -47,6 +62,17 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: ShortByteString -> Either Text ShortByteString decodeBase64 = fmap toShort . B64.decodeBase64 . fromShort {-# INLINE decodeBase64 #-}@@ -57,12 +83,34 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: ShortByteString -> ShortByteString decodeBase64Lenient = toShort . B64.decodeBase64Lenient . fromShort {-# INLINE decodeBase64Lenient #-} -- | Tell whether a 'ShortByteString' value is base64 encoded. --+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: ShortByteString -> Bool isBase64 = B64.isBase64 . fromShort {-# INLINE isBase64 #-}@@ -72,6 +120,20 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ShortByteString' value, use 'isBase64'.+--+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False -- isValidBase64 :: ShortByteString -> Bool isValidBase64 = B64.isValidBase64 . fromShort
src/Data/ByteString/Short/Base64/URL.hs view
@@ -1,25 +1,30 @@+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.ByteString.Short.Base64.URL -- Copyright : (c) 2019-2020 Emily Pillmore -- License : BSD-style -- -- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64url encoding including--- unpadded and lenient variants+-- This module contains 'Data.ByteString.Short.ShortByteString'-valued combinators for+-- implementing the RFC 4648 specification of the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient decoding+-- variants, as well as internal and external validation for canonicity. -- module Data.ByteString.Short.Base64.URL-( encodeBase64-, encodeBase64Unpadded+( -- * Encoding+ encodeBase64 , encodeBase64'+, encodeBase64Unpadded , encodeBase64Unpadded'+ -- * Decoding , decodeBase64-, decodeBase64Padded , decodeBase64Unpadded+, decodeBase64Padded , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -35,6 +40,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: ShortByteString -> ShortText encodeBase64 = fromShortByteStringUnsafe . encodeBase64' {-# INLINE encodeBase64 #-}@@ -43,6 +53,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64' "<<?>>"+-- "PDw_Pj4="+-- encodeBase64' :: ShortByteString -> ShortByteString encodeBase64' = toShort . B64U.encodeBase64' . fromShort @@ -54,6 +69,20 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: ShortByteString -> Either Text ShortByteString decodeBase64 = fmap toShort . B64U.decodeBase64 . fromShort @@ -65,6 +94,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: ShortByteString -> ShortText encodeBase64Unpadded = fromShortByteStringUnsafe . encodeBase64Unpadded' {-# INLINE encodeBase64Unpadded #-}@@ -75,6 +109,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded' "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded' :: ShortByteString -> ShortByteString encodeBase64Unpadded' = toShort . B64U.encodeBase64Unpadded' . fromShort @@ -87,6 +126,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: ShortByteString -> Either Text ShortByteString decodeBase64Unpadded = fmap toShort . B64U.decodeBase64Unpadded . fromShort {-# INLINE decodeBase64Unpadded #-}@@ -100,6 +147,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: ShortByteString -> Either Text ShortByteString decodeBase64Padded = fmap toShort . B64U.decodeBase64Padded . fromShort {-# INLINE decodeBase64Padded #-}@@ -110,12 +165,31 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: ShortByteString -> ShortByteString decodeBase64Lenient = toShort . B64U.decodeBase64Lenient . fromShort {-# INLINE decodeBase64Lenient #-} -- | Tell whether a 'ShortByteString' is Base64url-encoded. --+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: ShortByteString -> Bool isBase64Url = B64U.isBase64Url . fromShort {-# INLINE isBase64Url #-}@@ -125,6 +199,17 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ShortByteString' value, use 'isBase64Url'.+--+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False -- isValidBase64Url :: ShortByteString -> Bool isValidBase64Url = B64U.isValidBase64Url . fromShort
src/Data/Text/Encoding/Base64.hs view
@@ -1,22 +1,26 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Safe #-} -- | -- Module : Data.Text.Encoding.Base64--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Text'-valued combinators for+-- implementing the RFC 4648 specification of the Base64+-- encoding format. This includes lenient decoding variants, as well as+-- internal and external validation for canonicity. -- module Data.Text.Encoding.Base64-( encodeBase64+( -- * Encoding+ encodeBase64+ -- * Decoding , decodeBase64 , decodeBase64With , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -34,6 +38,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: Text -> Text encodeBase64 = B64.encodeBase64 . T.encodeUtf8 {-# INLINE encodeBase64 #-}@@ -48,6 +57,17 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: Text -> Either Text Text decodeBase64 = fmap T.decodeLatin1 . B64.decodeBase64 . T.encodeUtf8 {-# INLINE decodeBase64 #-}@@ -58,7 +78,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @ -- 'decodeBase64With' 'T.decodeUtf8''@@ -82,6 +102,17 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: Text -> Text decodeBase64Lenient = T.decodeLatin1 . B64.decodeBase64Lenient@@ -90,6 +121,17 @@ -- | Tell whether a 'Text' value is Base64-encoded. --+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: Text -> Bool isBase64 = B64.isBase64 . T.encodeUtf8 {-# INLINE isBase64 #-}@@ -99,6 +141,20 @@ -- This will not tell you whether or not this is a correct Base64 representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'Text' value, use 'isBase64'.+--+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False -- isValidBase64 :: Text -> Bool isValidBase64 = B64.isValidBase64 . T.encodeUtf8
src/Data/Text/Encoding/Base64/Error.hs view
@@ -1,11 +1,13 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE Safe #-} -- | -- Module : Data.Text.Encoding.Base64.Error--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable -- -- This module contains the error types raised (not as exceptions!) -- in the decoding process.@@ -15,8 +17,13 @@ ) where +import Control.DeepSeq (NFData(..))+import Control.Exception (Exception(..))+ import Data.Text (Text) +import GHC.Generics+ -- | This data type represents the type of decoding errors of -- various kinds as they pertain to decoding 'Text' values. -- Namely, to distinguish between decoding errors from opaque@@ -29,4 +36,21 @@ | ConversionError e -- ^ The error associated with the decoding failure -- as a result of the conversion process- deriving (Eq, Show)+ deriving+ ( Eq, Show+ , Generic+ -- ^ @since 4.2.2+ )++-- |+--+-- @since 4.2.2+--+instance Exception e => Exception (Base64Error e)+++-- |+--+-- @since 4.2.2+--+instance NFData e => NFData (Base64Error e)
src/Data/Text/Encoding/Base64/URL.hs view
@@ -1,26 +1,31 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Safe #-} -- | -- Module : Data.Text.Encoding.Base64.URL--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64-URL encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Text'-valued combinators for+-- implementing the RFC 4648 specification of the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient decoding+-- variants, as well as internal and external validation for canonicity.+-- module Data.Text.Encoding.Base64.URL-( encodeBase64+( -- * Encoding+ encodeBase64+, encodeBase64Unpadded+ -- * Decoding , decodeBase64 , decodeBase64With-, encodeBase64Unpadded , decodeBase64Unpadded , decodeBase64UnpaddedWith , decodeBase64Padded , decodeBase64PaddedWith , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -38,6 +43,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: Text -> Text encodeBase64 = B64U.encodeBase64 . T.encodeUtf8 {-# INLINE encodeBase64 #-}@@ -56,6 +66,20 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: Text -> Either Text Text decodeBase64 = fmap T.decodeLatin1 . B64U.decodeBase64 . T.encodeUtf8 {-# INLINE decodeBase64 #-}@@ -66,7 +90,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @ -- 'decodeBase64With' 'T.decodeUtf8''@@ -90,6 +114,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: Text -> Text encodeBase64Unpadded = B64U.encodeBase64Unpadded . T.encodeUtf8 {-# INLINE encodeBase64Unpadded #-}@@ -104,6 +133,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: Text -> Either Text Text decodeBase64Unpadded = fmap T.decodeLatin1 . B64U.decodeBase64Unpadded@@ -116,10 +153,10 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @--- 'decodeBase64With' 'T.decodeUtf8''+-- 'decodeBase64UnpaddedWith' 'T.decodeUtf8'' -- :: 'ByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'Text' -- @ --@@ -144,6 +181,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: Text -> Either Text Text decodeBase64Padded = fmap T.decodeLatin1 . B64U.decodeBase64Padded@@ -156,10 +201,10 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @--- 'decodeBase64With' 'T.decodeUtf8''+-- 'decodeBase64PaddedWith' 'T.decodeUtf8'' -- :: 'ByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'Text' -- @ --@@ -180,6 +225,14 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: Text -> Text decodeBase64Lenient = T.decodeLatin1 . B64U.decodeBase64Lenient@@ -188,6 +241,17 @@ -- | Tell whether a 'Text' value is Base64url-encoded. --+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: Text -> Bool isBase64Url = B64U.isBase64Url . T.encodeUtf8 {-# INLINE isBase64Url #-}@@ -197,6 +261,17 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'Text' value, use 'isBase64Url'.+--+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False -- isValidBase64Url :: Text -> Bool isValidBase64Url = B64U.isValidBase64Url . T.encodeUtf8
src/Data/Text/Lazy/Encoding/Base64.hs view
@@ -1,21 +1,26 @@+{-# LANGUAGE Safe #-} -- | -- Module : Data.Text.Lazy.Encoding.Base64--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Lazy.Text'-valued combinators+-- implementing the RFC 4648 specification for the Base64+-- encoding format. This includes lenient decoding variants, and+-- external + internal validations for canonicity. -- module Data.Text.Lazy.Encoding.Base64-( encodeBase64+( -- * Encoding+ encodeBase64+ -- * Decoding , decodeBase64 , decodeBase64With , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -34,6 +39,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: TL.Text -> TL.Text encodeBase64 = BL64.encodeBase64 . TL.encodeUtf8 {-# INLINE encodeBase64 #-}@@ -48,6 +58,17 @@ -- caller to make sure inputs are valid. If unsure, defer to `decodeBase64With` -- and pass in a custom decode function. --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: TL.Text -> Either T.Text TL.Text decodeBase64 = fmap TL.decodeLatin1 . BL64.decodeBase64 . TL.encodeUtf8 {-# INLINE decodeBase64 #-}@@ -58,7 +79,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @ -- 'decodeBase64With' 'TL.decodeUtf8''@@ -82,6 +103,17 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: TL.Text -> TL.Text decodeBase64Lenient = TL.decodeLatin1 . BL64.decodeBase64Lenient@@ -90,6 +122,17 @@ -- | Tell whether a 'TL.Text' value is Base64-encoded. --+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: TL.Text -> Bool isBase64 = BL64.isBase64 . TL.encodeUtf8 {-# INLINE isBase64 #-}@@ -99,6 +142,20 @@ -- This will not tell you whether or not this is a correct Base64 representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'TL.Text' value, use 'isBase64'.+--+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False -- isValidBase64 :: TL.Text -> Bool isValidBase64 = BL64.isValidBase64 . TL.encodeUtf8
src/Data/Text/Lazy/Encoding/Base64/URL.hs view
@@ -1,26 +1,31 @@+{-# LANGUAGE Safe #-} -- | -- Module : Data.Text.Lazy.Encoding.Base64.URL--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64-URL encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Lazy.Text'-valued combinators for+-- implementing the RFC 4648 specification of the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient decoding+-- variants, as well as internal and external validation for canonicity. -- module Data.Text.Lazy.Encoding.Base64.URL-( encodeBase64+( -- * Encoding+ encodeBase64+, encodeBase64Unpadded+ -- * Decoding , decodeBase64 , decodeBase64With-, encodeBase64Unpadded , decodeBase64Unpadded , decodeBase64UnpaddedWith , decodeBase64Padded , decodeBase64PaddedWith , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -39,6 +44,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: TL.Text -> TL.Text encodeBase64 = BL64U.encodeBase64 . TL.encodeUtf8 {-# INLINE encodeBase64 #-}@@ -57,6 +67,20 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: TL.Text -> Either T.Text TL.Text decodeBase64 = fmap TL.decodeLatin1 . BL64U.decodeBase64 . TL.encodeUtf8 {-# INLINE decodeBase64 #-}@@ -67,7 +91,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @ -- 'decodeBase64With' 'TL.decodeUtf8''@@ -91,6 +115,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: TL.Text -> TL.Text encodeBase64Unpadded = BL64U.encodeBase64Unpadded . TL.encodeUtf8 {-# INLINE encodeBase64Unpadded #-}@@ -105,6 +134,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: TL.Text -> Either T.Text TL.Text decodeBase64Unpadded = fmap TL.decodeLatin1 . BL64U.decodeBase64Unpadded@@ -117,10 +154,10 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @--- 'decodeBase64With' 'TL.decodeUtf8''+-- 'decodeBase64UnpaddedWith' 'TL.decodeUtf8'' -- :: 'ByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'TL.Text' -- @ --@@ -145,6 +182,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: TL.Text -> Either T.Text TL.Text decodeBase64Padded = fmap TL.decodeLatin1 . BL64U.decodeBase64Padded@@ -157,11 +202,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @--- 'decodeBase64With' 'TL.decodeUtf8''--- :: 'ByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'TL.Text'+-- 'decodeBase64PaddedWith' 'T.decodeUtf8''+-- :: 'ByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'Text' -- @ -- decodeBase64PaddedWith@@ -181,14 +226,33 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: TL.Text -> TL.Text decodeBase64Lenient = TL.decodeLatin1 . BL64U.decodeBase64Lenient . TL.encodeUtf8 {-# INLINE decodeBase64Lenient #-} --- | Tell whether a 'TL.Text' value is Base64url-encoded.+-- | Tell whether a 'TL.Text' value is Base64url-encoded --+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: TL.Text -> Bool isBase64Url = BL64U.isBase64Url . TL.encodeUtf8 {-# INLINE isBase64Url #-}@@ -198,6 +262,17 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'TL.Text' value, use 'isBase64Url'.+--+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False -- isValidBase64Url :: TL.Text -> Bool isValidBase64Url = BL64U.isValidBase64Url . TL.encodeUtf8
src/Data/Text/Short/Encoding/Base64.hs view
@@ -1,22 +1,26 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Trustworthy #-} -- |--- Module : Data.Text.Encoding.Base64--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Module : Data.Text.Short.Encoding.Base64+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64 encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Short.ShortText'-valued combinators+-- implementing the RFC 4648 specification for the Base64+-- encoding format. This includes lenient decoding variants, and+-- external + internal validations for canonicity. -- module Data.Text.Short.Encoding.Base64-( encodeBase64+( -- * Encoding+ encodeBase64+ -- * Decoding , decodeBase64 , decodeBase64With , decodeBase64Lenient+ -- * Validation , isBase64 , isValidBase64 ) where@@ -36,6 +40,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> encodeBase64 "Sun"+-- "U3Vu"+-- encodeBase64 :: ShortText -> ShortText encodeBase64 = fromByteStringUnsafe . B64.encodeBase64'@@ -52,6 +61,17 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "U3Vu"+-- Right "Sun"+--+-- >>> decodeBase64 "U3V"+-- Left "Base64-encoded bytestring requires padding"+--+-- >>> decodebase64 "U3V="+-- Left "non-canonical encoding detected at offset: 2"+-- decodeBase64 :: ShortText -> Either Text ShortText decodeBase64 = fmap fromText . B64T.decodeBase64 . toText {-# INLINE decodeBase64 #-}@@ -62,7 +82,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Example__: -- -- @ -- 'decodeBase64With' 'T.decodeUtf8''@@ -86,12 +106,34 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "U3Vu"+-- "Sun"+--+-- >>> decodeBase64Lenient "U3V"+-- "Su"+--+-- >>> decodebase64Lenient "U3V="+-- "Su"+-- decodeBase64Lenient :: ShortText -> ShortText decodeBase64Lenient = fromText . B64T.decodeBase64Lenient . toText {-# INLINE decodeBase64Lenient #-} -- | Tell whether a 'ShortText' value is Base64-encoded. --+-- === __Examples__:+--+-- >>> isBase64 "U3Vu"+-- True+--+-- >>> isBase64 "U3V"+-- False+--+-- >>> isBase64 "U3V="+-- False+-- isBase64 :: ShortText -> Bool isBase64 = B64.isBase64 . toByteString {-# INLINE isBase64 #-}@@ -101,6 +143,20 @@ -- This will not tell you whether or not this is a correct Base64 representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ShortText' value, use 'isBase64'.+--+-- === __Examples__:+--+-- >>> isValidBase64 "U3Vu"+-- True+--+-- >>> isValidBase64 "U3V"+-- True+--+-- >>> isValidBase64 "U3V="+-- True+--+-- >>> isValidBase64 "%"+-- False -- isValidBase64 :: ShortText -> Bool isValidBase64 = B64.isValidBase64 . toByteString
src/Data/Text/Short/Encoding/Base64/URL.hs view
@@ -1,25 +1,31 @@+{-# LANGUAGE Trustworthy #-} -- | -- Module : Data.Text.Short.Encoding.Base64.URL--- Copyright : (c) 2019-2020 Emily Pillmore--- License : BSD-style+-- Copyright : (c) 2019-2020 Emily Pillmore+-- License : BSD-style ----- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>--- Stability : Experimental--- Portability : portable+-- Maintainer : Emily Pillmore <emilypi@cohomolo.gy>+-- Stability : stable+-- Portability : non-portable ----- This module contains the combinators implementing the--- RFC 4648 specification for the Base64-URL encoding including--- unpadded and lenient variants+-- This module contains 'Data.Text.Short.ShortText'-valued combinators+-- implementing the RFC 4648 specification for the Base64url+-- encoding format. This includes strictly padded/unpadded and lenient+-- decoding variants, and external + internal validations for canonicity.+-- module Data.Text.Short.Encoding.Base64.URL-( encodeBase64+( -- * Encoding+ encodeBase64+, encodeBase64Unpadded+ -- * Decoding , decodeBase64 , decodeBase64With-, encodeBase64Unpadded , decodeBase64Unpadded , decodeBase64UnpaddedWith , decodeBase64Padded , decodeBase64PaddedWith , decodeBase64Lenient+ -- * Validation , isBase64Url , isValidBase64Url ) where@@ -39,6 +45,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-5 RFC-4648 section 5> --+-- === __Examples__:+--+-- >>> encodeBase64 "<<?>>"+-- "PDw_Pj4="+-- encodeBase64 :: ShortText -> ShortText encodeBase64 = fromByteStringUnsafe . B64U.encodeBase64'@@ -59,6 +70,20 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64 "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64 "PDw-Pg="+-- Left "Base64-encoded bytestring has invalid padding"+--+-- >>> decodeBase64 "PDw-Pg"+-- Right "<<>>"+-- decodeBase64 :: ShortText -> Either Text ShortText decodeBase64 = fmap fromText . B64TU.decodeBase64 . toText {-# INLINE decodeBase64 #-}@@ -69,7 +94,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @ -- 'decodeBase64With' 'T.decodeUtf8''@@ -93,6 +118,11 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-3.2 RFC-4648 section 3.2> --+-- === __Examples__:+--+-- >>> encodeBase64Unpadded "<<?>>"+-- "PDw_Pj4"+-- encodeBase64Unpadded :: ShortText -> ShortText encodeBase64Unpadded = fromByteStringUnsafe . B64U.encodeBase64Unpadded'@@ -109,6 +139,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Unpadded "PDw_Pj4"+-- Right "<<?>>"+--+-- >>> decodeBase64Unpadded "PDw_Pj4="+-- Left "Base64-encoded bytestring has invalid padding"+-- decodeBase64Unpadded :: ShortText -> Either Text ShortText decodeBase64Unpadded = fmap fromText . B64TU.decodeBase64Unpadded . toText {-# INLINE decodeBase64Unpadded #-}@@ -119,10 +157,10 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @--- 'decodeBase64With' 'T.decodeUtf8''+-- 'decodeBase64UnpaddedWith' 'T.decodeUtf8'' -- :: 'ShortByteString' -> 'Either' ('Base64Error' 'UnicodeException') 'ShortText' -- @ --@@ -147,6 +185,14 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> --+-- === __Examples__:+--+-- >>> decodeBase64Padded "PDw_Pj4="+-- Right "<<?>>"+--+-- >>> decodeBase64Padded "PDw_Pj4"+-- Left "Base64-encoded bytestring requires padding"+-- decodeBase64Padded :: ShortText -> Either Text ShortText decodeBase64Padded = fmap fromText . B64TU.decodeBase64Padded . toText {-# INLINE decodeBase64Padded #-}@@ -157,7 +203,7 @@ -- -- See: <https://tools.ietf.org/html/rfc4648#section-4 RFC-4648 section 4> ----- Example:+-- === __Examples__: -- -- @ -- 'decodeBase64With' 'T.decodeUtf8''@@ -181,12 +227,31 @@ -- -- __Note:__ This is not RFC 4648-compliant. --+-- === __Examples__:+--+-- >>> decodeBase64Lenient "PDw_Pj4="+-- "<<?>>"+--+-- >>> decodeBase64Lenient "PDw_%%%$}Pj4"+-- "<<?>>"+-- decodeBase64Lenient :: ShortText -> ShortText decodeBase64Lenient = fromText . B64TU.decodeBase64Lenient . toText {-# INLINE decodeBase64Lenient #-} -- | Tell whether a 'ShortText' value is Base64url-encoded. --+-- === __Examples__:+--+-- >>> isBase64Url "PDw_Pj4="+-- True+--+-- >>> isBase64Url "PDw_Pj4"+-- True+--+-- >>> isBase64Url "PDw_Pj"+-- False+-- isBase64Url :: ShortText -> Bool isBase64Url = B64U.isBase64Url . toByteString {-# INLINE isBase64Url #-}@@ -196,6 +261,17 @@ -- This will not tell you whether or not this is a correct Base64url representation, -- only that it conforms to the correct shape. To check whether it is a true -- Base64 encoded 'ShortText' value, use 'isBase64Url'.+--+-- === __Examples__:+--+-- >>> isValidBase64Url "PDw_Pj4="+-- True+--+-- >>> isValidBase64Url "PDw_Pj"+-- True+--+-- >>> isValidBase64Url "%"+-- False -- isValidBase64Url :: ShortText -> Bool isValidBase64Url = B64U.isValidBase64Url . toByteString
test/Internal.hs view
@@ -44,7 +44,6 @@ import "base64" Data.Text.Short.Encoding.Base64.URL as TS64U import Test.QuickCheck hiding (label)-import Test.QuickCheck.Instances () -- ------------------------------------------------------------------ -- -- Test Harnesses@@ -237,14 +236,44 @@ -- ------------------------------------------------------------------ -- -- Quickcheck instances +instance Arbitrary BS.ByteString where+ arbitrary = BS.pack <$> arbitrary+ shrink xs = BS.pack <$> shrink (BS.unpack xs)++instance CoArbitrary BS.ByteString where+ coarbitrary = coarbitrary . BS.unpack++instance Arbitrary LBS.ByteString where+ arbitrary = LBS.pack <$> arbitrary+ shrink xs = LBS.pack <$> shrink (LBS.unpack xs)++instance CoArbitrary LBS.ByteString where+ coarbitrary = coarbitrary . LBS.unpack++instance Arbitrary SBS.ShortByteString where+ arbitrary = SBS.pack <$> arbitrary+ shrink xs = SBS.pack <$> shrink (SBS.unpack xs)++instance CoArbitrary SBS.ShortByteString where+ coarbitrary = coarbitrary . SBS.unpack++instance Arbitrary T.Text where+ arbitrary = T.pack <$> arbitrary+ shrink xs = T.pack <$> shrink (T.unpack xs)++instance Arbitrary TL.Text where+ arbitrary = TL.pack <$> arbitrary+ shrink xs = TL.pack <$> shrink (TL.unpack xs)++instance CoArbitrary T.Text where+ coarbitrary = coarbitrary . T.unpack++instance CoArbitrary TL.Text where+ coarbitrary = coarbitrary . TL.unpack+ instance Arbitrary TS.ShortText where arbitrary = TS.fromText <$> arbitrary shrink xs = fmap TS.fromText $ shrink (TS.toText xs) instance CoArbitrary TS.ShortText where coarbitrary = coarbitrary . TS.toText--instance Function TS.ShortText where- function = functionMap- (T.unpack . TS.toText)- (TS.fromText . T.pack)
test/Main.hs view
@@ -130,6 +130,8 @@ [ paddingTests last_ length_ , rfcVectors , offsetVectors+ , validityTests+ , canonicityTests ] -- | Make unit tests for textual 'decode*With' functions@@ -373,7 +375,10 @@ decode @a "eAoe=Ao=" @=? Left "invalid padding at offset: 4" decode @a "eAoeA=o=" @=? Left "invalid padding at offset: 5" ]- , testGroup "Non-canonical encodings fail and canonical encodings succeed"+ ]++canonicityTests :: forall a b proxy. Harness a b => proxy a -> TestTree+canonicityTests _ = testGroup "Canonicity unit tests" [ testCase "roundtrip for d ~ ZA==" $ do decode @a "ZE==" @=? Left "non-canonical encoding detected at offset: 1" decode @a "ZK==" @=? Left "non-canonical encoding detected at offset: 1"@@ -393,7 +398,6 @@ decode @a "Zm9vYmD=" @=? Left "non-canonical encoding detected at offset: 6" decode @a "Zm9vYmA=" @=? Right "foob`" ]- ] -- | Unit test trees for the `decode*With` family of text-valued functions --@@ -462,5 +466,60 @@ a <- either (assertFailure . show) pure $ decodeUrlNopad @a "PDw_Pz4-" b <- either (assertFailure . show) pure $ decodeUrlUnpaddedWith_ @a utf8 "PDw_Pz4-" a @=? b+ ]+ ]++-- | Validity unit tests for the URL workflow+--+validityTests :: forall a b proxy. Harness a b => proxy a -> TestTree+validityTests _ = testGroup "Validity and correctness unit tests"+ [ testGroup "Validity unit tests"+ [ testCase "Padding tests" $ do+ not (validateUrl @a "P") @? "P"+ validateUrl @a "PA" @? "PA"+ validateUrl @a "PDw" @? "PDw"+ validateUrl @a "PDw_" @? "PDw_"+ validateUrl @a "PA==" @? "PA=="+ validateUrl @a "PDw=" @? "PDw="+ validateUrl @a "PDw_" @? "PDw_"++ , testCase "Canonicity tests" $ do+ validateUrl @a "ZK==" @? "ZK=="+ validateUrl @a "ZE==" @? "ZE=="++ validateUrl @a "ZA==" @? "ZA=="+ validateUrl @a "ZK==" @? "ZK=="+ validateUrl @a "ZK" @? "ZK"++ validateUrl @a "ZmA=" @? "ZmA="+ validateUrl @a "ZmC=" @? "ZmC="+ validateUrl @a "ZmE" @? "ZmE"++ validateUrl @a "Zm9vYmA=" @? "Zm9vYmA="+ validateUrl @a "Zm9vYmC=" @? "Zm9vYmC="+ validateUrl @a "Zm9vYmC" @? "Zm9vYmC"+ ]+ , testGroup "Correctness unit tests"+ [ testCase "Padding tests" $ do+ not (validateUrl @a "P") @? "P"+ correctUrl @a "PA" @? "PA"+ correctUrl @a "PDw" @? "PDw"+ correctUrl @a "PDw_" @? "PDw_"+ correctUrl @a "PA==" @? "PA=="+ correctUrl @a "PDw=" @? "PDw="+ correctUrl @a "PDw_" @? "PDw_"++ , testCase "Canonicity tests" $ do+ not (correctUrl @a "ZK==") @? "ZK=="+ not (correctUrl @a "ZE==") @? "ZE=="+ correctUrl @a "ZA==" @? "ZA=="++ correctUrl @a "ZmA=" @? "ZmA="+ not (correctUrl @a "ZmC=") @? "ZmC="+ not (correctUrl @a "ZmD") @? "ZmD"++ correctUrl @a "Zm9vYmA=" @? "Zm9vYmA="+ not (correctUrl @a "Zm9vYmC=") @? "Zm9vYmC="+ not (correctUrl @a "Zm9vYmC") @? "Zm9vYmC" ] ]