--------------------------------------------------------------------------------
-- Internal APIs

header :: Chain                -- Chain input
       -> KeyPair              -- Ephemeral keys
       -> Maybe KeyPair        -- Sender keys
       -> PublicKey Noise -- Receiver public key
       -> (Header, Chain)
header (Chain chain) (ephPK, ephSK) senderKeys recvPK
  = (Header ephPK encSendPK mac, key2)
  where
    sendPK = maybe ephPK fst senderKeys

    -- Key exchange
    dh1 = curve25519 ephSK recvPK
    dh2 = maybe dh1 (\(_,s) -> curve25519 s recvPK) senderKeys

    -- Key derivation, step one
    key1 = SecretKey $ nhash (txt <> dh1 <> chain)
      where txt = B8.pack "Noise_Box_KDF1"

    -- Block encryption, key derivation step two
    (chainTmp, macKey, encSendPK)
      = ( B.take 32 block
        , B.take 32 $ B.drop 32 block
        , B.drop 64 block)
      where
        block = ncipher datum key1
        datum = B.replicate 64 0x0 <> (unPublicKey sendPK)

    -- MAC derivation
    mac = Poly1305.authenticate (SecretKey macKey) dataBlock
      where dataBlock = (unPublicKey ephPK) <> encSendPK

    -- Key derivation, step three
    key2 = Chain $ nhash (txt <> dh2 <> chainTmp)
      where txt = B8.pack "Noise_Box_KDF2"
header :: Chain                -- Chain input
       -> KeyPair              -- Ephemeral keys
       -> Maybe KeyPair        -- Sender keys
       -> PublicKey Noise -- Receiver public key
       -> (Header, Chain)
header (Chain chain) (ephPK, ephSK) senderKeys recvPK
  = (Header ephPK encSendPK mac, key2)
  where
    sendPK = maybe ephPK fst senderKeys

    -- Key exchange
    dh1 = curve25519 ephSK recvPK
    dh2 = maybe dh1 (\(_,s) -> curve25519 s recvPK) senderKeys

    -- Key derivation, step one
    key1 = SecretKey $ nhash (txt <> dh1 <> chain)
      where txt = B8.pack "Noise_Box_KDF1"

    -- Block encryption, key derivation step two
    (chainTmp, macKey, encSendPK)
      = ( B.take 32 block
        , B.take 32 $ B.drop 32 block
        , B.drop 64 block)
      where
        block = ncipher datum key1
        datum = B.replicate 64 0x0 <> (unPublicKey sendPK)

    -- MAC derivation
    mac = Poly1305.authenticate (SecretKey macKey) dataBlock
      where dataBlock = (unPublicKey ephPK) <> encSendPK

    -- Key derivation, step three
    key2 = Chain $ nhash (txt <> dh2 <> chainTmp)
      where txt = B8.pack "Noise_Box_KDF2"
header (Chain chain) (ephPK, ephSK) senderKeys recvPK
  = (Header ephPK encSendPK mac, key2)
  where
    sendPK = maybe ephPK fst senderKeys

    -- Key exchange
    dh1 = curve25519 ephSK recvPK
    dh2 = maybe dh1 (\(_,s) -> curve25519 s recvPK) senderKeys

    -- Key derivation, step one
    key1 = SecretKey $ nhash (txt <> dh1 <> chain)
      where txt = B8.pack "Noise_Box_KDF1"

    -- Block encryption, key derivation step two
    (chainTmp, macKey, encSendPK)
      = ( B.take 32 block
        , B.take 32 $ B.drop 32 block
        , B.drop 64 block)
      where
        block = ncipher datum key1
        datum = B.replicate 64 0x0 <> (unPublicKey sendPK)

    -- MAC derivation
    mac = Poly1305.authenticate (SecretKey macKey) dataBlock
      where dataBlock = (unPublicKey ephPK) <> encSendPK

    -- Key derivation, step three
    key2 = Chain $ nhash (txt <> dh2 <> chainTmp)
      where txt = B8.pack "Noise_Box_KDF2"