{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE DeriveDataTypeable #-}
-- |
-- Module      : Network.TLS.Struct
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- the Struct module contains all definitions and values of the TLS protocol
--
module Network.TLS.Struct
        ( Bytes
        , Version(..)
        , ConnectionEnd(..)
        , CipherType(..)
        , CipherData(..)
        , ExtensionID
        , ExtensionRaw
        , CertificateType(..)
        , HashAlgorithm(..)
        , SignatureAlgorithm(..)
        , HashAndSignatureAlgorithm
        , ProtocolType(..)
        , TLSError(..)
        , DistinguishedName
        , ServerDHParams(..)
        , ServerRSAParams(..)
        , ServerKeyXchgAlgorithmData(..)
        , Packet(..)
        , Header(..)
        , ServerRandom(..)
        , ClientRandom(..)
        , serverRandom
        , clientRandom
        , FinishedData
        , SessionID
        , Session(..)
        , SessionData(..)
        , CertVerifyData(..)
        , AlertLevel(..)
        , AlertDescription(..)
        , HandshakeType(..)
        , Handshake(..)
        , numericalVer
        , verOfNum
        , TypeValuable, valOfType, valToType
        , packetType
        , typeOfHandshake
        ) where

import Data.ByteString (ByteString)
import qualified Data.ByteString as B (length)
import Data.Word
import Data.Certificate.X509 (X509)
import Data.Certificate.X509.Cert (DistinguishedName)
import Data.Typeable
import Control.Monad.Error (Error(..))
import Control.Exception (Exception(..))
import Network.TLS.Types

type Bytes = ByteString


data ConnectionEnd = ConnectionServer | ConnectionClient
data CipherType = CipherStream | CipherBlock | CipherAEAD

data CipherData = CipherData
        { cipherDataContent :: Bytes
        , cipherDataMAC     :: Maybe Bytes
        , cipherDataPadding :: Maybe Bytes
        } deriving (Show,Eq)

data CertificateType =
          CertificateType_RSA_Sign         -- TLS10
        | CertificateType_DSS_Sign         -- TLS10
        | CertificateType_RSA_Fixed_DH     -- TLS10
        | CertificateType_DSS_Fixed_DH     -- TLS10
        | CertificateType_RSA_Ephemeral_DH -- TLS12
        | CertificateType_DSS_Ephemeral_DH -- TLS12
        | CertificateType_fortezza_dms     -- TLS12
        | CertificateType_Unknown Word8
        deriving (Show,Eq)

data HashAlgorithm =
          HashNone
        | HashMD5
        | HashSHA1
        | HashSHA224
        | HashSHA256
        | HashSHA384
        | HashSHA512
        | HashOther Word8
        deriving (Show,Eq)

data SignatureAlgorithm =
          SignatureAnonymous
        | SignatureRSA
        | SignatureDSS
        | SignatureECDSA
        | SignatureOther Word8
        deriving (Show,Eq)

type HashAndSignatureAlgorithm = (HashAlgorithm, SignatureAlgorithm)

data ProtocolType =
          ProtocolType_ChangeCipherSpec
        | ProtocolType_Alert
        | ProtocolType_Handshake
        | ProtocolType_AppData
        | ProtocolType_DeprecatedHandshake
        deriving (Eq, Show)

-- | TLSError that might be returned through the TLS stack
data TLSError =
          Error_Misc String        -- ^ mainly for instance of Error
        | Error_Protocol (String, Bool, AlertDescription)
        | Error_Certificate String
        | Error_HandshakePolicy String -- ^ handshake policy failed.
        | Error_EOF
        | Error_Packet String
        | Error_Packet_unexpected String String
        | Error_Packet_Parsing String
        deriving (Eq, Show, Typeable)

instance Error TLSError where
        noMsg  = Error_Misc ""
        strMsg = Error_Misc

instance Exception TLSError

data Packet =
          Handshake [Handshake]
        | Alert [(AlertLevel, AlertDescription)]
        | ChangeCipherSpec
        | AppData ByteString
        deriving (Show,Eq)

data Header = Header ProtocolType Version Word16 deriving (Show,Eq)

newtype ServerRandom = ServerRandom Bytes deriving (Show, Eq)
newtype ClientRandom = ClientRandom Bytes deriving (Show, Eq)
newtype Session = Session (Maybe SessionID) deriving (Show, Eq)

type FinishedData = Bytes
type ExtensionID  = Word16
type ExtensionRaw = (ExtensionID, Bytes)

newtype CertVerifyData = CertVerifyData Bytes
  deriving (Show, Eq)

constrRandom32 :: (Bytes -> a) -> Bytes -> Maybe a
constrRandom32 constr l = if B.length l == 32 then Just (constr l) else Nothing

serverRandom :: Bytes -> Maybe ServerRandom
serverRandom l = constrRandom32 ServerRandom l

clientRandom :: Bytes -> Maybe ClientRandom
clientRandom l = constrRandom32 ClientRandom l

data AlertLevel =
          AlertLevel_Warning
        | AlertLevel_Fatal
        deriving (Show,Eq)

data AlertDescription =
          CloseNotify
        | UnexpectedMessage
        | BadRecordMac
        | DecryptionFailed       -- ^ deprecated alert, should never be sent by compliant implementation
        | RecordOverflow
        | DecompressionFailure
        | HandshakeFailure
        | BadCertificate
        | UnsupportedCertificate
        | CertificateRevoked
        | CertificateExpired
        | CertificateUnknown
        | IllegalParameter
        | UnknownCa
        | AccessDenied
        | DecodeError
        | DecryptError
        | ExportRestriction
        | ProtocolVersion
        | InsufficientSecurity
        | InternalError
        | UserCanceled
        | NoRenegotiation
        | UnsupportedExtension
        | CertificateUnobtainable
        | UnrecognizedName
        | BadCertificateStatusResponse
        | BadCertificateHashValue
        deriving (Show,Eq)

data HandshakeType =
          HandshakeType_HelloRequest
        | HandshakeType_ClientHello
        | HandshakeType_ServerHello
        | HandshakeType_Certificate
        | HandshakeType_ServerKeyXchg
        | HandshakeType_CertRequest
        | HandshakeType_ServerHelloDone
        | HandshakeType_CertVerify
        | HandshakeType_ClientKeyXchg
        | HandshakeType_Finished
        | HandshakeType_NPN -- Next Protocol Negotiation extension
        deriving (Show,Eq)

data ServerDHParams = ServerDHParams
        { dh_p  :: Integer -- ^ prime modulus
        , dh_g  :: Integer -- ^ generator
        , dh_Ys :: Integer -- ^ public value (g^X mod p)
        } deriving (Show,Eq)

data ServerRSAParams = ServerRSAParams
        { rsa_modulus  :: Integer
        , rsa_exponent :: Integer
        } deriving (Show,Eq)

data ServerKeyXchgAlgorithmData =
          SKX_DH_Anon ServerDHParams
        | SKX_DHE_DSS ServerDHParams [Word8]
        | SKX_DHE_RSA ServerDHParams [Word8]
        | SKX_RSA (Maybe ServerRSAParams)
        | SKX_DH_DSS (Maybe ServerRSAParams)
        | SKX_DH_RSA (Maybe ServerRSAParams)
        | SKX_Unknown Bytes
        deriving (Show,Eq)

type DeprecatedRecord = ByteString

data Handshake =
          ClientHello !Version !ClientRandom !Session ![CipherID] ![CompressionID] [ExtensionRaw] (Maybe DeprecatedRecord)
        | ServerHello !Version !ServerRandom !Session !CipherID !CompressionID [ExtensionRaw]
        | Certificates [X509]
        | HelloRequest
        | ServerHelloDone
        | ClientKeyXchg Bytes
        | ServerKeyXchg ServerKeyXchgAlgorithmData
        | CertRequest [CertificateType] (Maybe [ HashAndSignatureAlgorithm ]) [DistinguishedName]
        | CertVerify (Maybe HashAndSignatureAlgorithm) CertVerifyData
        | Finished FinishedData
        | HsNextProtocolNegotiation Bytes -- NPN extension
        deriving (Show,Eq)

packetType :: Packet -> ProtocolType
packetType (Handshake _)    = ProtocolType_Handshake
packetType (Alert _)        = ProtocolType_Alert
packetType ChangeCipherSpec = ProtocolType_ChangeCipherSpec
packetType (AppData _)      = ProtocolType_AppData

typeOfHandshake :: Handshake -> HandshakeType
typeOfHandshake (ClientHello {})             = HandshakeType_ClientHello
typeOfHandshake (ServerHello {})             = HandshakeType_ServerHello
typeOfHandshake (Certificates {})            = HandshakeType_Certificate
typeOfHandshake HelloRequest                 = HandshakeType_HelloRequest
typeOfHandshake (ServerHelloDone)            = HandshakeType_ServerHelloDone
typeOfHandshake (ClientKeyXchg {})           = HandshakeType_ClientKeyXchg
typeOfHandshake (ServerKeyXchg {})           = HandshakeType_ServerKeyXchg
typeOfHandshake (CertRequest {})             = HandshakeType_CertRequest
typeOfHandshake (CertVerify {})              = HandshakeType_CertVerify
typeOfHandshake (Finished {})                = HandshakeType_Finished
typeOfHandshake (HsNextProtocolNegotiation {}) = HandshakeType_NPN

numericalVer :: Version -> (Word8, Word8)
numericalVer SSL2  = (2, 0)
numericalVer SSL3  = (3, 0)
numericalVer TLS10 = (3, 1)
numericalVer TLS11 = (3, 2)
numericalVer TLS12 = (3, 3)

verOfNum :: (Word8, Word8) -> Maybe Version
verOfNum (2, 0) = Just SSL2
verOfNum (3, 0) = Just SSL3
verOfNum (3, 1) = Just TLS10
verOfNum (3, 2) = Just TLS11
verOfNum (3, 3) = Just TLS12
verOfNum _      = Nothing

class TypeValuable a where
        valOfType :: a -> Word8
        valToType :: Word8 -> Maybe a

instance TypeValuable ConnectionEnd where
        valOfType ConnectionServer = 0
        valOfType ConnectionClient = 1

        valToType 0 = Just ConnectionServer
        valToType 1 = Just ConnectionClient
        valToType _ = Nothing

instance TypeValuable CipherType where
        valOfType CipherStream = 0
        valOfType CipherBlock  = 1
        valOfType CipherAEAD   = 2

        valToType 0 = Just CipherStream
        valToType 1 = Just CipherBlock
        valToType 2 = Just CipherAEAD
        valToType _ = Nothing

instance TypeValuable ProtocolType where
        valOfType ProtocolType_ChangeCipherSpec    = 20
        valOfType ProtocolType_Alert               = 21
        valOfType ProtocolType_Handshake           = 22
        valOfType ProtocolType_AppData             = 23
        valOfType ProtocolType_DeprecatedHandshake = 128 -- unused

        valToType 20 = Just ProtocolType_ChangeCipherSpec
        valToType 21 = Just ProtocolType_Alert
        valToType 22 = Just ProtocolType_Handshake
        valToType 23 = Just ProtocolType_AppData
        valToType _  = Nothing

instance TypeValuable HandshakeType where
        valOfType HandshakeType_HelloRequest    = 0
        valOfType HandshakeType_ClientHello     = 1
        valOfType HandshakeType_ServerHello     = 2
        valOfType HandshakeType_Certificate     = 11
        valOfType HandshakeType_ServerKeyXchg   = 12
        valOfType HandshakeType_CertRequest     = 13
        valOfType HandshakeType_ServerHelloDone = 14
        valOfType HandshakeType_CertVerify      = 15
        valOfType HandshakeType_ClientKeyXchg   = 16
        valOfType HandshakeType_Finished        = 20
        valOfType HandshakeType_NPN             = 67

        valToType 0  = Just HandshakeType_HelloRequest
        valToType 1  = Just HandshakeType_ClientHello
        valToType 2  = Just HandshakeType_ServerHello
        valToType 11 = Just HandshakeType_Certificate
        valToType 12 = Just HandshakeType_ServerKeyXchg
        valToType 13 = Just HandshakeType_CertRequest
        valToType 14 = Just HandshakeType_ServerHelloDone
        valToType 15 = Just HandshakeType_CertVerify
        valToType 16 = Just HandshakeType_ClientKeyXchg
        valToType 20 = Just HandshakeType_Finished
        valToType 67 = Just HandshakeType_NPN
        valToType _  = Nothing

instance TypeValuable AlertLevel where
        valOfType AlertLevel_Warning = 1
        valOfType AlertLevel_Fatal   = 2

        valToType 1 = Just AlertLevel_Warning
        valToType 2 = Just AlertLevel_Fatal
        valToType _ = Nothing

instance TypeValuable AlertDescription where
        valOfType CloseNotify            = 0
        valOfType UnexpectedMessage      = 10
        valOfType BadRecordMac           = 20
        valOfType DecryptionFailed       = 21
        valOfType RecordOverflow         = 22
        valOfType DecompressionFailure   = 30
        valOfType HandshakeFailure       = 40
        valOfType BadCertificate         = 42
        valOfType UnsupportedCertificate = 43
        valOfType CertificateRevoked     = 44
        valOfType CertificateExpired     = 45
        valOfType CertificateUnknown     = 46
        valOfType IllegalParameter       = 47
        valOfType UnknownCa              = 48
        valOfType AccessDenied           = 49
        valOfType DecodeError            = 50
        valOfType DecryptError           = 51
        valOfType ExportRestriction      = 60
        valOfType ProtocolVersion        = 70
        valOfType InsufficientSecurity   = 71
        valOfType InternalError          = 80
        valOfType UserCanceled           = 90
        valOfType NoRenegotiation        = 100
        valOfType UnsupportedExtension   = 110
        valOfType CertificateUnobtainable = 111
        valOfType UnrecognizedName        = 112
        valOfType BadCertificateStatusResponse = 113
        valOfType BadCertificateHashValue = 114

        valToType 0   = Just CloseNotify
        valToType 10  = Just UnexpectedMessage
        valToType 20  = Just BadRecordMac
        valToType 21  = Just DecryptionFailed
        valToType 22  = Just RecordOverflow
        valToType 30  = Just DecompressionFailure
        valToType 40  = Just HandshakeFailure
        valToType 42  = Just BadCertificate
        valToType 43  = Just UnsupportedCertificate
        valToType 44  = Just CertificateRevoked
        valToType 45  = Just CertificateExpired
        valToType 46  = Just CertificateUnknown
        valToType 47  = Just IllegalParameter
        valToType 48  = Just UnknownCa
        valToType 49  = Just AccessDenied
        valToType 50  = Just DecodeError
        valToType 51  = Just DecryptError
        valToType 60  = Just ExportRestriction
        valToType 70  = Just ProtocolVersion
        valToType 71  = Just InsufficientSecurity
        valToType 80  = Just InternalError
        valToType 90  = Just UserCanceled
        valToType 100 = Just NoRenegotiation
        valToType 110 = Just UnsupportedExtension
        valToType 111 = Just CertificateUnobtainable
        valToType 112 = Just UnrecognizedName
        valToType 113 = Just BadCertificateStatusResponse
        valToType 114 = Just BadCertificateHashValue
        valToType _   = Nothing

instance TypeValuable CertificateType where
        valOfType CertificateType_RSA_Sign         = 1
        valOfType CertificateType_DSS_Sign         = 2
        valOfType CertificateType_RSA_Fixed_DH     = 3
        valOfType CertificateType_DSS_Fixed_DH     = 4
        valOfType CertificateType_RSA_Ephemeral_DH = 5
        valOfType CertificateType_DSS_Ephemeral_DH = 6
        valOfType CertificateType_fortezza_dms     = 20
        valOfType (CertificateType_Unknown i)      = i

        valToType 1  = Just CertificateType_RSA_Sign
        valToType 2  = Just CertificateType_DSS_Sign
        valToType 3  = Just CertificateType_RSA_Fixed_DH
        valToType 4  = Just CertificateType_DSS_Fixed_DH
        valToType 5  = Just CertificateType_RSA_Ephemeral_DH
        valToType 6  = Just CertificateType_DSS_Ephemeral_DH
        valToType 20 = Just CertificateType_fortezza_dms
        valToType i  = Just (CertificateType_Unknown i)

instance TypeValuable HashAlgorithm where
        valOfType HashNone      = 0
        valOfType HashMD5       = 1
        valOfType HashSHA1      = 2
        valOfType HashSHA224    = 3
        valOfType HashSHA256    = 4
        valOfType HashSHA384    = 5
        valOfType HashSHA512    = 6
        valOfType (HashOther i) = i

        valToType 0 = Just HashNone
        valToType 1 = Just HashMD5
        valToType 2 = Just HashSHA1
        valToType 3 = Just HashSHA224
        valToType 4 = Just HashSHA256
        valToType 5 = Just HashSHA384
        valToType 6 = Just HashSHA512
        valToType i = Just (HashOther i)

instance TypeValuable SignatureAlgorithm where
        valOfType SignatureAnonymous = 0
        valOfType SignatureRSA       = 1
        valOfType SignatureDSS       = 2
        valOfType SignatureECDSA     = 3
        valOfType (SignatureOther i) = i

        valToType 0 = Just SignatureAnonymous
        valToType 1 = Just SignatureRSA
        valToType 2 = Just SignatureDSS
        valToType 3 = Just SignatureECDSA
        valToType i = Just (SignatureOther i)
