packages feed

nettle-0.4: src/Nettle/Utils.hs

{-# LANGUAGE CPP #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Crypto.Nettle.Hash.Types
-- Copyright   :  (c) 2013 Stefan Bühler
-- License     :  MIT-style (see the file COPYING)
-- 
-- Maintainer  :  stbuehler@web.de
-- Stability   :  experimental
-- Portability :  portable
--
-- Collection of internal utility functions and exports of common imports
--
-----------------------------------------------------------------------------

module Nettle.Utils
	( Ptr
	, plusPtr
	, FunPtr
	, Word8
	, Word
	, forM_
	, unsafeDupablePerformIO
	, withByteStringPtr
	, copyScrubbedBytes
	, copyAndConvertToScrubbedBytes
	, createScrubbedBytes
	, concatToScrubbedBytes
	, netEncode

	, AlignedContext
	, alignedCtxCreate
	, alignedCtxCopy
	, alignedCtxBuffer
	, alignedCtxOffset
	, withAlignedContext
	) where

import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B

import Foreign.Ptr (Ptr, plusPtr, FunPtr, ptrToWordPtr)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Marshal.Utils (copyBytes)
import Data.Word (Word8, Word)
import Control.Monad (forM_)

import System.IO.Unsafe (unsafeDupablePerformIO)

{-|
Run action in IO monad with length and pointer to first byte of a 'B.ByteString'
-}
withByteStringPtr :: B.ByteString -> (Word -> Ptr Word8 -> IO a) -> IO a
withByteStringPtr b f = withForeignPtr fptr $ \ptr -> f (fromIntegral len) (ptr `plusPtr` off)
	where (fptr, off, len) = B.toForeignPtr b

{-|
Copy a 'BA.ScrubbedBytes'.
-}
copyScrubbedBytes :: BA.ScrubbedBytes -> BA.ScrubbedBytes
copyScrubbedBytes ba = BA.copyAndFreeze ba (\_ -> return ())

{-|
Make a copy of a 'BA.ByteArrayAccess' that gets scrubbed.
-}
copyAndConvertToScrubbedBytes :: BA.ByteArrayAccess a => a -> BA.ScrubbedBytes
copyAndConvertToScrubbedBytes = BA.convert

{-|
Create a 'BA.ScrubbedBytes'. Used for type hinting.
-}
createScrubbedBytes :: Int -> (Ptr p -> IO ()) -> IO BA.ScrubbedBytes
createScrubbedBytes = BA.create

{-|
Concatenate a 'BA.ByteArrayAccess' to a 'BA.ScrubbedBytes'. Used for type hinting.
-}
concatToScrubbedBytes :: BA.ByteArrayAccess a => [a] -> BA.ScrubbedBytes
concatToScrubbedBytes = BA.concat

{-|
Encode any 'Integral' @value@ in @bytes@ 'Word8' as big endian value.
-}
netEncode :: (Integral n) => Int {- ^ @bytes@ argument -} -> n {- ^ @value@ argument -} -> [Word8]
netEncode bytes = _work bytes [] where
	_work 0 r _ = r
	_work n r v = let (d, m) = divMod v 256 in _work (n-1) (fromIntegral m:r) d

-- Nettle 4 implementations (GCM, CMAC, EAX, OCB, SIV, ...) use 16-byte
-- aligned accesses on parts of their contexts, while @ram@'s 'ScrubbedBytes'
-- only guarantees 8-byte alignment.  Contexts are therefore over-allocated
-- with padding, and the pointer handed to C is aligned to a 16-byte boundary.
-- The byte offset to the aligned start of a buffer is stored alongside it;
-- because 'copyScrubbedBytes' would not preserve the alignment of the copied
-- data, copies re-place the context struct into the aligned region of a
-- freshly allocated buffer.

-- | The alignment required by Nettle contexts that contain 16-byte aligned members.
alignedCtxAlignment :: Int
alignedCtxAlignment = 16

-- | An over-allocated 'BA.ScrubbedBytes' buffer together with the offset of the
--   aligned start of the context struct within it.
data AlignedContext = AlignedContext !Int !BA.ScrubbedBytes

alignedCtxSize :: Int -> Int
alignedCtxSize n = n + alignedCtxAlignment - 1

alignedCtxOffset :: AlignedContext -> Int
alignedCtxOffset (AlignedContext off _) = off

alignedCtxBuffer :: AlignedContext -> BA.ScrubbedBytes
alignedCtxBuffer (AlignedContext _ buf) = buf

-- | The offset (relative to the buffer start) of the 16-byte aligned region.
alignedCtxOffsetFromBase :: BA.ScrubbedBytes -> Int
alignedCtxOffsetFromBase ba = unsafeDupablePerformIO $
	BA.withByteArray ba $ \p ->
		return (fromIntegral ((fromIntegral alignedCtxAlignment - ptrToWordPtr p `mod` fromIntegral alignedCtxAlignment) `mod` fromIntegral alignedCtxAlignment))

-- | Run an IO action with a pointer to the aligned start of a freshly allocated,
--   zeroed, over-allocated buffer; returns the resulting context with offset.
alignedCtxCreate :: Int -> (Ptr Word8 -> IO ()) -> IO AlignedContext
alignedCtxCreate size act = do
	buf <- BA.create (alignedCtxSize size) (return . const ())
	let off = alignedCtxOffsetFromBase buf
	BA.withByteArray buf $ \p -> act (p `plusPtr` off)
	return $ AlignedContext off buf

-- | Run an IO action with a pointer to the aligned start of a freshly allocated,
--   zeroed, over-allocated buffer; returns the result of the action.
withAlignedContext :: Int -> (Ptr Word8 -> IO a) -> IO a
withAlignedContext size act = do
	buf <- BA.create (alignedCtxSize size) (return . const ())
	let off = alignedCtxOffsetFromBase buf
	BA.withByteArray buf $ \p -> act (p `plusPtr` off)

-- | Copy the @size@-byte context struct from @src@ into the aligned region of a
--   fresh zeroed buffer, then run an IO action with a pointer to it; returns the
--   resulting context with offset.
alignedCtxCopy :: AlignedContext -> Int -> (Ptr Word8 -> IO ()) -> IO AlignedContext
alignedCtxCopy (AlignedContext srcOff src) size act = do
	dst <- BA.create (alignedCtxSize size) (return . const ())
	let dstOff = alignedCtxOffsetFromBase dst
	BA.withByteArray dst $ \dptr ->
		BA.withByteArray src $ \sptr ->
			copyBytes (dptr `plusPtr` dstOff) (sptr `plusPtr` srcOff) size
	BA.withByteArray dst $ \p -> act (p `plusPtr` dstOff)
	return $ AlignedContext dstOff dst