packages feed

eap 0.1.0.0 → 0.6.0.0

raw patch · 5 files changed

+328/−33 lines, 5 filesdep +cryptonitedep +memorydep +mtlPVP ok

version bump matches the API change (PVP)

Dependencies added: cryptonite, memory, mtl, pretty-hex

API changes (from Hackage documentation)

- Network.EAP.Encoding: getMessage :: Word16 -> Get (Maybe Message)
- Network.EAP.Encoding: instance Data.Binary.Class.Binary Network.EAP.Types.Message
+ Network.EAP.Authentication: authenticateMSCHAPv2 :: MSCHAPv2Data -> ByteString -> ByteString -> Except CryptoError Bool
+ Network.EAP.Authentication: generateNTResponse :: ByteString -> ByteString -> ByteString -> ByteString -> Except CryptoError ByteString
+ Network.EAP.Encoding: instance Data.Binary.Class.Binary Network.EAP.Types.MSCHAPv2Data
+ Network.EAP.Encoding: instance Data.Binary.Class.Binary Network.EAP.Types.MSCHAPv2ResponseDataField
+ Network.EAP.Types: GenericTokenCardAuth :: AuthType
+ Network.EAP.Types: MD5ChallengeAuth :: AuthType
+ Network.EAP.Types: MSCHAPv2Auth :: AuthType
+ Network.EAP.Types: MSCHAPv2Challenge :: MSCHAPv2OpCode
+ Network.EAP.Types: MSCHAPv2ChallengeData :: ByteString -> ByteString -> MSCHAPv2Data
+ Network.EAP.Types: MSCHAPv2ChangePassword :: MSCHAPv2OpCode
+ Network.EAP.Types: MSCHAPv2ChangePasswordData :: ByteString -> ByteString -> ByteString -> ByteString -> MSCHAPv2Data
+ Network.EAP.Types: MSCHAPv2Failure :: MSCHAPv2OpCode
+ Network.EAP.Types: MSCHAPv2FailureRequestData :: ByteString -> MSCHAPv2Data
+ Network.EAP.Types: MSCHAPv2Message :: MSCHAPv2OpCode -> Word8 -> Word16 -> MSCHAPv2Data -> Message
+ Network.EAP.Types: MSCHAPv2NoData :: MSCHAPv2Data
+ Network.EAP.Types: MSCHAPv2Response :: MSCHAPv2OpCode
+ Network.EAP.Types: MSCHAPv2ResponseData :: MSCHAPv2ResponseDataField -> ByteString -> MSCHAPv2Data
+ Network.EAP.Types: MSCHAPv2ResponseDataField :: ByteString -> ByteString -> MSCHAPv2ResponseDataField
+ Network.EAP.Types: MSCHAPv2Success :: MSCHAPv2OpCode
+ Network.EAP.Types: MSCHAPv2SuccessRequestData :: ByteString -> MSCHAPv2Data
+ Network.EAP.Types: OTPAuth :: AuthType
+ Network.EAP.Types: RSAPubKeyAuth :: AuthType
+ Network.EAP.Types: TLSAuth :: AuthType
+ Network.EAP.Types: [getMSCHAPv2ChallengeName] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2Challenge] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2Data] :: Message -> MSCHAPv2Data
+ Network.EAP.Types: [getMSCHAPv2EncryptedHash] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2EncryptedPassword] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2FailureRequestMessage] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2Id] :: Message -> Word8
+ Network.EAP.Types: [getMSCHAPv2Length] :: Message -> Word16
+ Network.EAP.Types: [getMSCHAPv2NTResponse] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2OpCode] :: Message -> MSCHAPv2OpCode
+ Network.EAP.Types: [getMSCHAPv2PeerChallenge] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2ResponseData] :: MSCHAPv2Data -> MSCHAPv2ResponseDataField
+ Network.EAP.Types: [getMSCHAPv2ResponseNTResponse] :: MSCHAPv2ResponseDataField -> ByteString
+ Network.EAP.Types: [getMSCHAPv2ResponseName] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: [getMSCHAPv2ResponsePeerChallenge] :: MSCHAPv2ResponseDataField -> ByteString
+ Network.EAP.Types: [getMSCHAPv2SuccessRequestMessage] :: MSCHAPv2Data -> ByteString
+ Network.EAP.Types: data AuthType
+ Network.EAP.Types: data MSCHAPv2Data
+ Network.EAP.Types: data MSCHAPv2OpCode
+ Network.EAP.Types: data MSCHAPv2ResponseDataField
+ Network.EAP.Types: instance Data.Data.Data Network.EAP.Types.AuthType
+ Network.EAP.Types: instance Data.Data.Data Network.EAP.Types.MSCHAPv2Data
+ Network.EAP.Types: instance Data.Data.Data Network.EAP.Types.MSCHAPv2OpCode
+ Network.EAP.Types: instance Data.Data.Data Network.EAP.Types.MSCHAPv2ResponseDataField
+ Network.EAP.Types: instance GHC.Classes.Eq Network.EAP.Types.AuthType
+ Network.EAP.Types: instance GHC.Classes.Eq Network.EAP.Types.MSCHAPv2Data
+ Network.EAP.Types: instance GHC.Classes.Eq Network.EAP.Types.MSCHAPv2OpCode
+ Network.EAP.Types: instance GHC.Classes.Eq Network.EAP.Types.MSCHAPv2ResponseDataField
+ Network.EAP.Types: instance GHC.Enum.Enum Network.EAP.Types.AuthType
+ Network.EAP.Types: instance GHC.Enum.Enum Network.EAP.Types.MSCHAPv2OpCode
+ Network.EAP.Types: instance GHC.Show.Show Network.EAP.Types.AuthType
+ Network.EAP.Types: instance GHC.Show.Show Network.EAP.Types.MSCHAPv2Data
+ Network.EAP.Types: instance GHC.Show.Show Network.EAP.Types.MSCHAPv2OpCode
+ Network.EAP.Types: instance GHC.Show.Show Network.EAP.Types.MSCHAPv2ResponseDataField

