\section{DHT Packet}
The DHT Packet contains the sender's DHT Public Key, an encryption Nonce, and
an encrypted payload. The payload is encrypted with the DHT secret key of the
sender, the DHT public key of the receiver, and the nonce that is sent along
with the packet. DHT Packets are sent inside Protocol Packets with a varying
Packet Kind.
\begin{tabular}{l|l|l}
Length & Type & \href{#protocol-packet}{Contents} \\
\hline
\texttt{32} & Public Key & Sender DHT Public Key \\
\texttt{24} & Nonce & Random nonce \\
\texttt{[16,]} & Bytes & Encrypted payload \\
\end{tabular}
The encrypted payload is at least 16 bytes long, because the encryption
includes a \href{https://en.wikipedia.org/wiki/Message_authentication_code}{MAC}
of 16 bytes. A 16 byte payload would thus be the empty message. The DHT
protocol never actually sends empty messages, so in reality the minimum size is
27 bytes for the \href{#ping-service}{Ping Packet}.
\begin{code}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE Safe #-}
module Network.Tox.DHT.DhtPacket where
import Control.Applicative ((<$>), (<*>))
import Data.Binary (Binary, get, put)
import Data.Binary.Get (getRemainingLazyByteString)
import Data.Binary.Put (putByteString, runPut)
import qualified Data.ByteString.Lazy as LazyByteString
import Data.MessagePack (MessagePack)
import Data.Typeable (Typeable)
import GHC.Generics (Generic)
import Network.Tox.Crypto.Box (CipherText, PlainText (..),
unCipherText)
import qualified Network.Tox.Crypto.Box as Box
import Network.Tox.Crypto.Key (Nonce, PublicKey)
import Network.Tox.Crypto.Keyed (Keyed)
import qualified Network.Tox.Crypto.Keyed as Keyed
import Network.Tox.Crypto.KeyPair (KeyPair (..))
import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
data DhtPacket = DhtPacket
{ senderPublicKey :: PublicKey
, encryptionNonce :: Nonce
, encryptedPayload :: CipherText
}
deriving (Eq, Read, Show, Generic, Typeable)
instance MessagePack DhtPacket
instance Binary DhtPacket where
put packet = do
put $ senderPublicKey packet
put $ encryptionNonce packet
putByteString . unCipherText . encryptedPayload $ packet
get =
DhtPacket <$> get <*> get <*> (LazyByteString.toStrict <$> getRemainingLazyByteString >>= Box.cipherText)
encrypt :: KeyPair -> PublicKey -> Nonce -> PlainText -> DhtPacket
encrypt = (((Keyed.runNullKeyed .) .) .) . encryptKeyed
encryptKeyed :: Keyed m => KeyPair -> PublicKey -> Nonce -> PlainText -> m DhtPacket
encryptKeyed (KeyPair senderSecretKey senderPublicKey') receiverPublicKey nonce plainText =
(\combinedKey -> DhtPacket senderPublicKey' nonce $
Box.encrypt combinedKey nonce plainText) <$>
Keyed.getCombinedKey senderSecretKey receiverPublicKey
encode :: Binary payload => KeyPair -> PublicKey -> Nonce -> payload -> DhtPacket
encode = (((Keyed.runNullKeyed .) .) .) . encodeKeyed
encodeKeyed :: (Binary payload, Keyed m) => KeyPair -> PublicKey -> Nonce -> payload -> m DhtPacket
encodeKeyed keyPair receiverPublicKey nonce =
encryptKeyed keyPair receiverPublicKey nonce
. PlainText
. LazyByteString.toStrict
. runPut
. put
decrypt :: KeyPair -> DhtPacket -> Maybe PlainText
decrypt = (Keyed.runNullKeyed .) . decryptKeyed
decryptKeyed :: Keyed m => KeyPair -> DhtPacket -> m (Maybe PlainText)
decryptKeyed (KeyPair receiverSecretKey _) DhtPacket { senderPublicKey, encryptionNonce, encryptedPayload } =
(\combinedKey -> Box.decrypt combinedKey encryptionNonce encryptedPayload) <$>
Keyed.getCombinedKey receiverSecretKey senderPublicKey
decode :: Binary payload => KeyPair -> DhtPacket -> Maybe payload
decode = (Keyed.runNullKeyed .) . decodeKeyed
decodeKeyed :: (Binary payload, Keyed m) => KeyPair -> DhtPacket -> m (Maybe payload)
decodeKeyed keyPair packet = (>>= Box.decode) <$> decryptKeyed keyPair packet
instance Arbitrary DhtPacket where
arbitrary =
DhtPacket <$> arbitrary <*> arbitrary <*> arbitrary
\end{code}