diff --git a/radius.cabal b/radius.cabal
--- a/radius.cabal
+++ b/radius.cabal
@@ -1,5 +1,5 @@
 name:                radius
-version:             0.3.0.0
+version:             0.4.0.0
 synopsis:            Remote Authentication Dial In User Service (RADIUS)
 description:         This module provides types and on the wire de/coding of RADIUS packets as per RFC2865
 homepage:            https://github.com/erickg/radius#readme
@@ -16,13 +16,14 @@
 library
   hs-source-dirs:      src
   exposed-modules:     Network.RADIUS.Encoding
+                     , Network.RADIUS.Microsoft
                      , Network.RADIUS.Types
   build-depends:       base >= 4.7 && < 5
                      , binary
                      , bytestring
-                     , cryptohash-md5
+                     , cryptonite
                      , iproute
-                     , pretty-hex
+                     , memory
   default-language:    Haskell2010
   ghc-options:         -Wall
 
diff --git a/src/Network/RADIUS/Encoding.hs b/src/Network/RADIUS/Encoding.hs
--- a/src/Network/RADIUS/Encoding.hs
+++ b/src/Network/RADIUS/Encoding.hs
@@ -18,15 +18,22 @@
 import Control.Monad               (when)
 import Data.Binary                 (Binary(..), encode)
 import Data.Binary.Put             (Put, putLazyByteString, putWord8, putWord16be, putWord32be)
-import Data.Binary.Get             (Get, getLazyByteString, getWord8, getWord16be, getWord32be, isEmpty)
+import Data.Binary.Get             (Get,
+                                    getLazyByteString,
+                                    getWord8,
+                                    getWord16be,
+                                    getWord32be,
+                                    isEmpty)
+import Data.ByteArray              (convert)
 import Data.ByteString.Lazy.Char8  (ByteString, append)
 import Data.IP                     (IPv4, IPv6)
 import Data.Int                    (Int64)
 import Data.Word                   (Word8, Word16)
+import Crypto.Hash.Algorithms      (MD5)
+import Crypto.Hash                 (Digest, hashlazy)
 import Network.RADIUS.Types
 
 import qualified Data.ByteString.Lazy.Char8 as B
-import qualified Crypto.Hash.MD5            as MD5
 
 -- | Self explanatory. It can be useful when reading a RADIUS packet from a socket for example,
 -- so one can retrieve the packet header (containing the packet length) first and then use that
@@ -77,11 +84,14 @@
 
 sign :: ByteString -> ByteString -> ByteString
 sign packet secret =
-    let authenticator = B.fromStrict . MD5.hashlazy $ packet `append` secret
+    let authenticator = hashMD5 $ packet `append` secret
         prologue      = B.take 4 packet -- size of type, id, length
         attributes    = B.drop (fromIntegral radiusHeaderSize) packet
     in prologue `append` authenticator `append` attributes
 
+hashMD5 :: ByteString -> ByteString
+hashMD5 = B.fromStrict . convert . (hashlazy :: ByteString -> Digest MD5)
+
 instance Binary PacketType where
     put = putWord8 . fromIntegral . fromEnum
     get = getWord8 >>= return . toEnum . fromIntegral
@@ -174,11 +184,11 @@
       putWord8 0  -- reserved
       putWord8 $ fromIntegral prefixLength
       putLazyByteString attr
-    put (VendorSpecificAttribute iD str) = do
+    put (VendorSpecificAttribute vendorId str) = do
       let attrLen = (fromIntegral . B.length $ str) + 6 -- Attribute header length + string
       putWord8 26 -- Attribute Type
       putWord8 attrLen
-      put iD
+      putWord32be vendorId
       putLazyByteString str
     put (CHAPPassword identity str)           = do
       let attrLen = (fromIntegral . B.length $ str) + 3 -- Attribute header plus string
