{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
module Crypto.Nettle.Ciphers.Internal
( NettleCipher(..)
, NettleBlockCipher(..)
, NettleStreamCipher(..)
, NettleBlockedStreamCipher(..)
, NettleGCM
, nettle_cipherInit
, nettle_cipherInit'
, nettle_ecbEncrypt
, nettle_ecbDecrypt
, nettle_cbcEncrypt
, nettle_cbcDecrypt
, nettle_cfbEncrypt
, nettle_cfbDecrypt
, nettle_ctrCombine
, nettle_streamCombine
, nettle_streamSetNonce
, nettle_blockedStreamCombine
, nettle_blockedStreamSetNonce
, nettle_gcm_aeadInit
, nettle_gcm_aeadStateAppendHeader
, nettle_gcm_aeadStateEncrypt
, nettle_gcm_aeadStateDecrypt
, nettle_gcm_aeadStateFinalize
) where
import Crypto.Cipher.Types as T
import Data.Tagged
import Data.Byteable (Byteable(..))
import Data.SecureMem
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import Data.Bits (xor)
import Nettle.Utils
import Crypto.Nettle.Ciphers.ForeignImports
{-# ANN module "HLint: ignore Use camelCase" #-}
class NettleCipher c where
nc_cipherInit :: Tagged c (Ptr Word8 -> Word -> Ptr Word8 -> IO())
nc_cipherName :: Tagged c String
nc_cipherKeySize :: Tagged c T.KeySizeSpecifier
nc_ctx_size :: Tagged c Int
nc_ctx :: c -> SecureMem
nc_Ctx :: SecureMem -> c
class NettleCipher c => NettleBlockCipher c where
nbc_blockSize :: Tagged c Int
nbc_encrypt_ctx_offset :: Tagged c (Ptr Word8 -> Ptr Word8)
nbc_encrypt_ctx_offset = Tagged id
nbc_decrypt_ctx_offset :: Tagged c (Ptr Word8 -> Ptr Word8)
nbc_decrypt_ctx_offset = Tagged id
nbc_ecb_encrypt :: Tagged c NettleCryptFunc
nbc_ecb_decrypt :: Tagged c NettleCryptFunc
nbc_fun_encrypt :: Tagged c (FunPtr NettleCryptFunc)
nbc_fun_decrypt :: Tagged c (FunPtr NettleCryptFunc)
class NettleCipher c => NettleStreamCipher c where
nsc_streamCombine :: Tagged c NettleCryptFunc
nsc_nonceSize :: Tagged c T.KeySizeSpecifier
nsc_nonceSize = Tagged $ T.KeySizeEnum []
nsc_setNonce :: Tagged c (Maybe (Ptr Word8 -> Word -> Ptr Word8 -> IO ()))
nsc_setNonce = Tagged Nothing
class NettleCipher c => NettleBlockedStreamCipher c where
nbsc_blockSize :: Tagged c Int
nbsc_IncompleteState :: c -> B.ByteString -> c
nbsc_incompleteState :: c -> B.ByteString
nbsc_streamCombine :: Tagged c NettleCryptFunc
nbsc_nonceSize :: Tagged c T.KeySizeSpecifier
nbsc_nonceSize = Tagged $ T.KeySizeEnum []
nbsc_setNonce :: Tagged c (Maybe (Ptr Word8 -> Word -> Ptr Word8 -> IO ()))
nbsc_setNonce = Tagged Nothing
nettle_cipherInit :: NettleCipher c => Key c -> c
nettle_cipherInit k = let ctx = nettle_cipherInit' (nc_cipherInit `witness` ctx) k in ctx
nettle_cipherInit' :: NettleCipher c => (Ptr Word8 -> Word -> Ptr Word8 -> IO()) -> Key c -> c
nettle_cipherInit' f k = let ctx = nc_Ctx $ key_init f (nc_ctx_size `witness` ctx) k in ctx
assert_blockSize :: NettleBlockCipher c => c -> B.ByteString -> a -> a
assert_blockSize c src result = if 0 /= B.length src `mod` (nbc_blockSize `witness` c) then error "input not a multiple of blockSize" else result
nettle_ecbEncrypt :: NettleBlockCipher c => c -> B.ByteString -> B.ByteString
nettle_ecbEncrypt c src = assert_blockSize c src $ c_run_crypt (nbc_encrypt_ctx_offset `witness` c) (nbc_ecb_encrypt `witness` c) (nc_ctx c) src
nettle_ecbDecrypt :: NettleBlockCipher c => c -> B.ByteString -> B.ByteString
nettle_ecbDecrypt c src = assert_blockSize c src $ c_run_crypt (nbc_decrypt_ctx_offset `witness` c) (nbc_ecb_decrypt `witness` c) (nc_ctx c) src
nettle_cbcEncrypt :: NettleBlockCipher c => c -> IV c -> B.ByteString -> B.ByteString
nettle_cbcEncrypt c iv src = assert_blockSize c src $ blockmode_run (nbc_encrypt_ctx_offset `witness` c) c_cbc_encrypt (nbc_fun_encrypt `witness` c) (nc_ctx c) iv src
nettle_cbcDecrypt :: NettleBlockCipher c => c -> IV c -> B.ByteString -> B.ByteString
nettle_cbcDecrypt c iv src = assert_blockSize c src $ blockmode_run (nbc_decrypt_ctx_offset `witness` c) c_cbc_decrypt (nbc_fun_decrypt `witness` c) (nc_ctx c) iv src
nettle_cfbEncrypt :: NettleBlockCipher c => c -> IV c -> B.ByteString -> B.ByteString
nettle_cfbEncrypt c iv src = assert_blockSize c src $ blockmode_run (nbc_encrypt_ctx_offset `witness` c) c_cfb_encrypt (nbc_fun_encrypt `witness` c) (nc_ctx c) iv src
nettle_cfbDecrypt :: NettleBlockCipher c => c -> IV c -> B.ByteString -> B.ByteString
nettle_cfbDecrypt c iv src = assert_blockSize c src $ blockmode_run (nbc_encrypt_ctx_offset `witness` c) c_cfb_decrypt (nbc_fun_encrypt `witness` c) (nc_ctx c) iv src
nettle_ctrCombine :: NettleBlockCipher c => c -> IV c -> B.ByteString -> B.ByteString
nettle_ctrCombine c = blockmode_run (nbc_encrypt_ctx_offset `witness` c) c_ctr_crypt (nbc_fun_encrypt `witness` c) (nc_ctx c)
nettle_streamCombine :: NettleStreamCipher c => c -> B.ByteString -> (B.ByteString, c)
nettle_streamCombine c indata = let (r, c') = stream_crypt (nsc_streamCombine `witness` c) (nc_ctx c) indata in (r, nc_Ctx c')
nettle_streamSetNonce :: NettleStreamCipher c => c -> B.ByteString -> Maybe c
nettle_streamSetNonce c nonce = case nsc_setNonce `witness` c of
Nothing -> Nothing
Just setnonce -> unsafeDupablePerformIO $
secureMemCopy (nc_ctx c) >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withByteStringPtr nonce $ \noncelen nonceptr ->
setnonce ctxptr noncelen nonceptr >>
return (Just $ nc_Ctx ctx')
nettle_blockedStreamCombine :: NettleBlockedStreamCipher c => c -> B.ByteString -> (B.ByteString, c)
nettle_blockedStreamCombine c indata = if B.length indata == 0 then (indata, c) else
let inc = nbsc_incompleteState c; blocksiz = nbsc_blockSize `witness` c in
if B.length inc /= 0
then let
(i1, i2) = B.splitAt (B.length inc) indata
(inc1, inc2) = B.splitAt (B.length indata) inc
r1 = B.pack $ B.zipWith xor i1 inc1
c' = if B.length inc2 == 0 then nc_Ctx $ nc_ctx c else nbsc_IncompleteState c inc2
(r, c'') = nettle_blockedStreamCombine c' i2
in (B.append r1 r, c'')
else if B.length indata `mod` blocksiz /= 0
then let
padding = B.replicate (blocksiz - (B.length indata `mod` blocksiz)) 0
(r', c') = stream_crypt (nbsc_streamCombine `witness` c) (nc_ctx c) (B.append indata padding)
(r, inc') = B.splitAt (B.length indata) r'
in (r, nbsc_IncompleteState (nc_Ctx c') inc')
else
let (r, c') = stream_crypt (nbsc_streamCombine `witness` c) (nc_ctx c) indata in (r, nc_Ctx c')
nettle_blockedStreamSetNonce :: NettleBlockedStreamCipher c => c -> B.ByteString -> Maybe c
nettle_blockedStreamSetNonce c nonce = case nbsc_setNonce `witness` c of
Nothing -> Nothing
Just setnonce -> unsafeDupablePerformIO $
secureMemCopy (nc_ctx c) >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withByteStringPtr nonce $ \noncelen nonceptr ->
setnonce ctxptr noncelen nonceptr >>
return (Just $ nc_Ctx ctx')
nettle_gcm_aeadInit :: (NettleBlockCipher c, AEADModeImpl c NettleGCM, Byteable iv) => c -> iv -> Maybe (AEAD c)
nettle_gcm_aeadInit c iv = if nbc_blockSize `witness` c == 16 then Just $ AEAD c $ AEADState $ gcm_init (nbc_encrypt_ctx_offset `witness` c) (nbc_fun_encrypt `witness` c) (nc_ctx c) iv else Nothing
nettle_gcm_aeadStateAppendHeader :: t -> NettleGCM -> B.ByteString -> NettleGCM
nettle_gcm_aeadStateAppendHeader _ = gcm_update
nettle_gcm_aeadStateEncrypt :: NettleBlockCipher c => c -> NettleGCM -> B.ByteString -> (B.ByteString, NettleGCM)
nettle_gcm_aeadStateEncrypt c = gcm_crypt c_gcm_encrypt (nbc_encrypt_ctx_offset `witness` c) (nbc_fun_encrypt `witness` c) (nc_ctx c)
nettle_gcm_aeadStateDecrypt :: NettleBlockCipher c => c -> NettleGCM -> B.ByteString -> (B.ByteString, NettleGCM)
nettle_gcm_aeadStateDecrypt c = gcm_crypt c_gcm_decrypt (nbc_encrypt_ctx_offset `witness` c) (nbc_fun_encrypt `witness` c) (nc_ctx c)
nettle_gcm_aeadStateFinalize :: NettleBlockCipher c => c -> NettleGCM -> Int -> AuthTag
nettle_gcm_aeadStateFinalize c = gcm_digest (nbc_encrypt_ctx_offset `witness` c) (nbc_fun_encrypt `witness` c) (nc_ctx c)
key_init
:: ToSecureMem k
=> (Ptr Word8 -> Word -> Ptr Word8 -> IO ())
-> Int -> k -> SecureMem
key_init initfun size k = unsafeCreateSecureMem size $ \ctxptr ->
withSecureMemPtrSz (toSecureMem k) $ \ksize kptr -> initfun ctxptr (fromIntegral ksize) kptr
c_run_crypt
:: (Ptr Word8 -> Ptr Word8)
-> NettleCryptFunc
-> SecureMem -> B.ByteString -> B.ByteString
c_run_crypt ctxoffset cfun ctx indata = unsafeDupablePerformIO $ withSecureMemPtr ctx $ \ctxptr ->
withByteStringPtr indata $ \indatalen indataptr ->
B.create (B.length indata) $ \outptr ->
cfun (ctxoffset ctxptr) indatalen outptr indataptr
blockmode_run
:: (Byteable iv)
=> (Ptr Word8 -> Ptr Word8)
-> NettleBlockMode
-> FunPtr NettleCryptFunc
-> SecureMem -> iv -> B.ByteString -> B.ByteString
blockmode_run ctxoffset mode crypt ctx iv indata = unsafeDupablePerformIO $ withSecureMemPtr ctx $ \ctxptr ->
withByteStringPtr indata $ \indatalen indataptr ->
withSecureMemPtrSz (toSecureMem $ toBytes iv) $ \ivlen ivptr ->
B.create (B.length indata) $ \outptr ->
mode (ctxoffset ctxptr) crypt (fromIntegral ivlen) ivptr indatalen outptr indataptr
data NettleGCM = NettleGCM !SecureMem !SecureMem
gcm_init
:: (Byteable iv)
=> (Ptr Word8 -> Ptr Word8)
-> FunPtr NettleCryptFunc
-> SecureMem -> iv -> NettleGCM
gcm_init encctxoffset encrypt encctx iv = unsafeDupablePerformIO $
withBytePtr iv $ \ivptr ->
withSecureMemPtr encctx $ \encctxptr -> do
h <- createSecureMem c_gcm_key_size $ \hptr ->
c_gcm_set_key hptr (encctxoffset encctxptr) encrypt
withSecureMemPtr h $ \hptr -> do
ctx <- createSecureMem c_gcm_ctx_size $ \ctxptr ->
c_gcm_set_iv ctxptr hptr (fromIntegral $ byteableLength iv) ivptr
return (NettleGCM ctx h)
gcm_update
:: NettleGCM -> B.ByteString -> NettleGCM
gcm_update (NettleGCM ctx h) indata = unsafeDupablePerformIO $
secureMemCopy ctx >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withSecureMemPtr h $ \hptr ->
withByteStringPtr indata $ \indatalen indataptr ->
c_gcm_update ctxptr hptr indatalen indataptr >>
return (NettleGCM ctx' h)
gcm_crypt
:: NettleGCMMode
-> (Ptr Word8 -> Ptr Word8)
-> FunPtr NettleCryptFunc
-> SecureMem -> NettleGCM -> B.ByteString -> (B.ByteString, NettleGCM)
gcm_crypt mode encctxoffset encrypt encctx (NettleGCM ctx h) indata = unsafeDupablePerformIO $
secureMemCopy ctx >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withSecureMemPtr h $ \hptr ->
withSecureMemPtr encctx $ \encctxptr ->
withByteStringPtr indata $ \indatalen indataptr -> do
outdata <- B.create (B.length indata) $ \outptr ->
mode ctxptr hptr (encctxoffset encctxptr) encrypt indatalen outptr indataptr
return (outdata, NettleGCM ctx' h)
gcm_digest
:: (Ptr Word8 -> Ptr Word8)
-> FunPtr NettleCryptFunc
-> SecureMem -> NettleGCM -> Int -> AuthTag
gcm_digest encctxoffset encrypt encctx (NettleGCM ctx h) taglen = unsafeDupablePerformIO $
secureMemCopy ctx >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withSecureMemPtr h $ \hptr ->
withSecureMemPtr encctx $ \encctxptr -> do
tag <- B.create (fromIntegral taglen) $ \tagptr ->
c_gcm_digest ctxptr hptr (encctxoffset encctxptr) encrypt (fromIntegral taglen) tagptr
return $ AuthTag tag
stream_crypt
:: NettleCryptFunc
-> SecureMem -> B.ByteString -> (B.ByteString, SecureMem)
stream_crypt crypt ctx indata = unsafeDupablePerformIO $
secureMemCopy ctx >>= \ctx' ->
withSecureMemPtr ctx' $ \ctxptr ->
withByteStringPtr indata $ \indatalen indataptr -> do
outdata <- B.create (B.length indata) $ \outptr ->
crypt ctxptr indatalen outptr indataptr
return (outdata, ctx')