Blame Data/ByteArray/Encoding.hs

Packit c1c4f9
-- |
Packit c1c4f9
-- Module      : Data.ByteArray.Encoding
Packit c1c4f9
-- License     : BSD-style
Packit c1c4f9
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit c1c4f9
-- Stability   : experimental
Packit c1c4f9
-- Portability : unknown
Packit c1c4f9
--
Packit c1c4f9
-- ByteArray base converting
Packit c1c4f9
--
Packit c1c4f9
module Data.ByteArray.Encoding
Packit c1c4f9
    ( convertToBase
Packit c1c4f9
    , convertFromBase
Packit c1c4f9
    , Base(..)
Packit c1c4f9
    ) where
Packit c1c4f9
Packit c1c4f9
import           Data.ByteArray.Types
Packit c1c4f9
import qualified Data.ByteArray.Types        as B
Packit c1c4f9
import qualified Data.ByteArray.Methods      as B
Packit c1c4f9
import           Data.Memory.Internal.Compat
Packit c1c4f9
import           Data.Memory.Encoding.Base16
Packit c1c4f9
import           Data.Memory.Encoding.Base32
Packit c1c4f9
import           Data.Memory.Encoding.Base64
Packit c1c4f9
Packit c1c4f9
-- | Different bases that can be used
Packit c1c4f9
--
Packit c1c4f9
-- See <http://tools.ietf.org/html/rfc4648 RFC4648> for details.
Packit c1c4f9
-- In particular, Base64 can be standard or
Packit c1c4f9
-- <http://tools.ietf.org/html/rfc4648#section-5 URL-safe>. URL-safe
Packit c1c4f9
-- encoding is often used in other specifications without
Packit c1c4f9
-- <http://tools.ietf.org/html/rfc4648#section-3.2 padding> characters.
Packit c1c4f9
data Base = Base16            -- ^ similar to hexadecimal
Packit c1c4f9
          | Base32
Packit c1c4f9
          | Base64            -- ^ standard Base64
Packit c1c4f9
          | Base64URLUnpadded -- ^ unpadded URL-safe Base64
Packit c1c4f9
          | Base64OpenBSD     -- ^ Base64 as used in OpenBSD password encoding (such as bcrypt)
Packit c1c4f9
          deriving (Show,Eq)
Packit c1c4f9
Packit c1c4f9
-- | Convert a bytearray to the equivalent representation in a specific Base
Packit c1c4f9
convertToBase :: (ByteArrayAccess bin, ByteArray bout) => Base -> bin -> bout
Packit c1c4f9
convertToBase base b = case base of
Packit c1c4f9
    Base16 -> doConvert (binLength * 2) toHexadecimal
Packit c1c4f9
    Base32 -> let (q,r)  = binLength `divMod` 5
Packit c1c4f9
                  outLen = 8 * (if r == 0 then q else q + 1)
Packit c1c4f9
               in doConvert outLen toBase32
Packit c1c4f9
    Base64 -> doConvert base64Length toBase64
Packit c1c4f9
    -- Base64URL         -> doConvert base64Length (toBase64URL True)
Packit c1c4f9
    Base64URLUnpadded -> doConvert base64UnpaddedLength (toBase64URL False)
Packit c1c4f9
    Base64OpenBSD     -> doConvert base64UnpaddedLength toBase64OpenBSD
Packit c1c4f9
  where
Packit c1c4f9
    binLength = B.length b
Packit c1c4f9
Packit c1c4f9
    base64Length = let (q,r) = binLength `divMod` 3
Packit c1c4f9
                    in 4 * (if r == 0 then q else q+1)
Packit c1c4f9
Packit c1c4f9
    base64UnpaddedLength = let (q,r) = binLength `divMod` 3
Packit c1c4f9
                            in 4 * q + (if r == 0 then 0 else r+1)
Packit c1c4f9
    doConvert l f =
Packit c1c4f9
        B.unsafeCreate l $ \bout ->
Packit c1c4f9
        B.withByteArray b     $ \bin  ->
