hstox-0.0.1: src/tox/Network/Tox/DHT/DhtPacket.lhs
\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 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, 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 qualified Network.Tox.Crypto.CombinedKey as CombinedKey
import Network.Tox.Crypto.Key (Nonce, PublicKey)
import Network.Tox.Crypto.KeyPair (KeyPair (..))
import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
{-------------------------------------------------------------------------------
-
- :: Implementation.
-
------------------------------------------------------------------------------}
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 (KeyPair senderSecretKey senderPublicKey') receiverPublicKey nonce plainText =
DhtPacket senderPublicKey' nonce $ Box.encrypt combinedKey nonce plainText
where combinedKey = CombinedKey.precompute senderSecretKey receiverPublicKey
encode :: Binary payload => KeyPair -> PublicKey -> Nonce -> payload -> DhtPacket
encode keyPair receiverPublicKey nonce =
encrypt keyPair receiverPublicKey nonce
. PlainText
. LazyByteString.toStrict
. runPut
. put
decrypt :: KeyPair -> DhtPacket -> Maybe PlainText
decrypt (KeyPair receiverSecretKey _) DhtPacket { senderPublicKey, encryptionNonce, encryptedPayload } =
Box.decrypt combinedKey encryptionNonce encryptedPayload
where combinedKey = CombinedKey.precompute receiverSecretKey senderPublicKey
decode :: Binary payload => KeyPair -> DhtPacket -> Maybe payload
decode keyPair packet = decrypt keyPair packet >>= Box.decode
{-------------------------------------------------------------------------------
-
- :: Tests.
-
------------------------------------------------------------------------------}
instance Arbitrary DhtPacket where
arbitrary =
DhtPacket <$> arbitrary <*> arbitrary <*> arbitrary
\end{code}