packages feed

clientsession (empty) → 0.0.0

raw patch · 4 files changed

+164/−0 lines, 4 filesdep +Cryptodep +basedep +bytestringsetup-changed

Dependencies added: Crypto, base, bytestring, dataenc, random, utf8-string

Files

+ LICENSE view
@@ -0,0 +1,25 @@+The following license covers this documentation, and the source code, except+where otherwise indicated.++Copyright 2008, Michael Snoyman. All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+  list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+  this list of conditions and the following disclaimer in the documentation+  and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,7 @@+#!/usr/bin/env runhaskell++> module Main where+> import Distribution.Simple++> main :: IO ()+> main = defaultMain
+ Web/ClientSession.hs view
@@ -0,0 +1,114 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE TypeSynonymInstances #-}+---------------------------------------------------------+-- |+-- Module        : Web.ClientSession+-- Copyright     : Michael Snoyman+-- License       : BSD3+--+-- Maintainer    : Michael Snoyman <michael@snoyman.com>+-- Stability     : Stable+-- Portability   : portable+--+-- Stores session data in a client cookie.+--+---------------------------------------------------------+module Web.ClientSession+    ( -- * Automatic key generation+      getKey+    , getDefaultKey+      -- * Actual encryption/decryption+    , encrypt+    , decrypt+      -- * Classes+    , IsByteString (..)+    ) where++import Codec.Encryption.AES (AESKey)+import qualified Data.ByteString as BS+import qualified Data.ByteString.UTF8 as BSU++import Data.LargeWord (Word256)+import Codec.Utils (listFromOctets, listToOctets)+import Data.Word (Word8)+import System.Random (getStdGen, randoms, Random, randomR, random)+import qualified Data.ByteString as BS+import qualified Codec.Encryption.AES as AES+import qualified Codec.Binary.Base64Url as Base64+import qualified Data.Digest.MD5 as MD5++-- | A class for anything which can become a 'Data.ByteString'.+-- The 'String' instance uses UTF8 encoding.+class IsByteString a where+    toByteString :: a -> BS.ByteString+    fromByteString :: BS.ByteString -> a+instance IsByteString BS.ByteString where+    toByteString = id+    fromByteString = id+instance IsByteString String where+    toByteString = BSU.fromString+    fromByteString = BSU.toString++-- | Simply calls 'getKey' \"client_session_key.aes\"+getDefaultKey :: IO Word256+getDefaultKey = getKey "client_session_key.aes"++-- | Get a 256-bit key from the given text file.+-- If the file does not exist, or did not contain enough bits,+-- a random key will be generated and stored in that file.+getKey :: FilePath     -- ^ File name where key is stored.+       -> IO Word256   -- ^ The actual 256-bit key.+getKey keyFile = catch loadKeyFromFile $ const generateNewKey where+        loadKeyFromFile :: IO Word256+        loadKeyFromFile = do+                contents <- BS.readFile keyFile+                if BS.length contents < 32+                        then fail "Key too small"+                        else return $ head $ listFromOctets $ BS.unpack contents+        generateNewKey :: IO Word256+        generateNewKey = do+                stdGen <- getStdGen+                let word8s = take 32 $ randoms stdGen+                let newKey = head $ listFromOctets word8s+                BS.writeFile keyFile $ BS.pack word8s+                return newKey++instance Random Word8 where+        randomR (a,b) g =+                let (x, g') = randomR (toInteger a, toInteger b) g+                in (toEnum $ fromEnum $ mod x 256, g')+        random = randomR (minBound,maxBound)++-- | Encrypt with the given key and base-64 encode.+-- A hash is stored inside the encrypted key so that, upon decryption,+-- integrity can be guaranteed.+encrypt :: (IsByteString b, AES.AESKey k)+        => k               -- ^ The key used for encryption.+        -> b               -- ^ The data to encrypt.+        -> String          -- ^ Encrypted and encoded data.+encrypt k x =+        let unpacked = BS.unpack $ toByteString x+        in Base64.encode . listToOctets . map (AES.encrypt k) .+           listFromOctets $ MD5.hash unpacked ++ unpacked++-- | Helper function to convert a Maybe into any monad+liftMaybe :: Monad m => Maybe a -> m a+liftMaybe Nothing = fail "Nothing"+liftMaybe (Just x) = return x++-- | Base-64 decode and decrypt with the given key, if possible.+-- If either the original string is not a valid base-64 encoded string,+-- or the hash at the beginning of the decrypted string does not match,+-- this function 'fail's.+decrypt :: (AES.AESKey k, Monad m, IsByteString b)+        => k                    -- ^ The key used for encryption.+        -> String               -- ^ Data to decrypt.+        -> m b                  -- ^ The decrypted data, if possible.+decrypt k x = do+        decoded <- liftMaybe $ Base64.decode x+        let decrypted = listToOctets $ map (AES.decrypt k)+                        $ listFromOctets decoded+        let (hash, rest) = splitAt 16 decrypted+        if hash == MD5.hash rest+                then return $ fromByteString $ BS.pack rest+                else fail "Invalid"
+ clientsession.cabal view
@@ -0,0 +1,18 @@+name:            clientsession+version:         0.0.0+license:         BSD3+license-file:    LICENSE+author:          Michael Snoyman <michael@snoyman.com>+maintainer:      Michael Snoyman <michael@snoyman.com>+synopsis:        Store session data in a cookie.+description:     Achieves security through AES encryption and MD5 hashing.+                 Uses base64 encoding to avoid any issues with characters.+category:        Web+stability:       stable+cabal-version:   >= 1.2+build-type:      Simple++library+    build-depends:   base, Crypto, dataenc, bytestring, random, utf8-string+    exposed-modules: Web.ClientSession+    ghc-options:     -Wall