Packit c1c4f9
            f bout bin binLength
Packit c1c4f9
Packit c1c4f9
-- | Try to Convert a bytearray from the equivalent representation in a specific Base
Packit c1c4f9
convertFromBase :: (ByteArrayAccess bin, ByteArray bout) => Base -> bin -> Either String bout
Packit c1c4f9
convertFromBase Base16 b
Packit c1c4f9
    | odd (B.length b) = Left "base16: input: invalid length"
Packit c1c4f9
    | otherwise        = unsafeDoIO $ do
Packit c1c4f9
        (ret, out) <-
Packit c1c4f9
            B.allocRet (B.length b `div` 2) $ \bout ->
Packit c1c4f9
            B.withByteArray b               $ \bin  ->
Packit c1c4f9
                fromHexadecimal bout bin (B.length b)
Packit c1c4f9
        case ret of
Packit c1c4f9
            Nothing  -> return $ Right out
Packit c1c4f9
            Just ofs -> return $ Left ("base16: input: invalid encoding at offset: " ++ show ofs)
Packit c1c4f9
convertFromBase Base32 b = unsafeDoIO $
Packit c1c4f9
    withByteArray b $ \bin -> do
Packit c1c4f9
        mDstLen <- unBase32Length bin (B.length b)
Packit c1c4f9
        case mDstLen of
Packit c1c4f9
            Nothing     -> return $ Left "base32: input: invalid length"
Packit c1c4f9
            Just dstLen -> do
Packit c1c4f9
                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase32 bout bin (B.length b)
Packit c1c4f9
                case ret of
Packit c1c4f9
                    Nothing  -> return $ Right out
Packit c1c4f9
                    Just ofs -> return $ Left ("base32: input: invalid encoding at offset: " ++ show ofs)
Packit c1c4f9
convertFromBase Base64 b = unsafeDoIO $
Packit c1c4f9
    withByteArray b $ \bin -> do
Packit c1c4f9
        mDstLen <- unBase64Length bin (B.length b)
Packit c1c4f9
        case mDstLen of
Packit c1c4f9
            Nothing     -> return $ Left "base64: input: invalid length"
Packit c1c4f9
            Just dstLen -> do
Packit c1c4f9
                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64 bout bin (B.length b)
Packit c1c4f9
                case ret of
Packit c1c4f9
                    Nothing  -> return $ Right out
Packit c1c4f9
                    Just ofs -> return $ Left ("base64: input: invalid encoding at offset: " ++ show ofs)
Packit c1c4f9
convertFromBase Base64URLUnpadded b = unsafeDoIO $
Packit c1c4f9
    withByteArray b $ \bin ->
Packit c1c4f9
        case unBase64LengthUnpadded (B.length b) of
Packit c1c4f9
            Nothing     -> return $ Left "base64URL unpadded: input: invalid length"
Packit c1c4f9
            Just dstLen -> do
Packit c1c4f9
                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64URLUnpadded bout bin (B.length b)
Packit c1c4f9
                case ret of
Packit c1c4f9
                    Nothing  -> return $ Right out
Packit c1c4f9
                    Just ofs -> return $ Left ("base64URL unpadded: input: invalid encoding at offset: " ++ show ofs)
Packit c1c4f9
convertFromBase Base64OpenBSD b = unsafeDoIO $
Packit c1c4f9
    withByteArray b $ \bin ->
Packit c1c4f9
        case unBase64LengthUnpadded (B.length b) of
Packit c1c4f9
            Nothing     -> return $ Left "base64 unpadded: input: invalid length"
Packit c1c4f9
            Just dstLen -> do
Packit c1c4f9
                (ret, out) <- B.allocRet dstLen $ \bout -> fromBase64OpenBSD bout bin (B.length b)
Packit c1c4f9
                case ret of
Packit c1c4f9
                    Nothing  -> return $ Right out
Packit c1c4f9
                    Just ofs -> return $ Left ("base64 unpadded: input: invalid encoding at offset: " ++ show ofs)
Packit c1c4f9