{-|
Module      : Botan.Low.KDF
Description : Key Derivation Functions (KDF)
Copyright   : (c) Leo D, 2023
License     : BSD-3-Clause
Maintainer  : leo@apotheca.io
Stability   : experimental
Portability : POSIX

Key derivation functions are used to turn some amount of shared
secret material into uniform random keys suitable for use with
symmetric algorithms. An example of an input which is useful for
a KDF is a shared secret created using Diffie-Hellman key agreement.

Typically a KDF is also used with a salt and a label. The salt should
be some random information which is available to all of the parties
that would need to use the KDF; this could be performed by setting
the salt to some kind of session identifier, or by having one of the
parties generate a random salt and including it in a message.

The label is used to bind the KDF output to some specific context. For
instance if you were using the KDF to derive a specific key referred to
as the “message key” in the protocol description, you might use a label
of “FooProtocol v2 MessageKey”. This labeling ensures that if you
accidentally use the same input key and salt in some other context, you
still use different keys in the two contexts.
-}

module Botan.Low.KDF
(

-- * Key derivation function

  KDFName(..)
, kdf

-- * KDF algorithms

, pattern HKDF
, hkdf
, pattern HKDF_Extract
, hkdf_extract
, pattern HKDF_Expand
, hkdf_expand
, pattern KDF2
, kdf2
, pattern KDF1_18033
, kdf1_18033
, pattern KDF1
, kdf1
, pattern TLS_12_PRF
, tls_12_prf
, pattern X9_42_PRF
, x9_42_prf
, pattern SP800_108_Counter
, sp800_108_counter
, pattern SP800_108_Feedback
, sp800_108_feedback
, pattern SP800_108_Pipeline
, sp800_108_pipeline
, pattern SP800_56A
, sp800_56A
, pattern SP800_56C
, sp800_56C

-- * Convenience

, kdfs

) where

import qualified Data.ByteString as ByteString

import Botan.Bindings.KDF

import Botan.Low.Hash
import Botan.Low.MAC
import Botan.Low.Error
import Botan.Low.Make
import Botan.Low.Prelude

type KDFName = ByteString

pattern HKDF
    ,   HKDF_Extract
    ,   HKDF_Expand
    ,   KDF2
    ,   KDF1_18033
    ,   KDF1
    ,   TLS_12_PRF
    ,   X9_42_PRF
    ,   SP800_108_Counter
    ,   SP800_108_Feedback
    ,   SP800_108_Pipeline
    ,   SP800_56A
    ,   SP800_56C
    :: KDFName

