botan-low-0.2.0.1: src/Botan/Low/Utility.hs
{-|
Module : Botan.Low.Utility
Description : Utility functions
Copyright : (c) 2023-2024, Apotheca Labs
(c) 2024-2025, Haskell Foundation
License : BSD-3-Clause
Maintainer : joris@well-typed.com, leo@apotheca.io
Stability : experimental
Portability : POSIX
This module is based on the [Utility
Functions](https://botan.randombit.net/handbook/api_ref/ffi.html#utility-functions)
section of the C Botan FFI documentation.
-}
module Botan.Low.Utility (
constantTimeCompare
, scrubMem
, HexEncodingFlags(..)
, hexEncode
, hexDecode
, base64Encode
, base64Decode
) where
import Botan.Bindings.ConstPtr (ConstPtr (..))
import Botan.Bindings.Error
import Botan.Bindings.Utility
import Botan.Low.Error.Internal
import Botan.Low.Internal.ByteString
import Botan.Low.Make
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Text (Text)
import qualified Data.Text.Encoding as Text
import Data.Word
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable
-- | Returns 0 if x[0..len] == y[0..len], -1 otherwise.
constantTimeCompare ::
ByteString -- ^ __x__
-> ByteString -- ^ __y__
-> Int -- ^ __len__
-> IO Bool
constantTimeCompare x y len =
asBytesLen x $ \ xPtr _xlen ->
asBytesLen y $ \ yPtr _ylen -> do
result <- botan_constant_time_compare
(ConstPtr xPtr)
(ConstPtr yPtr)
(fromIntegral len)
case result of
0 -> return True
_ -> return False
scrubMem ::
Ptr a -- ^ __mem__
-> Int -- ^ __bytes__
-> IO ()
scrubMem ptr sz = throwBotanIfNegative_ $ botan_scrub_mem (castPtr ptr) (fromIntegral sz)
data HexEncodingFlags =
HexUpperCase
| HexLowerCase
hexEncodingFlags :: HexEncodingFlags -> Word32
hexEncodingFlags HexUpperCase = BOTAN_FFI_HEX_UPPER_CASE
hexEncodingFlags HexLowerCase = BOTAN_FFI_HEX_LOWER_CASE
hexEncode ::
ByteString -- ^ __x__
-> HexEncodingFlags -- ^ __flags__
-> IO Text -- ^ __y__
hexEncode bytes flags =
fmap Text.decodeUtf8 $
asBytesLen bytes $ \ bytesPtr bytesLen ->
allocBytes (fromIntegral $ 2 * bytesLen) $ \ hexPtr ->
throwBotanIfNegative_ $ botan_hex_encode
(ConstPtr bytesPtr)
bytesLen
hexPtr
(hexEncodingFlags flags)
hexDecode ::
Text -- ^ __hex_str__
-> IO ByteString -- ^ __out__
hexDecode txt =
asBytesLen (Text.encodeUtf8 txt) $ \ hexPtr hexLen ->
allocBytesQuerying $ \ bytesPtr szPtr ->
botan_hex_decode
(ConstPtr hexPtr)
hexLen
bytesPtr
szPtr
base64Encode ::
ByteString -- ^ __x__
-> IO Text -- ^ __out__
base64Encode bytes =
fmap Text.decodeUtf8 $
asBytesLen bytes $ \ bytesPtr bytesLen ->
allocBytesQueryingCString $ \ base64Ptr szPtr ->
botan_base64_encode
(ConstPtr bytesPtr)
bytesLen
base64Ptr
szPtr
base64Decode ::
Text -- ^ __base64_str__
-> IO ByteString -- ^ __out__
base64Decode txt =
asBytesLen (Text.encodeUtf8 txt) $ \ base64Ptr base64Len ->
alloca $ \ szPtr -> do
poke szPtr 0
-- Botan 3.12.0 added an any_null_pointers guard on botan_base64_decode that
-- rejects a NULL out, so use a non-null zero-byte sentinel for the size-query call.
code <- allocaBytes 0 $ \ sentinelPtr ->
botan_base64_decode (ConstPtr base64Ptr) base64Len sentinelPtr szPtr
case code of
BOTAN_FFI_ERROR_INSUFFICIENT_BUFFER_SPACE -> do
sz <- fromIntegral <$> peek szPtr
bs <- allocBytes sz $ \ outPtr ->
throwBotanIfNegative_ $ botan_base64_decode
(ConstPtr base64Ptr) base64Len outPtr szPtr
actual <- fromIntegral <$> peek szPtr
return $! BS.take actual bs
_ -> throwBotanError code