File: SHAKE.hs

package info (click to toggle)
haskell-cryptonite 0.30-4
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,368 kB
  • sloc: ansic: 22,009; haskell: 18,423; makefile: 8
file content (131 lines) | stat: -rw-r--r-- 5,110 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-- |
-- Module      : Crypto.Hash.SHAKE
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- Module containing the binding functions to work with the
-- SHA3 extendable output functions (SHAKE).
--
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Crypto.Hash.SHAKE
    (  SHAKE128 (..), SHAKE256 (..), HashSHAKE (..)
    ) where

import           Control.Monad (when)
import           Crypto.Hash.Types
import           Foreign.Ptr (Ptr, castPtr)
import           Foreign.Storable (Storable(..))
import           Data.Bits
import           Data.Data
import           Data.Word (Word8, Word32)

import           GHC.TypeLits (Nat, KnownNat, type (+))
import           Crypto.Internal.Nat

-- | Type class of SHAKE algorithms.
class HashAlgorithm a => HashSHAKE a where
    -- | Alternate finalization needed for cSHAKE
    cshakeInternalFinalize :: Ptr (Context a) -> Ptr (Digest a) -> IO ()
    -- | Get the digest bit length
    cshakeOutputLength :: a -> Int

-- | SHAKE128 (128 bits) extendable output function.  Supports an arbitrary
-- digest size, to be specified as a type parameter of kind 'Nat'.
--
-- Note: outputs from @'SHAKE128' n@ and @'SHAKE128' m@ for the same input are
-- correlated (one being a prefix of the other).  Results are unrelated to
-- 'SHAKE256' results.
data SHAKE128 (bitlen :: Nat) = SHAKE128
    deriving (Show, Data)

instance KnownNat bitlen => HashAlgorithm (SHAKE128 bitlen) where
    type HashBlockSize           (SHAKE128 bitlen)  = 168
    type HashDigestSize          (SHAKE128 bitlen) = Div8 (bitlen + 7)
    type HashInternalContextSize (SHAKE128 bitlen) = 376
    hashBlockSize  _          = 168
    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
    hashInternalContextSize _ = 376
    hashInternalInit p        = c_sha3_init p 128
    hashInternalUpdate        = c_sha3_update
    hashInternalFinalize      = shakeFinalizeOutput (Proxy :: Proxy bitlen)

instance KnownNat bitlen => HashSHAKE (SHAKE128 bitlen) where
    cshakeInternalFinalize = cshakeFinalizeOutput (Proxy :: Proxy bitlen)
    cshakeOutputLength _ = integralNatVal (Proxy :: Proxy bitlen)

-- | SHAKE256 (256 bits) extendable output function.  Supports an arbitrary
-- digest size, to be specified as a type parameter of kind 'Nat'.
--
-- Note: outputs from @'SHAKE256' n@ and @'SHAKE256' m@ for the same input are
-- correlated (one being a prefix of the other).  Results are unrelated to
-- 'SHAKE128' results.
data SHAKE256 (bitlen :: Nat) = SHAKE256
    deriving (Show, Data)

instance KnownNat bitlen => HashAlgorithm (SHAKE256 bitlen) where
    type HashBlockSize           (SHAKE256 bitlen) = 136
    type HashDigestSize          (SHAKE256 bitlen) = Div8 (bitlen + 7)
    type HashInternalContextSize (SHAKE256 bitlen) = 344
    hashBlockSize  _          = 136
    hashDigestSize _          = byteLen (Proxy :: Proxy bitlen)
    hashInternalContextSize _ = 344
    hashInternalInit p        = c_sha3_init p 256
    hashInternalUpdate        = c_sha3_update
    hashInternalFinalize      = shakeFinalizeOutput (Proxy :: Proxy bitlen)

instance KnownNat bitlen => HashSHAKE (SHAKE256 bitlen) where
    cshakeInternalFinalize = cshakeFinalizeOutput (Proxy :: Proxy bitlen)
    cshakeOutputLength _ = integralNatVal (Proxy :: Proxy bitlen)

shakeFinalizeOutput :: KnownNat bitlen
                    => proxy bitlen
                    -> Ptr (Context a)
                    -> Ptr (Digest a)
                    -> IO ()
shakeFinalizeOutput d ctx dig = do
    c_sha3_finalize_shake ctx
    c_sha3_output ctx dig (byteLen d)
    shakeTruncate d (castPtr dig)

cshakeFinalizeOutput :: KnownNat bitlen
                     => proxy bitlen
                     -> Ptr (Context a)
                     -> Ptr (Digest a)
                     -> IO ()
cshakeFinalizeOutput d ctx dig = do
    c_sha3_finalize_cshake ctx
    c_sha3_output ctx dig (byteLen d)
    shakeTruncate d (castPtr dig)

shakeTruncate :: KnownNat bitlen => proxy bitlen -> Ptr Word8 -> IO ()
shakeTruncate d ptr =
    when (bits > 0) $ do
        byte <- peekElemOff ptr index
        pokeElemOff ptr index (byte .&. mask)
  where
    mask = (1 `shiftL` bits) - 1
    (index, bits) = integralNatVal d `divMod` 8

foreign import ccall unsafe "cryptonite_sha3_init"
    c_sha3_init :: Ptr (Context a) -> Word32 -> IO ()

foreign import ccall "cryptonite_sha3_update"
    c_sha3_update :: Ptr (Context a) -> Ptr Word8 -> Word32 -> IO ()

foreign import ccall unsafe "cryptonite_sha3_finalize_shake"
    c_sha3_finalize_shake :: Ptr (Context a) -> IO ()

foreign import ccall unsafe "cryptonite_sha3_finalize_cshake"
    c_sha3_finalize_cshake :: Ptr (Context a) -> IO ()

foreign import ccall unsafe "cryptonite_sha3_output"
    c_sha3_output :: Ptr (Context a) -> Ptr (Digest a) -> Word32 -> IO ()