pattern $mHKDF :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bHKDF :: KDFName
HKDF                = BOTAN_KDF_HKDF
pattern $mHKDF_Extract :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bHKDF_Extract :: KDFName
HKDF_Extract        = BOTAN_KDF_HKDF_EXTRACT
pattern $mHKDF_Expand :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bHKDF_Expand :: KDFName
HKDF_Expand         = BOTAN_KDF_HKDF_EXPAND
pattern $mKDF2 :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bKDF2 :: KDFName
KDF2                = BOTAN_KDF_KDF2
pattern $mKDF1_18033 :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bKDF1_18033 :: KDFName
KDF1_18033          = BOTAN_KDF_KDF1_18033
pattern $mKDF1 :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bKDF1 :: KDFName
KDF1                = BOTAN_KDF_KDF1
pattern $mTLS_12_PRF :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bTLS_12_PRF :: KDFName
TLS_12_PRF          = BOTAN_KDF_TLS_12_PRF
pattern $mX9_42_PRF :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bX9_42_PRF :: KDFName
X9_42_PRF           = BOTAN_KDF_X9_42_PRF
pattern $mSP800_108_Counter :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bSP800_108_Counter :: KDFName
SP800_108_Counter   = BOTAN_KDF_SP800_108_COUNTER
pattern $mSP800_108_Feedback :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bSP800_108_Feedback :: KDFName
SP800_108_Feedback  = BOTAN_KDF_SP800_108_FEEDBACK
pattern $mSP800_108_Pipeline :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bSP800_108_Pipeline :: KDFName
SP800_108_Pipeline  = BOTAN_KDF_SP800_108_PIPELINE
pattern $mSP800_56A :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bSP800_56A :: KDFName
SP800_56A           = BOTAN_KDF_SP800_56A
pattern $mSP800_56C :: forall {r}. KDFName -> ((# #) -> r) -> ((# #) -> r) -> r
$bSP800_56C :: KDFName
SP800_56C           = BOTAN_KDF_SP800_56C

hkdf :: HashName -> KDFName
hkdf :: KDFName -> KDFName
hkdf KDFName
h = KDFName
HKDF KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
hkdf_extract :: KDFName -> KDFName
hkdf_extract KDFName
h = KDFName
HKDF_Extract KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
hkdf_expand :: KDFName -> KDFName
hkdf_expand KDFName
h = KDFName
HKDF_Expand KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
kdf2 :: KDFName -> KDFName
kdf2 KDFName
h = KDFName
KDF2 KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
kdf1_18033 :: KDFName -> KDFName
kdf1_18033 KDFName
h = KDFName
KDF1_18033 KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
kdf1 :: KDFName -> KDFName
kdf1 KDFName
h = KDFName
KDF1 KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
tls_12_prf :: KDFName -> KDFName
tls_12_prf KDFName
h = KDFName
TLS_12_PRF KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
x9_42_prf :: KDFName -> KDFName
x9_42_prf KDFName
h = KDFName
X9_42_PRF KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
sp800_108_counter :: KDFName -> KDFName
sp800_108_counter KDFName
h = KDFName
SP800_108_Counter KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
HMAC KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
sp800_108_feedback :: KDFName -> KDFName
sp800_108_feedback KDFName
h = KDFName
SP800_108_Feedback KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
HMAC KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
sp800_108_pipeline :: KDFName -> KDFName
sp800_108_pipeline KDFName
h = KDFName
SP800_108_Pipeline KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
HMAC KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
sp800_56A :: KDFName -> KDFName
sp800_56A KDFName
h = KDFName
SP800_56A KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
HMAC KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h
sp800_56C :: KDFName -> KDFName
sp800_56C KDFName
h = KDFName
SP800_56C KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
HMAC KDFName -> KDFName -> KDFName
forall a. (IsString a, Semigroup a) => a -> a -> a
/$ KDFName
h

kdfs :: [KDFName]
kdfs = [[KDFName]] -> [KDFName]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
    [ [ KDFName -> KDFName
hkdf KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
hkdf_extract KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
hkdf_expand KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
kdf2 KDFName
h | KDFName
h <- [KDFName]
allHashes ]
    , [ KDFName -> KDFName
kdf1_18033 KDFName
h | KDFName
h <- [KDFName]
allHashes ]
    , [ KDFName -> KDFName
kdf1 KDFName
h | KDFName
h <- [KDFName]
allHashes ]
    , [ KDFName -> KDFName
tls_12_prf KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
x9_42_prf KDFName
SHA1 ]
    , [ KDFName -> KDFName
sp800_108_counter KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
sp800_108_feedback KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
sp800_108_pipeline KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
sp800_56A KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    , [ KDFName -> KDFName
sp800_56C KDFName
h | KDFName
h <- [KDFName]
cryptohashes ]
    ]

-- SEE: Algos here: https://botan.randombit.net/doxygen/classBotan_1_1KDF.html
kdf
    :: KDFName          -- ^ __kdf_algo__: KDF algorithm, e.g., "SP800-56C"
    -> Int              -- ^ __out_len__: the desired output length in bytes
    -> ByteString       -- ^ __secret[]__: the secret input
    -> ByteString       -- ^ __salt[]__: a diversifier
    -> ByteString       -- ^ __label[]__: purpose for the derived keying material
    -> IO ByteString    -- ^ __out[]__: buffer holding the derived key
kdf :: KDFName -> Int -> KDFName -> KDFName -> KDFName -> IO KDFName
kdf KDFName
algo Int
outLen KDFName
secret KDFName
salt KDFName
label = Int -> (Ptr Word8 -> IO ()) -> IO KDFName
forall byte. Int -> (Ptr byte -> IO ()) -> IO KDFName
allocBytes Int
outLen ((Ptr Word8 -> IO ()) -> IO KDFName)
-> (Ptr Word8 -> IO ()) -> IO KDFName
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
outPtr -> do
    KDFName -> (Ptr CChar -> IO ()) -> IO ()
forall a. KDFName -> (Ptr CChar -> IO a) -> IO a
asCString KDFName
algo ((Ptr CChar -> IO ()) -> IO ()) -> (Ptr CChar -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr CChar
algoPtr -> do
        KDFName -> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall byte a. KDFName -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen KDFName
secret ((Ptr Word8 -> CSize -> IO ()) -> IO ())
-> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
secretPtr CSize
secretLen -> do
            KDFName -> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall byte a. KDFName -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen KDFName
salt ((Ptr Word8 -> CSize -> IO ()) -> IO ())
-> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
saltPtr CSize
saltLen -> do
                KDFName -> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall byte a. KDFName -> (Ptr byte -> CSize -> IO a) -> IO a
asBytesLen KDFName
label ((Ptr Word8 -> CSize -> IO ()) -> IO ())
-> (Ptr Word8 -> CSize -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ Ptr Word8
labelPtr CSize
labelLen -> do
                    HasCallStack => IO BotanErrorCode -> IO ()
IO BotanErrorCode -> IO ()
throwBotanIfNegative_ (IO BotanErrorCode -> IO ()) -> IO BotanErrorCode -> IO ()
forall a b. (a -> b) -> a -> b
$ ConstPtr CChar
-> Ptr Word8
-> CSize
-> ConstPtr Word8
-> CSize
-> ConstPtr Word8
-> CSize
-> ConstPtr Word8
-> CSize
-> IO BotanErrorCode
botan_kdf
                        (Ptr CChar -> ConstPtr CChar
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr CChar
algoPtr)
                        Ptr Word8
outPtr
                        (Int -> CSize
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
outLen)
                        (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
secretPtr)
                        CSize
secretLen
                        (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
saltPtr)
                        CSize
saltLen
                        (Ptr Word8 -> ConstPtr Word8
forall a. Ptr a -> ConstPtr a
ConstPtr Ptr Word8
labelPtr)
                        CSize
labelLen