diff --git a/src/Network/RADIUS/Microsoft.hs b/src/Network/RADIUS/Microsoft.hs
new file mode 100644
--- /dev/null
+++ b/src/Network/RADIUS/Microsoft.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE RecordWildCards, OverloadedStrings #-}
+{-|
+Module      : Network.RADIUS.Microsoft
+Description : Microsoft specific RADIUS Attributes
+Copyright   : (c) Erick Gonzalez, 2017
+License     : BSD3
+Maintainer  : erick@codemonkeylabs.de
+Stability   : experimental
+Portability : POSIX
+
+This module provides encoding for some of the Microsoft specific attributes, particularly those
+needed for MSCHAPv2.
+
+-}
+module Network.RADIUS.Microsoft (encodeMPPESendKeyAttribute,
+                                 encodeMPPERecvKeyAttribute,
+                                 encodeMPPEEncryptionPolicyAttribute,
+                                 encodeMPPEEncryptionTypesAttribute) where
+
+import Prelude hiding (zipWith)
+import Crypto.Hash.Algorithms    (MD5)
+import Crypto.Hash               (Digest, hash)
+import Data.Binary.Put           (Put, putByteString, putWord8, putWord16be, runPut, putWord32be)
+import Data.Bits                 ((.|.), xor)
+import Data.ByteArray            (convert)
+import Data.ByteString           (ByteString, pack, zipWith)
+import Data.ByteString.Internal  (w2c)
+import Data.Monoid               ((<>))
+import Data.Word                 (Word8, Word16, Word32)
+import Network.RADIUS.Types
+
+import qualified Data.ByteString.Lazy.Char8 as LB
+import qualified Data.ByteString.Char8      as B
+
+-- | Wraps the given encoded vendor specific attribute data into a PacketAttribute with
+-- Microsoft SMI Network Management Enterprise Code
+vendorSpecificAttribute :: LB.ByteString -> PacketAttribute
+vendorSpecificAttribute = VendorSpecificAttribute 311
+
+-- | Encode the MS-MPPE-Send-Key RADIUS attribute as per [RFC2548]
+encodeMPPESendKeyAttribute :: Word16    -- ^ 16 bit random salt
+                           -> ByteString -- ^ MPPE send key
+                           -> ByteString -- ^ Password
+                           -> ByteString -- ^ Authenticator in Access-Request message
+                           -> PacketAttribute
+encodeMPPESendKeyAttribute salt key secret authenticator =
+    vendorSpecificAttribute . runPut $ encodeMPPEKeyAttribute 16 salt key secret authenticator
+
+-- | Encode the MS-MPPE-Recv-Key RADIUS attribute as per [RFC2548]
+encodeMPPERecvKeyAttribute :: Word16     -- ^ 16 bit random salt
+                           -> ByteString -- ^ MPPE recv key
+                           -> ByteString -- ^ Password
+                           -> ByteString -- ^ Authenticator in Access-Request message
+                           -> PacketAttribute
+encodeMPPERecvKeyAttribute salt key secret authenticator =
+    vendorSpecificAttribute . runPut $ encodeMPPEKeyAttribute 17 salt key secret authenticator
+
+encodeMPPEKeyAttribute :: Word8
+                       -> Word16
+                       -> ByteString
+                       -> ByteString
+                       -> ByteString
+                       -> Put
+encodeMPPEKeyAttribute vendorType salt key secret authenticator = do
+  putWord8 vendorType
+  let salt'      = LB.toStrict . runPut . putWord16be $ salt .|. 0x8000 -- MSB in salt must be set
+      keyLength  = w2c . fromIntegral $ B.length key
+      str        = B.cons keyLength key
+      n          = B.length str `mod` 16
+      m          = if n == 0 then 0 else 16 - n
+      str'       = str <> B.replicate m '\NUL'
+      (_,result) = foldl encrypt ((authenticator <> salt'), B.empty) $ partition 16 str'
+      vendorLen  = fromIntegral $ 4 + B.length result
+  putWord8 vendorLen
+  putByteString salt'
+  putByteString result
+      where md5                  = convert . (hash :: ByteString -> Digest MD5)
+            partition n          = partition' [] n
+            partition' acc _ ""  = reverse acc
+            partition' acc n str =
+                let (x, xs) = B.splitAt n str
+                in partition' (x:acc) n xs
+            encrypt (bytes, acc) chunk =
+                let c = pack $ zipWith xor chunk (md5 $ secret <> bytes)
+                in (c, acc <> c)
+
+-- | Encode MS-MPPE-Encryption-Policy as per [RFC2548]
+encodeMPPEEncryptionPolicyAttribute :: Word32         -- ^ Policy value
+                                    -> PacketAttribute
+encodeMPPEEncryptionPolicyAttribute policy =
+    vendorSpecificAttribute . runPut $ do
+      putWord8 7 -- for MS-MPPE-Encryption-Policy.
+      putWord8 6 -- fixed length
+      putWord32be policy
+
+-- | Encode MS-MPPE-Encryption-Types as per [RFC2548]
+encodeMPPEEncryptionTypesAttribute :: Word32
+                                   -> PacketAttribute -- ^ Encryption types value (see RFC)
+encodeMPPEEncryptionTypesAttribute types =
+    vendorSpecificAttribute . runPut $ do
+      putWord8 8 -- for MS-MPPE-Encryption-Types.
+      putWord8 6 -- fixed length
+      putWord32be types
diff --git a/src/Network/RADIUS/Types.hs b/src/Network/RADIUS/Types.hs
--- a/src/Network/RADIUS/Types.hs
+++ b/src/Network/RADIUS/Types.hs
@@ -95,7 +95,7 @@
   | FramedIPXNetworkAttribute       { getFramedIPXNetworkAttribute       :: Word32            }
   | StateAttribute                  { getStateAttribute                  :: ByteString        }
   | ClassAttribute                  { getClassAttribute                  :: ByteString        }
-  | VendorSpecificAttribute         { getVendorSpecificAttributeId       :: Word32,
+  | VendorSpecificAttribute         { getVendorIdAttribute               :: Word32,
                                       getVendorSpecificAttribute         :: ByteString        }
   | SessionTimeoutAttribute         { getSessionTimeoutAttribute         :: Word32            }
   | IdleTimeoutAttribute            { getIdleTimeoutAttribute            :: Word32            }
