crypton-1.0.2: Crypto/Cipher/XSalsa.hs
{-# LANGUAGE ForeignFunctionInterface #-}
-- |
-- Module : Crypto.Cipher.XSalsa
-- License : BSD-style
-- Maintainer : Brandon Hamilton <brandon.hamilton@gmail.com>
-- Stability : stable
-- Portability : good
--
-- Implementation of XSalsa20 algorithm
-- <https://cr.yp.to/snuffle/xsalsa-20081128.pdf>
-- Based on the Salsa20 algorithm with 256 bit key extended with 192 bit nonce
module Crypto.Cipher.XSalsa (
initialize,
derive,
combine,
generate,
State,
) where
import Crypto.Cipher.Salsa hiding (initialize)
import Crypto.Internal.ByteArray (ByteArrayAccess)
import qualified Crypto.Internal.ByteArray as B
import Crypto.Internal.Compat
import Crypto.Internal.Imports
import Foreign.Ptr
-- | Initialize a new XSalsa context with the number of rounds,
-- the key and the nonce associated.
initialize
:: (ByteArrayAccess key, ByteArrayAccess nonce)
=> Int
-- ^ number of rounds (8,12,20)
-> key
-- ^ the key (256 bits)
-> nonce
-- ^ the nonce (192 bits)
-> State
-- ^ the initial XSalsa state
initialize nbRounds key nonce
| kLen /= 32 =
error "XSalsa: key length should be 256 bits"
| nonceLen /= 24 =
error "XSalsa: nonce length should be 192 bits"
| nbRounds `notElem` [8, 12, 20] = error "XSalsa: rounds should be 8, 12 or 20"
| otherwise = unsafeDoIO $ do
stPtr <- B.alloc 132 $ \stPtr ->
B.withByteArray nonce $ \noncePtr ->
B.withByteArray key $ \keyPtr ->
ccrypton_xsalsa_init stPtr nbRounds kLen keyPtr nonceLen noncePtr
return $ State stPtr
where
kLen = B.length key
nonceLen = B.length nonce
-- | Use an already initialized context and new nonce material to derive another
-- XSalsa context.
--
-- This allows a multi-level cascade where a first key @k1@ and nonce @n1@ is
-- used to get @HState(k1,n1)@, and this value is then used as key @k2@ to build
-- @XSalsa(k2,n2)@. Function 'initialize' is to be called with the first 192
-- bits of @n1|n2@, and the call to @derive@ should add the remaining 128 bits.
--
-- The output context always uses the same number of rounds as the input
-- context.
derive
:: ByteArrayAccess nonce
=> State
-- ^ base XSalsa state
-> nonce
-- ^ the remainder nonce (128 bits)
-> State
-- ^ the new XSalsa state
derive (State stPtr') nonce
| nonceLen /= 16 = error "XSalsa: nonce length should be 128 bits"
| otherwise = unsafeDoIO $ do
stPtr <- B.copy stPtr' $ \stPtr ->
B.withByteArray nonce $ \noncePtr ->
ccrypton_xsalsa_derive stPtr nonceLen noncePtr
return $ State stPtr
where
nonceLen = B.length nonce
foreign import ccall "crypton_xsalsa_init"
ccrypton_xsalsa_init
:: Ptr State -> Int -> Int -> Ptr Word8 -> Int -> Ptr Word8 -> IO ()
foreign import ccall "crypton_xsalsa_derive"
ccrypton_xsalsa_derive :: Ptr State -> Int -> Ptr Word8 -> IO ()