|
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 |
|