Files

README.md view
@@ -1,1 +1,2 @@-# eap+# Extensible Authentication Protocol - EAP+This module provides types and definitions as well as on the wire encoding and decoding of EAP packets and messages as per RFC 3748
eap.cabal view
@@ -1,5 +1,5 @@ name:                eap-version:             0.1.0.0+version:             0.6.0.0 synopsis:            Extensible Authentication Protocol (EAP) description:         This module provides types and on the wire de/coding of EAP packets as per RFC 3748 homepage:            https://github.com/erickg/eap#readme@@ -15,11 +15,16 @@  library   hs-source-dirs:      src-  exposed-modules:     Network.EAP.Encoding+  exposed-modules:     Network.EAP.Authentication+                     , Network.EAP.Encoding                      , Network.EAP.Types   build-depends:       base >= 4.7 && < 5                      , binary                      , bytestring+                     , cryptonite+                     , memory+                     , mtl+                     , pretty-hex   default-language:    Haskell2010   ghc-options:         -Wall 
+ src/Network/EAP/Authentication.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE NamedFieldPuns, RecordWildCards, OverloadedStrings, ScopedTypeVariables #-}+{-|+Module      : Network.EAP.Authentication+Description : Collection of EAP authentication methods and related utility functions+Copyright   : (c) Erick Gonzalez, 2017+License     : BSD3+Maintainer  : erick@codemonkeylabs.de+Stability   : experimental+Portability : POSIX++This module provides functions implements EAP authentication schemes. New authentication types+will be added as needed and contributions are very welcome.++-}+module Network.EAP.Authentication (authenticateMSCHAPv2,+                                   generateNTResponse) where++import Prelude hiding (concatMap)+import Data.Bits                 ((.|.), (.&.), complement, shiftL, shiftR, xor)+import Data.ByteString.Lazy      (ByteString, cons, concatMap, toStrict)+import Data.ByteArray            (convert)+import Control.Monad.Except      (ExceptT(..), Except, throwError)+import Control.Monad.State.Lazy  (execState, modify)+import Crypto.Cipher.DES         (DES)+import Crypto.Cipher.Types       (cipherInit, ecbEncrypt)+import Crypto.Hash.Algorithms    (MD4, SHA1(..))+import Crypto.Hash               (Digest, hashFinalize, hashInitWith, hashUpdate, hashlazy)+import Crypto.Error              (CryptoError, CryptoFailable(..))+import Network.EAP.Types+import qualified Data.ByteString     as SB++-- | Authenticate the MSCHAPv2 response data to a given challenge request, using the supplied+-- cleartext password.+authenticateMSCHAPv2+    :: MSCHAPv2Data -- ^ Decoded data from the MSCHAPv2 response+     -> ByteString   -- ^ Authenticator challenge sent to the peer on a previous request+     -> ByteString   -- ^ Authenticating user password+     -> Except CryptoError Bool -- ^ Returns either an error from one of the encryption+                               -- routines or a boolean indicating whether the user+                               -- response matches the expected value+authenticateMSCHAPv2+  MSCHAPv2ResponseData{ getMSCHAPv2ResponseData = MSCHAPv2ResponseDataField{..}, .. }+  challenge+  password = do+  let peerChallenge = getMSCHAPv2ResponsePeerChallenge+      username      = getMSCHAPv2ResponseName+  r <- generateNTResponse challenge peerChallenge username password+  return $ r == toStrict getMSCHAPv2ResponseNTResponse++authenticateMSCHAPv2 msCHAPv2Data _ _ =+  error $ "Invalid authentication attempt of " ++ show msCHAPv2Data++-- | Calculate the NT Response as per [RFC2759], Section 8.1+generateNTResponse :: ByteString -- ^ Authenticator challenge sent to the peer on a previous+                                 -- request+                   ->  ByteString -- ^ Challenge sent back by authenticating peer+                   ->  ByteString -- ^ MSCHAP username+                   ->  ByteString -- ^ Cleartext user password+                   ->  Except CryptoError SB.ByteString -- ^ Returns either an error from one of+                                                       -- the encryption routines or the+                                                       -- calculated NT response+generateNTResponse authenticatorChallenge peerChallenge username password = do+    let challenge        = challengeHash+        passwordHash     = ntPasswordHash password+        zPasswordHash    = passwordHash `SB.append` SB.replicate 5 0 -- pad to 21 octets+        (pHash0, rest)   = SB.splitAt 7 zPasswordHash+        (pHash1, pHash2) = SB.splitAt 7 rest+    r0 <- encryptDES pHash0 challenge+    r1 <- encryptDES pHash1 challenge+    r2 <- encryptDES pHash2 challenge+    return $ r0 `SB.append` r1 `SB.append` r2+        where challengeHash  = SB.take 8 . convert . hashFinalize . flip execState ctx0 $ do+                                 hash peerChallenge+                                 hash authenticatorChallenge+                                 hash username+              hash           = modify . flip hashUpdate . toStrict+              ctx0           = hashInitWith SHA1+              ntPasswordHash = convert . (hashlazy :: ByteString -> Digest MD4) . concatMap with0s+              with0s         = flip cons "\NUL"++-- | Used internally to encrypt a message using a DES cipher in ECB mode+encryptDES :: SB.ByteString -> SB.ByteString -> Except CryptoError SB.ByteString+encryptDES key msg = do+  (cipher :: DES) <- ExceptT . return $ initCipher+  ExceptT . return . Right $ ecbEncrypt cipher msg+    where initCipher = case cipherInit $ addParity key of+                         CryptoFailed e -> throwError e+                         CryptoPassed c -> Right c++-- | Used internally to add the parity bits to a 56 bit (7 octet key) thus becoming an 8+-- octet key+addParity :: SB.ByteString -> SB.ByteString+addParity = expand . SB.foldl f ((0, 0), SB.empty)+    where f ((i, carry), acc) word =+              let v      = carry  .|. (word `shiftR` i)+                  carry' = word `shiftL` (7 - i)+                  v'     = v .&. 0xfe+                  v''    = v' .|. (complement $ parity v') .&. 1+                  acc'   = acc `SB.snoc` v''+              in ((i+1, carry'), acc')+          expand ((_, carry), str) = str `SB.snoc` carry+          parity x0 = foldl (\x i -> x `xor` (x `shiftR` i)) x0 [1, 2, 4, 8, 16] .&. 1
src/Network/EAP/Encoding.hs view
@@ -9,20 +9,22 @@ Stability   : experimental Portability : POSIX -This basically provides Binary instances for the EAP Packet type and the embedded messages it-encapsulates. So you basically decode a (lazy) bytestring and get an EAP Packet back or you can+This module provides Binary instances for the EAP Packet type and the embedded messages it+encapsulates. So you decode a (lazy) bytestring and get an EAP Packet back or you can encode an EAP packet to ByteString you can send on the wire as is. Simple as that.  -}-module Network.EAP.Encoding where+module Network.EAP.Encoding () where -import Data.Binary                     (Binary(..), encode, decode)+import Control.Monad                   (when)+import Data.Binary                     (Binary(..), encode) import Data.Binary.Get                 (Get,                                         getRemainingLazyByteString,                                         getLazyByteString,                                         getWord8,-                                        getWord16be)-import Data.Binary.Put                 (putLazyByteString, putWord8, putWord16be)+                                        getWord16be,+                                        runGet)+import Data.Binary.Put                 (Put, putLazyByteString, putWord8, putWord16be, runPut) import Data.Word                       (Word16) import Network.EAP.Types @@ -30,7 +32,7 @@  instance Binary Packet where     put Packet{..} = do-      let packetData   = encode getPacketMessage+      let packetData   = runPut $ putMessage getPacketType getPacketMessage           packetLength = fromIntegral $ 4 + LB.length packetData -- EAP header length is 4 bytes       put getPacketType       putWord8 getPacketId@@ -42,43 +44,159 @@       packetId     <- getWord8       packetLength <- getWord16be       let dataLength = packetLength - 4-      packetMessage <- getMessage $ fromIntegral dataLength+      packetMessage <- getMessage packetType $ fromIntegral dataLength       return $ Packet packetType packetId packetMessage  -- | Given the length of data to decode, decode an EAP message in the Get Monad. Used internally -- so you probably don't need to use this.-getMessage :: Word16 -> Get (Maybe Message)-getMessage 0 = return Nothing-getMessage dataLength = do+getMessage :: PacketType -> Word16 -> Get (Maybe Message)+getMessage _ 0 = return Nothing+getMessage packetType dataLength = do   packetData <- getLazyByteString $ fromIntegral dataLength-  return . Just . decode $ packetData+  return . Just $ runGet (decodeMessage packetType) packetData -instance Binary Message where-    put (IdentityMessage str)     = putLazyByteString str-    put (NotificationMessage str) = putLazyByteString str-    put (NakMessage value)        = putWord8 value-    put MD5ChallengeMessage{..}   = do-      putWord8 . fromIntegral . LB.length $ getMD5ChallengeValue-      putLazyByteString getMD5ChallengeValue-      putLazyByteString getMD5ChallengeName-    put (OTPMessage str)              = putLazyByteString str-    put (GenericTokenCardMessage str) = putLazyByteString str+-- | Given Maybe an EAP message to encode, do so in the Put Monad. Used internally. This is+-- necessary cause if one was to put the Maybe value directly on the wire, it would include the+-- coding for the Maybe type itself, as opposed to empty or *just* the data which is what we want+putMessage :: PacketType -> Maybe Message -> Put+putMessage _ Nothing             = return ()+putMessage packetType (Just msg) = encodeMessage packetType msg -    get = do+encodeMessage :: PacketType -> Message -> Put+encodeMessage _ (IdentityMessage str)     = putWord8 1 >> putLazyByteString str+encodeMessage _ (NotificationMessage str) = putWord8 2 >> putLazyByteString str+encodeMessage _ (NakMessage value)        = putWord8 3 >> putWord8 value+encodeMessage _ MD5ChallengeMessage{..}   = do+  putWord8 4 -- MD5 Challenge+  putWord8 . fromIntegral . LB.length $ getMD5ChallengeValue+  putLazyByteString getMD5ChallengeValue+  putLazyByteString getMD5ChallengeName+encodeMessage _ (OTPMessage str)                  = putWord8 5 >> putLazyByteString str+encodeMessage _ (GenericTokenCardMessage str)     = putWord8 6 >> putLazyByteString str+encodeMessage packetType (MSCHAPv2Message op iD _len msgData)+  | op == MSCHAPv2Success || op == MSCHAPv2Failure, packetType == ResponsePacket = do+    putWord8 26 -- MSCHAPv2+    putWord8 . fromIntegral . fromEnum $ op+  | otherwise = do+    putWord8 26 -- MSCHAPv2+    putWord8 . fromIntegral . fromEnum $ op+    putWord8 iD+    let bytes  = encode msgData+        msgLen = fromIntegral $ 4 + LB.length bytes+    putWord16be msgLen+    putLazyByteString bytes++decodeMessage :: PacketType -> Get Message+decodeMessage packetType = do       messageType <- getWord8       getMessage' messageType-        where getMessage' 1 = getRemainingLazyByteString >>= return . IdentityMessage-              getMessage' 2 = getRemainingLazyByteString >>= return . NotificationMessage-              getMessage' 3 = getWord8 >>= return . NakMessage-              getMessage' 4 = do+        where getMessage' 1  = getRemainingLazyByteString >>= return . IdentityMessage+              getMessage' 2  = getRemainingLazyByteString >>= return . NotificationMessage+              getMessage' 3  = getWord8 >>= return . NakMessage+              getMessage' 4  = do                 valueSize <- getWord8                 value     <- getLazyByteString $ fromIntegral valueSize                 name      <- getRemainingLazyByteString                 return $ MD5ChallengeMessage value name-              getMessage' 5 = getRemainingLazyByteString >>= return . OTPMessage-              getMessage' 6 = getRemainingLazyByteString >>= return . GenericTokenCardMessage+              getMessage' 5  = getRemainingLazyByteString >>= return . OTPMessage+              getMessage' 6  = getRemainingLazyByteString >>= return . GenericTokenCardMessage+              getMessage' 26 = do+                op  <- getWord8 >>= return . toEnum . fromIntegral+                if packetType == ResponsePacket && (op == MSCHAPv2Success || op == MSCHAPv2Failure)+                   then return $ MSCHAPv2Message op 0 0 MSCHAPv2NoData+                   else do+                     iD  <- getWord8+                     len <- getWord16be+                     let dataLen = fromIntegral $ len - 4+                     when (dataLen < 0) $ error $ "Invalid MSCHAPv2 data length: " ++ show dataLen+                     bytes <- getLazyByteString dataLen+                     return $ MSCHAPv2Message op iD len $ runGet (decodeData op) bytes               getMessage' n = fail $ "Invalid EAP Message Type " ++ show n  instance Binary PacketType where     put = putWord8 . fromIntegral . fromEnum     get = getWord8 >>= return . toEnum . fromIntegral++instance Binary MSCHAPv2Data where+    put MSCHAPv2ChallengeData{..}+      | msCHAPv2ChallengeLen == 16 = do+        putWord8 . fromIntegral $ msCHAPv2ChallengeLen+        putLazyByteString getMSCHAPv2Challenge+        putLazyByteString getMSCHAPv2ChallengeName+      | otherwise = error $ "Illegal MSCHAPv2 challenge length " ++ (show msCHAPv2ChallengeLen)+      where msCHAPv2ChallengeLen = LB.length getMSCHAPv2Challenge+    put MSCHAPv2ResponseData{..} = do+      putWord8 49 -- fixed length as per RFC+      put getMSCHAPv2ResponseData+      put getMSCHAPv2ResponseName+    put MSCHAPv2SuccessRequestData{..} =+      putLazyByteString getMSCHAPv2SuccessRequestMessage+    put MSCHAPv2FailureRequestData{..} =+      putLazyByteString getMSCHAPv2FailureRequestMessage+    put MSCHAPv2ChangePasswordData{..} =+      if LB.length getMSCHAPv2EncryptedPassword /= 516 then+          error $ "Invalid MSCHAPv2 encrypted password length "+                    ++ (show $ LB.length getMSCHAPv2EncryptedPassword)+      else if LB.length getMSCHAPv2EncryptedHash /= 16 then+          error $ "Invalid MSCHAPv2 encrypted hash length "+                    ++ (show $ LB.length getMSCHAPv2EncryptedHash)+      else if LB.length getMSCHAPv2PeerChallenge /= 16 then+          error $ "Invalid MSCHAPv2 peer challenge length "+                    ++ (show $ LB.length getMSCHAPv2PeerChallenge)+      else if LB.length getMSCHAPv2NTResponse /= 24 then+          error $ "Invalid MSCHAPv2 NT Response length "+                    ++ (show $ LB.length getMSCHAPv2NTResponse)+      else do+        putLazyByteString getMSCHAPv2EncryptedPassword+        putLazyByteString getMSCHAPv2EncryptedHash+        putLazyByteString getMSCHAPv2PeerChallenge+        putLazyByteString $ LB.replicate 8 '\NUL'+        putLazyByteString getMSCHAPv2NTResponse+        putWord16be 0 -- flags unused+    put MSCHAPv2NoData = return ()+    get = error "Impossible to decode MSCHAPv2 message data for an unknown opcode. Use decodeData"++instance Binary MSCHAPv2ResponseDataField where+    put MSCHAPv2ResponseDataField{..}+      | dataLength == 49 = do+      putLazyByteString getMSCHAPv2ResponsePeerChallenge+      putLazyByteString getMSCHAPv2ResponseNTResponse+      | otherwise = error $ "Invalid MSCHAPv2 Response Data length: " ++ show dataLength+        where dataLength = LB.length getMSCHAPv2ResponsePeerChallenge ++                           LB.length getMSCHAPv2ResponseNTResponse ++                           9 -- reserved 8 bytes + flags octet+    get = error $ "Do not use get on MSCHAPv2ResponseDataField directly. Use decodeData instead"++-- | Used internally to decode MSCHAPv2Data since it is not possible to do so in the Get Monad+-- cause the data itself is meaningless without the opcode of the enclosing packet. You normally+-- wouldn't need this.+decodeData :: MSCHAPv2OpCode -> Get MSCHAPv2Data+decodeData MSCHAPv2Challenge = do+  valueLen  <- getWord8+  when (valueLen /= 16) $ error $ "Invalid MSCHAPv2 Challenge length " ++ show valueLen+  challenge <- getLazyByteString 16+  name      <- getRemainingLazyByteString+  return $ MSCHAPv2ChallengeData challenge name+decodeData MSCHAPv2Response = do+  valueLen     <- getWord8+  when (valueLen /= 49) $ error $ "Invalid MSCHAPv2 Response value length " ++ show valueLen+  challenge    <- getLazyByteString 16+  _reserved    <- getLazyByteString 8+  ntResponse   <- getLazyByteString 24+  _flagsUnused <- getWord8+  let msCHAPv2ResponseData = MSCHAPv2ResponseDataField challenge ntResponse+  name         <- getRemainingLazyByteString+  return $ MSCHAPv2ResponseData msCHAPv2ResponseData name+decodeData MSCHAPv2Success = do+  msg <- getRemainingLazyByteString+  return $ MSCHAPv2SuccessRequestData msg+decodeData MSCHAPv2Failure = do+  msg <- getRemainingLazyByteString+  return $ MSCHAPv2FailureRequestData msg+decodeData MSCHAPv2ChangePassword = do+  password   <- getLazyByteString 516+  hash       <- getLazyByteString 16+  challenge  <- getLazyByteString 16+  _reserved  <- getLazyByteString 8+  ntResponse <- getLazyByteString 24+  return $ MSCHAPv2ChangePasswordData password hash challenge ntResponse
src/Network/EAP/Types.hs view
@@ -11,12 +11,15 @@ The types in this module are pretty much self explanatory. See RFC 3748 for further information on what a given type or definition means. +Note that not nearly all 50+ EAP authentication types are supported. Contributions for further+types are definitely welcome.+ -} module Network.EAP.Types where  import Data.ByteString.Lazy.Char8        (ByteString) import Data.Data                         (Data)-import Data.Word                         (Word8)+import Data.Word                         (Word8, Word16)  data Packet = Packet { getPacketType    :: PacketType,                        getPacketId      :: Word8,@@ -47,4 +50,70 @@                                          getMD5ChallengeName        :: ByteString }              | OTPMessage              { getOTPMessage              :: ByteString }              | GenericTokenCardMessage { getGenericTokenCardMessage :: ByteString }+             | MSCHAPv2Message         { getMSCHAPv2OpCode          :: MSCHAPv2OpCode,+                                         getMSCHAPv2Id              :: Word8,+                                         getMSCHAPv2Length          :: Word16,+                                         getMSCHAPv2Data            :: MSCHAPv2Data }                deriving (Show, Eq, Data)++data AuthType = MD5ChallengeAuth+              | OTPAuth+              | GenericTokenCardAuth+              | RSAPubKeyAuth+              | TLSAuth+              | MSCHAPv2Auth+                deriving (Show, Eq, Data)++data MSCHAPv2OpCode = MSCHAPv2Challenge+                    | MSCHAPv2Response+                    | MSCHAPv2Success+                    | MSCHAPv2Failure+                    | MSCHAPv2ChangePassword+                    deriving (Show, Eq, Data)++data MSCHAPv2Data = MSCHAPv2ChallengeData { getMSCHAPv2Challenge :: ByteString,+                                            getMSCHAPv2ChallengeName :: ByteString }+                  | MSCHAPv2ResponseData { getMSCHAPv2ResponseData :: MSCHAPv2ResponseDataField,+                                           getMSCHAPv2ResponseName :: ByteString }+                  | MSCHAPv2SuccessRequestData { getMSCHAPv2SuccessRequestMessage :: ByteString }+                  | MSCHAPv2FailureRequestData { getMSCHAPv2FailureRequestMessage :: ByteString }+                  | MSCHAPv2ChangePasswordData { getMSCHAPv2EncryptedPassword :: ByteString,+                                                 getMSCHAPv2EncryptedHash     :: ByteString,+                                                 getMSCHAPv2PeerChallenge     :: ByteString,+                                                 getMSCHAPv2NTResponse        :: ByteString }+                  | MSCHAPv2NoData+                  deriving (Show, Eq, Data)++data MSCHAPv2ResponseDataField =+    MSCHAPv2ResponseDataField { getMSCHAPv2ResponsePeerChallenge :: ByteString,+                                getMSCHAPv2ResponseNTResponse    :: ByteString }+    deriving (Show, Eq, Data)++instance Enum AuthType where+    toEnum 4  = MD5ChallengeAuth+    toEnum 5  = OTPAuth+    toEnum 6  = GenericTokenCardAuth+    toEnum 9  = RSAPubKeyAuth+    toEnum 10 = RSAPubKeyAuth+    toEnum 13 = TLSAuth+    toEnum 26 = MSCHAPv2Auth+    toEnum x  = error $ "Unknown EAP authentication type " ++ show x+    fromEnum MD5ChallengeAuth     = 4+    fromEnum OTPAuth              = 5+    fromEnum GenericTokenCardAuth = 6+    fromEnum RSAPubKeyAuth        = 9+    fromEnum TLSAuth              = 13+    fromEnum MSCHAPv2Auth         = 26++instance Enum MSCHAPv2OpCode where+    toEnum 1 = MSCHAPv2Challenge+    toEnum 2 = MSCHAPv2Response+    toEnum 3 = MSCHAPv2Success+    toEnum 4 = MSCHAPv2Failure+    toEnum 7 = MSCHAPv2ChangePassword+    toEnum x = error $ "Unknown MSCHAPv2 Op Code: " ++ show x+    fromEnum MSCHAPv2Challenge      = 1+    fromEnum MSCHAPv2Response       = 2+    fromEnum MSCHAPv2Success        = 3+    fromEnum MSCHAPv2Failure        = 4+    fromEnum MSCHAPv2ChangePassword = 7