packages feed

HsOpenSSL 0.10.1.1 → 0.10.1.2

raw patch · 19 files changed

+557/−567 lines, 19 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- OpenSSL.EVP.Sign: signFinal :: KeyPair k => DigestCtx -> k -> IO ByteString
- OpenSSL.Session: UnknownError :: !Int -> UnknownError
- OpenSSL.Session: data ConnectionCleanlyClosed
- OpenSSL.Session: data SSLIOError
- OpenSSL.Session: data UnknownError
- OpenSSL.Session: data WantAccept
- OpenSSL.Session: data WantConnect
- OpenSSL.Session: data WantX509Lookup
- OpenSSL.Session: instance Eq ConnectionCleanlyClosed
- OpenSSL.Session: instance Eq SSLIOError
- OpenSSL.Session: instance Eq UnknownError
- OpenSSL.Session: instance Eq WantAccept
- OpenSSL.Session: instance Eq WantConnect
- OpenSSL.Session: instance Eq WantX509Lookup
- OpenSSL.Session: instance Exception ConnectionCleanlyClosed
- OpenSSL.Session: instance Exception SSLIOError
- OpenSSL.Session: instance Exception UnknownError
- OpenSSL.Session: instance Exception WantAccept
- OpenSSL.Session: instance Exception WantConnect
- OpenSSL.Session: instance Exception WantX509Lookup
- OpenSSL.Session: instance Show ConnectionCleanlyClosed
- OpenSSL.Session: instance Show SSLIOError
- OpenSSL.Session: instance Show UnknownError
- OpenSSL.Session: instance Show WantAccept
- OpenSSL.Session: instance Show WantConnect
- OpenSSL.Session: instance Show WantX509Lookup
- OpenSSL.Session: instance Typeable ConnectionCleanlyClosed
- OpenSSL.Session: instance Typeable SSLIOError
- OpenSSL.Session: instance Typeable UnknownError
- OpenSSL.Session: instance Typeable WantAccept
- OpenSSL.Session: instance Typeable WantConnect
- OpenSSL.Session: instance Typeable WantX509Lookup
+ OpenSSL.EVP.Digest: digestBS' :: Digest -> ByteString -> ByteString
+ OpenSSL.EVP.PKey: class PKey k
+ OpenSSL.Session: ProtocolError :: !String -> ProtocolError
+ OpenSSL.Session: instance Eq ShutdownType
+ OpenSSL.Session: instance Show ShutdownType

Files

HsOpenSSL.cabal view
@@ -1,26 +1,27 @@-Name: HsOpenSSL-Synopsis: (Incomplete) OpenSSL binding for Haskell+Name:         HsOpenSSL+Synopsis:     (Incomplete) OpenSSL binding for Haskell Description:-        HsOpenSSL is an (incomplete) OpenSSL binding for Haskell. It-        can generate RSA and DSA keys, read and write PEM files,-        generate message digests, sign and verify messages, encrypt-        and decrypt messages. It also has some capabilities of-        creating SSL clients and servers.-Version: 0.10.1.1-License: PublicDomain-License-File: COPYING-Author: Adam Langley, Mikhail Vorozhtsov, PHO, Taru Karttunen-Maintainer: PHO <pho at cielonegro dot org>-Stability: stable-Homepage: http://cielonegro.org/HsOpenSSL.html-Bug-Reports: http://static.cielonegro.org/ditz/HsOpenSSL/-Category: Cryptography-Tested-With: GHC == 7.0.3+    HsOpenSSL is an (incomplete) OpenSSL binding for Haskell. It can+    generate RSA and DSA keys, read and write PEM files, generate+    message digests, sign and verify messages, encrypt and decrypt+    messages. It has also some capabilities of creating SSL clients+    and servers.+Version:       0.10.1.2+License:       PublicDomain+License-File:  COPYING+Author:        Adam Langley, Mikhail Vorozhtsov, PHO, Taru Karttunen+Maintainer:    PHO <pho at cielonegro dot org>+Stability:     stable+Homepage:      https://github.com/phonohawk/HsOpenSSL+Bug-Reports:   https://github.com/phonohawk/HsOpenSSL/issues+Category:      Cryptography+Tested-With:   GHC == 7.0.3 Cabal-Version: >= 1.6-Build-Type: Simple+Build-Type:    Simple Extra-Source-Files:     AUTHORS     NEWS+    README.rst     cbits/HsOpenSSL.h     cbits/mutex.h     examples/Makefile@@ -87,6 +88,7 @@           OpenSSL.Stack           OpenSSL.Utils           OpenSSL.X509.Name+          OpenSSL.EVP.Internal           OpenSSL.DH.Internal   Extensions:           ForeignFunctionInterface, EmptyDataDecls, MagicHash,
NEWS view
@@ -1,7 +1,23 @@ -*- coding: utf-8 -*- +Changes from 0.10.1.1 to 0.10.1.2+---------------------------------+* Applied a patch by Mikhail Vorozhtsov:+  - Moved all EVP-related private functions to OpenSSL.EVP.Internal.++* Improve the error handling in OpenSSL.Session:+  - SSL_get_error() must be called within the OS thread which caused+    the failed operation as it inspects the thread-local storage.+  - write/tryWrite should throw EPIPE for cleanly-closed connections+    rather than EOF.+  - shutdown/tryShutdown shouldn't throw an exception when a remote+    peer sends us a "close notify" alert and closes the connection+    without waiting for our reply.+  - ProtocolError should contain an error message string.++ Changes from 0.10.1 to 0.10.1.1------------------------------------+------------------------------- * Applied a patch by Peter Gammie:   - GHC 6.12.3 friendliness: don't use Control.Monad.void 
OpenSSL/DH.hsc view
@@ -1,23 +1,27 @@ -- | Diffie-Hellman key exchange-module OpenSSL.DH (-    DHP,-    DH,-    DHGen(..),-    genDHParams,-    getDHLength,-    checkDHParams,-    genDH,-    getDHParams,-    getDHPublicKey,-    computeDHKey,-  ) where-+module OpenSSL.DH+    ( DHP+    , DH+    , DHGen(..)+    , genDHParams+    , getDHLength+    , checkDHParams+    , genDH+    , getDHParams+    , getDHPublicKey+    , computeDHKey+    )+    where import Data.Word (Word8) import Data.ByteString (ByteString) import qualified Data.ByteString.Internal as BS import Control.Applicative ((<$>)) import Foreign.Ptr (Ptr, nullPtr)+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CInt(..))+#else import Foreign.C.Types (CInt)+#endif import Foreign.Marshal.Alloc (alloca) import OpenSSL.BN import OpenSSL.DH.Internal
OpenSSL/ERR.hsc view
@@ -5,29 +5,18 @@     , errorString     )     where--import           Foreign-import           Foreign.C-+import Foreign+import Foreign.C  foreign import ccall unsafe "ERR_get_error"-        _get_error :: IO CULong+    getError :: IO CULong  foreign import ccall unsafe "ERR_peek_error"-        _peek_error :: IO CULong+    peekError :: IO CULong  foreign import ccall unsafe "ERR_error_string"-        _error_string :: CULong -> CString -> IO CString---getError :: IO Integer-getError = fmap fromIntegral _get_error---peekError :: IO Integer-peekError = fmap fromIntegral _peek_error-+    _error_string :: CULong -> CString -> IO CString -errorString :: Integer -> IO String+errorString :: CULong -> IO String errorString code-    = _error_string (fromIntegral code) nullPtr >>= peekCString+    = _error_string code nullPtr >>= peekCString
OpenSSL/EVP/Cipher.hsc view
@@ -1,71 +1,34 @@ {- -*- haskell -*- -} -{-# OPTIONS_HADDOCK prune #-}- -- |An interface to symmetric cipher algorithms.  #include "HsOpenSSL.h"  module OpenSSL.EVP.Cipher     ( Cipher-    , EVP_CIPHER -- private-    , withCipherPtr -- private-     , getCipherByName     , getCipherNames -    , cipherIvLength -- private--    , CipherCtx -- private-    , EVP_CIPHER_CTX -- private-    , newCtx -- private-    , withCipherCtxPtr -- private-     , CryptoMode(..) -    , cipherStrictly -- private-    , cipherLazily -- private-     , cipher     , cipherBS     , cipherLBS     , cipherStrictLBS     )     where--import           Control.Exception(bracket_)-import           Control.Monad-import           Data.ByteString.Internal (createAndTrim)-import           Data.ByteString.Unsafe (unsafeUseAsCStringLen)+import Data.ByteString.Unsafe (unsafeUseAsCStringLen) import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy.Char8 as L8-import qualified Data.ByteString.Lazy.Internal as L8Internal-import           Foreign-import           Foreign.C-import           OpenSSL.Objects-import           OpenSSL.Utils-import           System.IO.Unsafe---{- EVP_CIPHER ---------------------------------------------------------------- -}---- |@Cipher@ is an opaque object that represents an algorithm of--- symmetric cipher.-newtype Cipher     = Cipher (Ptr EVP_CIPHER)-data    EVP_CIPHER-+import Foreign+import Foreign.C+import OpenSSL.Objects+import OpenSSL.Utils+import OpenSSL.EVP.Internal  foreign import ccall unsafe "EVP_get_cipherbyname"         _get_cipherbyname :: CString -> IO (Ptr EVP_CIPHER) --foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_iv_length"-        _iv_length :: Ptr EVP_CIPHER -> CInt---withCipherPtr :: Cipher -> (Ptr EVP_CIPHER -> IO a) -> IO a-withCipherPtr (Cipher cipherPtr) f = f cipherPtr- -- |@'getCipherByName' name@ returns a symmetric cipher algorithm -- whose name is @name@. If no algorithms are found, the result is -- @Nothing@.@@ -83,65 +46,21 @@ getCipherNames :: IO [String] getCipherNames = getObjNames CipherMethodType True --cipherIvLength :: Cipher -> Int-cipherIvLength (Cipher cipherPtr) = fromIntegral $ _iv_length cipherPtr---{- EVP_CIPHER_CTX ------------------------------------------------------------ -}--newtype CipherCtx      = CipherCtx (ForeignPtr EVP_CIPHER_CTX)-data    EVP_CIPHER_CTX---foreign import ccall unsafe "EVP_CIPHER_CTX_init"-        _ctx_init :: Ptr EVP_CIPHER_CTX -> IO ()--foreign import ccall unsafe "&EVP_CIPHER_CTX_cleanup"-        _ctx_cleanup :: FunPtr (Ptr EVP_CIPHER_CTX -> IO ())--foreign import ccall unsafe "EVP_CIPHER_CTX_cleanup"-        _ctx_cleanup' :: Ptr EVP_CIPHER_CTX -> IO ()--foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_CTX_block_size"-        _ctx_block_size :: Ptr EVP_CIPHER_CTX -> CInt---newCtx :: IO CipherCtx-newCtx = do ctx <- mallocForeignPtrBytes (#size EVP_CIPHER_CTX)-            withForeignPtr ctx _ctx_init-            addForeignPtrFinalizer _ctx_cleanup ctx-            return $ CipherCtx ctx---withCipherCtxPtr :: CipherCtx -> (Ptr EVP_CIPHER_CTX -> IO a) -> IO a-withCipherCtxPtr (CipherCtx ctx) = withForeignPtr ctx-- {- encrypt/decrypt ----------------------------------------------------------- -}  -- |@CryptoMode@ represents instruction to 'cipher' and such like. data CryptoMode = Encrypt | Decrypt --foreign import ccall unsafe "EVP_CipherInit"-        _CipherInit :: Ptr EVP_CIPHER_CTX -> Ptr EVP_CIPHER -> CString -> CString -> CInt -> IO CInt--foreign import ccall unsafe "EVP_CipherUpdate"-        _CipherUpdate :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt -> Ptr CChar -> CInt -> IO CInt--foreign import ccall unsafe "EVP_CipherFinal"-        _CipherFinal :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt -> IO CInt-- cryptoModeToInt :: CryptoMode -> CInt cryptoModeToInt Encrypt = 1 cryptoModeToInt Decrypt = 0 +foreign import ccall unsafe "EVP_CipherInit"+        _CipherInit :: Ptr EVP_CIPHER_CTX -> Ptr EVP_CIPHER -> CString -> CString -> CInt -> IO CInt  cipherInit :: Cipher -> String -> String -> CryptoMode -> IO CipherCtx cipherInit (Cipher c) key iv mode-    = do ctx <- newCtx+    = do ctx <- newCipherCtx          withCipherCtxPtr ctx $ \ ctxPtr ->              withCString key $ \ keyPtr ->                  withCString iv $ \ ivPtr ->@@ -157,8 +76,7 @@                 -> L8.ByteString  -- ^ Input                 -> IO L8.ByteString cipherStrictLBS (Cipher c) key iv mode input =-  allocaBytes (#size EVP_CIPHER_CTX) $ \cptr ->-  bracket_ (_ctx_init cptr) (_ctx_cleanup' cptr) $+  withNewCipherCtxPtr $ \cptr ->   unsafeUseAsCStringLen key $ \(keyp,_) ->   unsafeUseAsCStringLen iv  $ \(ivp, _) -> do   failIf_ (/= 1) =<< _CipherInit cptr c keyp ivp (cryptoModeToInt mode)@@ -167,26 +85,6 @@   rf <- cipherFinalBS cc   return $ L8.fromChunks (rr++[rf]) -cipherUpdateBS :: CipherCtx -> B8.ByteString -> IO B8.ByteString-cipherUpdateBS ctx inBS-    = withCipherCtxPtr ctx $ \ ctxPtr ->-      unsafeUseAsCStringLen inBS $ \ (inBuf, inLen) ->-      createAndTrim (inLen + fromIntegral (_ctx_block_size ctxPtr) - 1) $ \ outBuf ->-      alloca $ \ outLenPtr ->-      _CipherUpdate ctxPtr (castPtr outBuf) outLenPtr inBuf (fromIntegral inLen)-           >>= failIf (/= 1)-           >>  liftM fromIntegral (peek outLenPtr)---cipherFinalBS :: CipherCtx -> IO B8.ByteString-cipherFinalBS ctx-    = withCipherCtxPtr ctx $ \ ctxPtr ->-      createAndTrim (fromIntegral $ _ctx_block_size ctxPtr) $ \ outBuf ->-      alloca $ \ outLenPtr ->-      _CipherFinal ctxPtr (castPtr outBuf) outLenPtr-           >>= failIf (/= 1)-           >>  liftM fromIntegral (peek outLenPtr)- -- |@'cipher'@ lazilly encrypts or decrypts a stream of data. The -- input string doesn't necessarily have to be finite. cipher :: Cipher     -- ^ algorithm to use@@ -199,7 +97,7 @@                      --   U+00FF.        -> IO String  -- ^ the result string cipher c key iv mode input-    = liftM L8.unpack $ cipherLBS c key iv mode $ L8.pack input+    = fmap L8.unpack $ cipherLBS c key iv mode $ L8.pack input  -- |@'cipherBS'@ strictly encrypts or decrypts a chunk of data. cipherBS :: Cipher        -- ^ algorithm to use@@ -224,21 +122,3 @@     = do ctx <- cipherInit c key iv mode          cipherLazily ctx input --cipherStrictly :: CipherCtx -> B8.ByteString -> IO B8.ByteString-cipherStrictly ctx input-    = do output'  <- cipherUpdateBS ctx input-         output'' <- cipherFinalBS ctx-         return $ B8.append output' output''---cipherLazily :: CipherCtx -> L8.ByteString -> IO L8.ByteString--cipherLazily ctx (L8Internal.Empty) =-  cipherFinalBS ctx >>= \ bs -> (return . L8.fromChunks) [bs]--cipherLazily ctx (L8Internal.Chunk x xs) = do-  y  <- cipherUpdateBS ctx x-  ys <- unsafeInterleaveIO $-        cipherLazily ctx xs-  return $ L8Internal.Chunk y ys
OpenSSL/EVP/Digest.hsc view
@@ -1,26 +1,14 @@ {- -*- haskell -*- -} -{-# OPTIONS_HADDOCK prune #-}- -- |An interface to message digest algorithms.  #include "HsOpenSSL.h"  module OpenSSL.EVP.Digest     ( Digest-    , EVP_MD -- private-    , withMDPtr -- private-     , getDigestByName     , getDigestNames -    , DigestCtx -- private-    , EVP_MD_CTX -- private-    , withDigestCtxPtr -- private--    , digestStrictly -- private-    , digestLazily   -- private-     , digest     , digestBS     , digestBS'@@ -31,32 +19,19 @@     )     where -import           Control.Monad-import           Data.ByteString.Internal (createAndTrim, create)+import           Data.ByteString.Internal (create) import           Data.ByteString.Unsafe (unsafeUseAsCStringLen) import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy.Char8 as L8+import           Control.Applicative ((<$>)) import           Foreign import           Foreign.C+import           OpenSSL.EVP.Internal import           OpenSSL.Objects-import           OpenSSL.Utils --{- EVP_MD -------------------------------------------------------------------- -}---- |@Digest@ is an opaque object that represents an algorithm of--- message digest.-newtype Digest  = Digest (Ptr EVP_MD)-data    EVP_MD-- foreign import ccall unsafe "EVP_get_digestbyname"         _get_digestbyname :: CString -> IO (Ptr EVP_MD) --withMDPtr :: Digest -> (Ptr EVP_MD -> IO a) -> IO a-withMDPtr (Digest mdPtr) f = f mdPtr- -- |@'getDigestByName' name@ returns a message digest algorithm whose -- name is @name@. If no algorithms are found, the result is -- @Nothing@.@@ -74,89 +49,8 @@ getDigestNames :: IO [String] getDigestNames = getObjNames MDMethodType True --{- EVP_MD_CTX ---------------------------------------------------------------- -}--newtype DigestCtx  = DigestCtx (ForeignPtr EVP_MD_CTX)-data    EVP_MD_CTX---foreign import ccall unsafe "EVP_MD_CTX_init"-        _ctx_init :: Ptr EVP_MD_CTX -> IO ()--foreign import ccall unsafe "&EVP_MD_CTX_cleanup"-        _ctx_cleanup :: FunPtr (Ptr EVP_MD_CTX -> IO ())---newCtx :: IO DigestCtx-newCtx = do ctx <- mallocForeignPtrBytes (#size EVP_MD_CTX)-            withForeignPtr ctx _ctx_init-            addForeignPtrFinalizer _ctx_cleanup ctx-            return $ DigestCtx ctx---withDigestCtxPtr :: DigestCtx -> (Ptr EVP_MD_CTX -> IO a) -> IO a-withDigestCtxPtr (DigestCtx ctx) = withForeignPtr ctx-- {- digest -------------------------------------------------------------------- -} -foreign import ccall unsafe "EVP_DigestInit"-        _DigestInit :: Ptr EVP_MD_CTX -> Ptr EVP_MD -> IO CInt--foreign import ccall unsafe "EVP_DigestUpdate"-        _DigestUpdate :: Ptr EVP_MD_CTX -> Ptr CChar -> CSize -> IO CInt--foreign import ccall unsafe "EVP_DigestFinal"-        _DigestFinal :: Ptr EVP_MD_CTX -> Ptr CChar -> Ptr CUInt -> IO CInt---digestInit :: Digest -> IO DigestCtx-digestInit (Digest md)-    = do ctx <- newCtx-         withDigestCtxPtr ctx $ \ ctxPtr ->-             _DigestInit ctxPtr md >>= failIf_ (/= 1)-         return ctx   ---digestUpdateBS :: DigestCtx -> B8.ByteString -> IO ()-digestUpdateBS ctx bs-    = withDigestCtxPtr ctx $ \ ctxPtr ->-      unsafeUseAsCStringLen bs $ \ (buf, len) ->-      _DigestUpdate ctxPtr buf (fromIntegral len) >>= failIf (/= 1) >> return ()---digestFinal :: DigestCtx -> IO String-digestFinal ctx-    = withDigestCtxPtr ctx $ \ ctxPtr ->-      allocaArray (#const EVP_MAX_MD_SIZE) $ \ bufPtr ->-      alloca $ \ bufLenPtr ->-      do _DigestFinal ctxPtr bufPtr bufLenPtr >>= failIf_ (/= 1)-         bufLen <- liftM fromIntegral $ peek bufLenPtr-         peekCStringLen (bufPtr, bufLen)--digestFinalBS :: DigestCtx -> IO B8.ByteString-digestFinalBS ctx =-  withDigestCtxPtr ctx $ \ctxPtr ->-  createAndTrim (#const EVP_MAX_MD_SIZE) $ \bufPtr ->-  alloca $ \bufLenPtr ->-  do _DigestFinal ctxPtr (castPtr bufPtr) bufLenPtr >>= failIf_ (/= 1)-     liftM fromIntegral $ peek bufLenPtr---digestStrictly :: Digest -> B8.ByteString -> IO DigestCtx-digestStrictly md input-    = do ctx <- digestInit md-         digestUpdateBS ctx input-         return ctx---digestLazily :: Digest -> L8.ByteString -> IO DigestCtx-digestLazily md lbs-    = do ctx <- digestInit md-         mapM_ (digestUpdateBS ctx) $ L8.toChunks lbs-         return ctx- -- |@'digest'@ digests a stream of data. The string must -- not contain any letters which aren't in the range of U+0000 - -- U+00FF.@@ -167,19 +61,17 @@ -- |@'digestBS'@ digests a chunk of data. digestBS :: Digest -> B8.ByteString -> String digestBS md input-    = unsafePerformIO-      (digestStrictly md input >>= digestFinal)+    = unsafePerformIO $ digestStrictly md input >>= digestFinal +-- |Same as 'digestBS' but returns 'B8.ByteString' instead. digestBS' :: Digest -> B8.ByteString -> B8.ByteString digestBS' md input-    = unsafePerformIO-      (digestStrictly md input >>= digestFinalBS)+    = unsafePerformIO $ digestStrictly md input >>= digestFinalBS  -- |@'digestLBS'@ digests a stream of data. digestLBS :: Digest -> L8.ByteString -> String digestLBS md input-    = unsafePerformIO-      (digestLazily md input >>= digestFinal)+    = unsafePerformIO $ digestLazily md input >>= digestFinal  {- HMAC ---------------------------------------------------------------------- -} @@ -197,9 +89,11 @@   allocaArray (#const EVP_MAX_MD_SIZE) $ \bufPtr ->   alloca $ \bufLenPtr ->   unsafeUseAsCStringLen key $ \(keydata, keylen) ->-  unsafeUseAsCStringLen input $ \(inputdata, inputlen) ->-  do _HMAC md keydata (fromIntegral keylen) inputdata (fromIntegral inputlen) bufPtr bufLenPtr-     bufLen <- liftM fromIntegral $ peek bufLenPtr+  unsafeUseAsCStringLen input $ \(inputdata, inputlen) -> do+     _HMAC md+       keydata (fromIntegral keylen) inputdata (fromIntegral inputlen)+       bufPtr bufLenPtr+     bufLen <- fromIntegral <$> peek bufLenPtr      B8.packCStringLen (bufPtr, bufLen)  -- | Calculate a PKCS5-PBKDF2 SHA1-HMAC suitable for password hashing.@@ -219,7 +113,9 @@            (fromIntegral iter) (fromIntegral dkeylen) (castPtr dkeydata)       >> return () -foreign import ccall unsafe "PKCS5_PBKDF2_HMAC_SHA1" _PKCS5_PBKDF2_HMAC_SHA1 :: Ptr CChar -> CInt-                                                                             -> Ptr CChar -> CInt-                                                                             -> CInt -> CInt -> Ptr CChar-                                                                             -> IO CInt+foreign import ccall unsafe "PKCS5_PBKDF2_HMAC_SHA1"+  _PKCS5_PBKDF2_HMAC_SHA1 :: Ptr CChar -> CInt+                          -> Ptr CChar -> CInt+                          -> CInt -> CInt -> Ptr CChar+                          -> IO CInt+
+ OpenSSL/EVP/Internal.hsc view
@@ -0,0 +1,307 @@++module OpenSSL.EVP.Internal ( +    Cipher(..),+    EVP_CIPHER,+    withCipherPtr,++    cipherIvLength,++    CipherCtx(..),+    EVP_CIPHER_CTX,+    newCipherCtx,+    withCipherCtxPtr,+    withNewCipherCtxPtr,++    cipherUpdateBS,+    cipherFinalBS,+    cipherStrictly,+    cipherLazily,++    Digest(..),+    EVP_MD,+    withMDPtr,++    DigestCtx(..),+    EVP_MD_CTX,+    withDigestCtxPtr,++    digestUpdateBS,+    digestFinalBS,+    digestFinal,+    digestStrictly,+    digestLazily,++    VaguePKey(..),+    EVP_PKEY,+    PKey(..),+    createPKey,+    wrapPKeyPtr,+    withPKeyPtr,+    withPKeyPtr',+    unsafePKeyToPtr,+    touchPKey+  ) where++#include "HsOpenSSL.h"++import qualified Data.ByteString.Internal as B8+import qualified Data.ByteString.Unsafe as B8+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy.Char8 as L8+import qualified Data.ByteString.Lazy.Internal as L8+import Control.Applicative ((<$>))+import Control.Exception (mask, mask_, bracket_, onException)+import Foreign.C.Types (CChar)+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CInt(..), CUInt(..), CSize(..))+#else+import Foreign.C.Types (CInt, CUInt, CSize)+#endif+import Foreign.Ptr (Ptr, castPtr, FunPtr)+import Foreign.C.String (peekCStringLen)+import Foreign.ForeignPtr (+         ForeignPtr, newForeignPtr, withForeignPtr, addForeignPtrFinalizer,+         mallocForeignPtrBytes, touchForeignPtr, unsafeForeignPtrToPtr)+import Foreign.Storable (Storable(..))+import Foreign.Marshal.Alloc (alloca, allocaBytes)+import Foreign.Marshal.Array (allocaArray)+import System.IO.Unsafe (unsafeInterleaveIO)+import OpenSSL.Utils++{- EVP_CIPHER ---------------------------------------------------------------- -}++-- |@Cipher@ is an opaque object that represents an algorithm of+-- symmetric cipher.+newtype Cipher     = Cipher (Ptr EVP_CIPHER)+data    EVP_CIPHER++withCipherPtr :: Cipher -> (Ptr EVP_CIPHER -> IO a) -> IO a+withCipherPtr (Cipher cipherPtr) f = f cipherPtr++foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_iv_length"+        _iv_length :: Ptr EVP_CIPHER -> CInt++cipherIvLength :: Cipher -> Int+cipherIvLength (Cipher cipherPtr) = fromIntegral $ _iv_length cipherPtr++{- EVP_CIPHER_CTX ------------------------------------------------------------ -}++newtype CipherCtx      = CipherCtx (ForeignPtr EVP_CIPHER_CTX)+data    EVP_CIPHER_CTX++foreign import ccall unsafe "EVP_CIPHER_CTX_init"+  _cipher_ctx_init :: Ptr EVP_CIPHER_CTX -> IO ()++foreign import ccall unsafe "&EVP_CIPHER_CTX_cleanup"+  _cipher_ctx_cleanup :: FunPtr (Ptr EVP_CIPHER_CTX -> IO ())++foreign import ccall unsafe "EVP_CIPHER_CTX_cleanup"+  _cipher_ctx_cleanup' :: Ptr EVP_CIPHER_CTX -> IO ()++foreign import ccall unsafe "HsOpenSSL_EVP_CIPHER_CTX_block_size"+  _cipher_ctx_block_size :: Ptr EVP_CIPHER_CTX -> CInt++newCipherCtx :: IO CipherCtx+newCipherCtx = do+  ctx <- mallocForeignPtrBytes (#size EVP_CIPHER_CTX)+  mask_ $ do+    withForeignPtr ctx _cipher_ctx_init+    addForeignPtrFinalizer _cipher_ctx_cleanup ctx+  return $ CipherCtx ctx++withCipherCtxPtr :: CipherCtx -> (Ptr EVP_CIPHER_CTX -> IO a) -> IO a+withCipherCtxPtr (CipherCtx ctx) = withForeignPtr ctx++withNewCipherCtxPtr :: (Ptr EVP_CIPHER_CTX -> IO a) -> IO a+withNewCipherCtxPtr f =+  allocaBytes (#size EVP_CIPHER_CTX) $ \ptr ->+    bracket_ (_cipher_ctx_init ptr) (_cipher_ctx_cleanup' ptr) (f ptr)++{- encrypt/decrypt ----------------------------------------------------------- -}++foreign import ccall unsafe "EVP_CipherUpdate"+  _CipherUpdate :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt+                -> Ptr CChar -> CInt -> IO CInt++cipherUpdateBS :: CipherCtx -> B8.ByteString -> IO B8.ByteString+cipherUpdateBS ctx inBS =+  withCipherCtxPtr ctx $ \ctxPtr ->+    B8.unsafeUseAsCStringLen inBS $ \(inBuf, inLen) ->+      let len = inLen + fromIntegral (_cipher_ctx_block_size ctxPtr) - 1 in+        B8.createAndTrim len $ \outBuf ->+          alloca $ \outLenPtr ->+            _CipherUpdate ctxPtr (castPtr outBuf) outLenPtr inBuf+                          (fromIntegral inLen)+              >>= failIf (/= 1)+              >>  fromIntegral <$> peek outLenPtr++foreign import ccall unsafe "EVP_CipherFinal"+  _CipherFinal :: Ptr EVP_CIPHER_CTX -> Ptr CChar -> Ptr CInt -> IO CInt++cipherFinalBS :: CipherCtx -> IO B8.ByteString+cipherFinalBS ctx =+  withCipherCtxPtr ctx $ \ctxPtr ->+    let len = fromIntegral $ _cipher_ctx_block_size ctxPtr in+      B8.createAndTrim len $ \outBuf ->+        alloca $ \outLenPtr ->+          _CipherFinal ctxPtr (castPtr outBuf) outLenPtr+            >>= failIf (/= 1)+            >>  fromIntegral <$> peek outLenPtr++cipherStrictly :: CipherCtx -> B8.ByteString -> IO B8.ByteString+cipherStrictly ctx input = do+  output'  <- cipherUpdateBS ctx input+  output'' <- cipherFinalBS ctx+  return $ B8.append output' output''++cipherLazily :: CipherCtx -> L8.ByteString -> IO L8.ByteString+cipherLazily ctx (L8.Empty) =+  cipherFinalBS ctx >>= return . L8.fromChunks . return+cipherLazily ctx (L8.Chunk x xs) = do+  y  <- cipherUpdateBS ctx x+  ys <- unsafeInterleaveIO $ cipherLazily ctx xs+  return $ L8.Chunk y ys++{- EVP_MD -------------------------------------------------------------------- -}++-- |@Digest@ is an opaque object that represents an algorithm of+-- message digest.+newtype Digest = Digest (Ptr EVP_MD)+data    EVP_MD++withMDPtr :: Digest -> (Ptr EVP_MD -> IO a) -> IO a+withMDPtr (Digest mdPtr) f = f mdPtr++{- EVP_MD_CTX ---------------------------------------------------------------- -}++newtype DigestCtx  = DigestCtx (ForeignPtr EVP_MD_CTX)+data    EVP_MD_CTX++foreign import ccall unsafe "EVP_MD_CTX_init"+  _md_ctx_init :: Ptr EVP_MD_CTX -> IO ()++foreign import ccall unsafe "&EVP_MD_CTX_cleanup"+  _md_ctx_cleanup :: FunPtr (Ptr EVP_MD_CTX -> IO ())++newDigestCtx :: IO DigestCtx+newDigestCtx = do+  ctx <- mallocForeignPtrBytes (#size EVP_MD_CTX)+  mask_ $ do+    withForeignPtr ctx _md_ctx_init+    addForeignPtrFinalizer _md_ctx_cleanup ctx+  return $ DigestCtx ctx++withDigestCtxPtr :: DigestCtx -> (Ptr EVP_MD_CTX -> IO a) -> IO a+withDigestCtxPtr (DigestCtx ctx) = withForeignPtr ctx++{- digest -------------------------------------------------------------------- -}++foreign import ccall unsafe "EVP_DigestInit"+  _DigestInit :: Ptr EVP_MD_CTX -> Ptr EVP_MD -> IO CInt++digestInit :: Digest -> IO DigestCtx+digestInit (Digest md) = do+  ctx <- newDigestCtx+  withDigestCtxPtr ctx $ \ctxPtr ->+    _DigestInit ctxPtr md+      >>= failIf_ (/= 1)+      >>  return ctx++foreign import ccall unsafe "EVP_DigestUpdate"+  _DigestUpdate :: Ptr EVP_MD_CTX -> Ptr CChar -> CSize -> IO CInt++digestUpdateBS :: DigestCtx -> B8.ByteString -> IO ()+digestUpdateBS ctx bs =+  withDigestCtxPtr ctx $ \ctxPtr ->+    B8.unsafeUseAsCStringLen bs $ \(buf, len) ->+      _DigestUpdate ctxPtr buf (fromIntegral len)+        >>= failIf (/= 1)+        >>  return ()++foreign import ccall unsafe "EVP_DigestFinal"+  _DigestFinal :: Ptr EVP_MD_CTX -> Ptr CChar -> Ptr CUInt -> IO CInt++digestFinalBS :: DigestCtx -> IO B8.ByteString+digestFinalBS ctx =+  withDigestCtxPtr ctx $ \ctxPtr ->+    B8.createAndTrim (#const EVP_MAX_MD_SIZE) $ \bufPtr ->+      alloca $ \bufLenPtr -> do+        _DigestFinal ctxPtr (castPtr bufPtr) bufLenPtr >>= failIf_ (/= 1)+        fromIntegral <$> peek bufLenPtr++digestFinal :: DigestCtx -> IO String+digestFinal ctx =+  withDigestCtxPtr ctx $ \ctxPtr ->+    allocaArray (#const EVP_MAX_MD_SIZE) $ \bufPtr ->+      alloca $ \bufLenPtr -> do+        _DigestFinal ctxPtr bufPtr bufLenPtr >>= failIf_ (/= 1)+        bufLen <- fromIntegral <$> peek bufLenPtr+        peekCStringLen (bufPtr, bufLen)++digestStrictly :: Digest -> B8.ByteString -> IO DigestCtx+digestStrictly md input = do+  ctx <- digestInit md+  digestUpdateBS ctx input+  return ctx++digestLazily :: Digest -> L8.ByteString -> IO DigestCtx+digestLazily md lbs = do+  ctx <- digestInit md+  mapM_ (digestUpdateBS ctx) $ L8.toChunks lbs+  return ctx++{- EVP_PKEY ------------------------------------------------------------------ -}++-- VaguePKey is a ForeignPtr to EVP_PKEY, that is either public key or+-- a ker pair. We can't tell which at compile time.+newtype VaguePKey = VaguePKey (ForeignPtr EVP_PKEY)+data    EVP_PKEY++-- Instances of class PKey can be converted back and forth to+-- VaguePKey.+class PKey k where+    -- Wrap the key (i.g. RSA) into EVP_PKEY.+    toPKey        :: k -> IO VaguePKey++    -- Extract the concrete key from the EVP_PKEY. Returns Nothing if+    -- the type mismatches.+    fromPKey      :: VaguePKey -> IO (Maybe k)++    -- Do the same as EVP_PKEY_size().+    pkeySize      :: k -> Int++    -- Return the default digesting algorithm for the key.+    pkeyDefaultMD :: k -> IO Digest++foreign import ccall unsafe "EVP_PKEY_new"+  _pkey_new :: IO (Ptr EVP_PKEY)++foreign import ccall unsafe "&EVP_PKEY_free"+  _pkey_free :: FunPtr (Ptr EVP_PKEY -> IO ())++foreign import ccall unsafe "EVP_PKEY_free"+  _pkey_free' :: Ptr EVP_PKEY -> IO ()++wrapPKeyPtr :: Ptr EVP_PKEY -> IO VaguePKey+wrapPKeyPtr = fmap VaguePKey . newForeignPtr _pkey_free++createPKey :: (Ptr EVP_PKEY -> IO a) -> IO VaguePKey+createPKey f = mask $ \restore -> do+  ptr <- _pkey_new >>= failIfNull+  (restore $ f ptr >> return ()) `onException` _pkey_free' ptr+  wrapPKeyPtr ptr++withPKeyPtr :: VaguePKey -> (Ptr EVP_PKEY -> IO a) -> IO a+withPKeyPtr (VaguePKey pkey) = withForeignPtr pkey++withPKeyPtr' :: PKey k => k -> (Ptr EVP_PKEY -> IO a) -> IO a+withPKeyPtr' k f = do+  pk <- toPKey k+  withPKeyPtr pk f++unsafePKeyToPtr :: VaguePKey -> Ptr EVP_PKEY+unsafePKeyToPtr (VaguePKey pkey) = unsafeForeignPtrToPtr pkey++touchPKey :: VaguePKey -> IO ()+touchPKey (VaguePKey pkey) = touchForeignPtr pkey+
OpenSSL/EVP/Open.hsc view
@@ -16,6 +16,7 @@ import           Foreign.C import           OpenSSL.EVP.Cipher hiding (cipher) import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.Utils  @@ -31,7 +32,7 @@  openInit :: KeyPair key => Cipher -> String -> String -> key -> IO CipherCtx openInit cipher encKey iv pkey-    = do ctx <- newCtx+    = do ctx <- newCipherCtx          withCipherCtxPtr ctx $ \ ctxPtr ->              withCStringLen encKey $ \ (encKeyPtr, encKeyLen) ->                  withCString iv $ \ ivPtr ->
OpenSSL/EVP/PKey.hsc view
@@ -1,57 +1,28 @@ {- -*- haskell -*- -} -{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}  -- |An interface to asymmetric cipher keypair.  #include "HsOpenSSL.h"  module OpenSSL.EVP.PKey-    ( PublicKey(..)+    ( PKey+    , PublicKey(..)     , KeyPair(..)     , SomePublicKey     , SomeKeyPair--    -- private-    , PKey(..)-    , EVP_PKEY-    , withPKeyPtr-    , withPKeyPtr'-    , wrapPKeyPtr-    , unsafePKeyToPtr-    , touchPKey     )     where--import           Data.Typeable-import           Data.Maybe-import           Foreign-import           Foreign.C-import           OpenSSL.DSA-import           OpenSSL.EVP.Digest hiding (digest)-import           OpenSSL.RSA-import           OpenSSL.Utils---- VaguePKey is a ForeignPtr to EVP_PKEY, that is either public key or--- a ker pair. We can't tell which at compile time.-newtype VaguePKey = VaguePKey (ForeignPtr EVP_PKEY)-data    EVP_PKEY---- Instances of class PKey can be converted back and forth to--- VaguePKey.-class PKey k where-    -- Wrap the key (i.g. RSA) into EVP_PKEY.-    toPKey        :: k -> IO VaguePKey--    -- Extract the concrete key from the EVP_PKEY. Returns Nothing if-    -- the type mismatches.-    fromPKey      :: VaguePKey -> IO (Maybe k)--    -- Do the same as EVP_PKEY_size().-    pkeySize      :: k -> Int--    -- Return the default digesting algorithm for the key.-    pkeyDefaultMD :: k -> IO Digest+import Data.Typeable+import Data.Maybe+import Foreign+import Foreign.C+import OpenSSL.DSA+import OpenSSL.EVP.Digest+import OpenSSL.EVP.Internal+import OpenSSL.RSA+import OpenSSL.Utils  -- |Instances of this class has at least public portion of a -- keypair. They might or might not have the private key.@@ -175,35 +146,6 @@         = withConcreteKeyPair pk (return . Just . SomeKeyPair)  -foreign import ccall unsafe "EVP_PKEY_new"-        _pkey_new :: IO (Ptr EVP_PKEY)--foreign import ccall unsafe "&EVP_PKEY_free"-        _pkey_free :: FunPtr (Ptr EVP_PKEY -> IO ())---wrapPKeyPtr :: Ptr EVP_PKEY -> IO VaguePKey-wrapPKeyPtr-    = fmap VaguePKey . newForeignPtr _pkey_free---withPKeyPtr' :: PKey k => k -> (Ptr EVP_PKEY -> IO a) -> IO a-withPKeyPtr' k f = do pk <- toPKey k-                      withPKeyPtr pk f---withPKeyPtr :: VaguePKey -> (Ptr EVP_PKEY -> IO a) -> IO a-withPKeyPtr (VaguePKey pkey) = withForeignPtr pkey---unsafePKeyToPtr :: VaguePKey -> Ptr EVP_PKEY-unsafePKeyToPtr (VaguePKey pkey) = unsafeForeignPtrToPtr pkey---touchPKey :: VaguePKey -> IO ()-touchPKey (VaguePKey pkey) = touchForeignPtr pkey-- #ifndef OPENSSL_NO_RSA -- The resulting Ptr RSA must be freed by caller. foreign import ccall unsafe "EVP_PKEY_get1_RSA"@@ -215,10 +157,9 @@  rsaToPKey :: RSAKey k => k -> IO VaguePKey rsaToPKey rsa-    = withRSAPtr rsa $ \ rsaPtr ->-      do pkeyPtr <- _pkey_new >>= failIfNull-         _set1_RSA pkeyPtr rsaPtr >>= failIf_ (/= 1)-         wrapPKeyPtr pkeyPtr+    = withRSAPtr rsa $ \rsaPtr ->+        createPKey $ \pkeyPtr ->+          _set1_RSA pkeyPtr rsaPtr >>= failIf_ (/= 1)  rsaFromPKey :: RSAKey k => VaguePKey -> IO (Maybe k) rsaFromPKey pk@@ -255,10 +196,9 @@  dsaToPKey :: DSAKey k => k -> IO VaguePKey dsaToPKey dsa-    = withDSAPtr dsa $ \ dsaPtr ->-      do pkeyPtr <- _pkey_new >>= failIfNull-         _set1_DSA pkeyPtr dsaPtr >>= failIf_ (/= 1)-         wrapPKeyPtr pkeyPtr+    = withDSAPtr dsa $ \dsaPtr ->+        createPKey $ \pkeyPtr ->+          _set1_DSA pkeyPtr dsaPtr >>= failIf_ (/= 1)  dsaFromPKey :: DSAKey k => VaguePKey -> IO (Maybe k) dsaFromPKey pk
OpenSSL/EVP/Seal.hsc view
@@ -16,6 +16,7 @@ import           Foreign.C import           OpenSSL.EVP.Cipher hiding (cipher) import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.Utils  @@ -36,7 +37,7 @@     = fail "sealInit: at least one public key is required"  sealInit cipher pubKeys-    = do ctx <- newCtx+    = do ctx <- newCipherCtx           -- Allocate a list of buffers to write encrypted symmetric          -- keys. Each keys will be at most pkeySize bytes long.
OpenSSL/EVP/Sign.hsc view
@@ -7,35 +7,33 @@     ( sign     , signBS     , signLBS-    , signFinal -- private     )     where -import           Control.Monad import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Internal as B8 import qualified Data.ByteString.Lazy.Char8 as L8+import           Control.Applicative ((<$>)) import           Foreign import           Foreign.C import           OpenSSL.EVP.Digest import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.Utils - foreign import ccall unsafe "EVP_SignFinal"-        _SignFinal :: Ptr EVP_MD_CTX -> Ptr Word8 -> Ptr CUInt -> Ptr EVP_PKEY -> IO CInt-+  _SignFinal :: Ptr EVP_MD_CTX -> Ptr Word8 -> Ptr CUInt+             -> Ptr EVP_PKEY -> IO CInt  signFinal :: KeyPair k => DigestCtx -> k -> IO B8.ByteString-signFinal ctx k-    = do let maxLen = pkeySize k-         withDigestCtxPtr ctx $ \ ctxPtr ->-             withPKeyPtr' k $ \ pkeyPtr ->-                 B8.createAndTrim maxLen $ \ bufPtr ->-                     alloca $ \ bufLenPtr ->-                         do failIf_ (/= 1) =<< _SignFinal ctxPtr bufPtr bufLenPtr pkeyPtr-                            liftM fromIntegral $ peek bufLenPtr-+signFinal ctx k = do+  let maxLen = pkeySize k+  withDigestCtxPtr ctx $ \ ctxPtr ->+    withPKeyPtr' k $ \ pkeyPtr ->+      B8.createAndTrim maxLen $ \ bufPtr ->+        alloca $ \ bufLenPtr -> do+          failIf_ (/= 1) =<< _SignFinal ctxPtr bufPtr bufLenPtr pkeyPtr+          fromIntegral <$> peek bufLenPtr  -- |@'sign'@ generates a signature from a stream of data. The string -- must not contain any letters which aren't in the range of U+0000 -@@ -46,7 +44,7 @@      -> String    -- ^ input string      -> IO String -- ^ the result signature sign md pkey input-    = liftM L8.unpack $ signLBS md pkey $ L8.pack input+    = fmap L8.unpack $ signLBS md pkey $ L8.pack input  -- |@'signBS'@ generates a signature from a chunk of data. signBS :: KeyPair key =>@@ -68,3 +66,4 @@     = do ctx <- digestLazily md input          sig <- signFinal ctx pkey          return $ L8.fromChunks [sig]+
OpenSSL/EVP/Verify.hsc view
@@ -18,6 +18,7 @@ import           Foreign.C import           OpenSSL.EVP.Digest import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.Utils  -- |@'VerifyStatus'@ represents a result of verification.
OpenSSL/PEM.hsc view
@@ -48,6 +48,7 @@ import           OpenSSL.BIO import           OpenSSL.EVP.Cipher hiding (cipher) import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.DH.Internal import           OpenSSL.PKCS7 import           OpenSSL.Utils
OpenSSL/PKCS7.hsc view
@@ -35,6 +35,7 @@ import           OpenSSL.BIO import           OpenSSL.EVP.Cipher hiding (cipher) import           OpenSSL.EVP.PKey+import           OpenSSL.EVP.Internal import           OpenSSL.Stack import           OpenSSL.Utils import           OpenSSL.X509
OpenSSL/Session.hsc view
@@ -48,27 +48,21 @@      -- * SSL Exceptions   , SomeSSLException-  , ConnectionCleanlyClosed   , ConnectionAbruptlyTerminated-  , WantConnect-  , WantAccept-  , WantX509Lookup-  , SSLIOError-  , ProtocolError-  , UnknownError(..)+  , ProtocolError(..)   ) where  #include "openssl/ssl.h"  import Prelude hiding (catch, read, ioError, mapM, mapM_)-import Control.Concurrent (threadWaitWrite, threadWaitRead)+import Control.Concurrent (threadWaitWrite, threadWaitRead, runInBoundThread) import Control.Concurrent.QSem import Control.Exception import Control.Applicative ((<$>), (<$)) import Control.Monad (unless) import Data.Typeable import Data.Foldable (Foldable, mapM_, forM_)-import Data.Traversable (Traversable, mapM, forM)+import Data.Traversable (Traversable, mapM) import Data.Maybe (fromMaybe) import Data.IORef import Foreign@@ -79,12 +73,13 @@ import qualified Data.ByteString.Unsafe as B import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Internal as L-import System.IO.Error (mkIOError, eofErrorType, isEOFError) import System.IO.Unsafe import System.Posix.Types (Fd(..)) import Network.Socket (Socket(..)) +import OpenSSL.ERR import OpenSSL.EVP.PKey+import OpenSSL.EVP.Internal import OpenSSL.Utils import OpenSSL.X509 (X509, X509_, wrapX509, withX509Ptr) import OpenSSL.X509.Store@@ -331,46 +326,52 @@ foreign import ccall "SSL_connect" _ssl_connect :: Ptr SSL_ -> IO CInt foreign import ccall unsafe "SSL_get_error" _ssl_get_error :: Ptr SSL_ -> CInt -> IO CInt -throwSSLException :: CInt -> IO a-throwSSLException (#const SSL_ERROR_ZERO_RETURN     ) = throwIO ConnectionCleanlyClosed-throwSSLException (#const SSL_ERROR_WANT_CONNECT    ) = throwIO WantConnect-throwSSLException (#const SSL_ERROR_WANT_ACCEPT     ) = throwIO WantAccept-throwSSLException (#const SSL_ERROR_WANT_X509_LOOKUP) = throwIO WantX509Lookup-throwSSLException (#const SSL_ERROR_SYSCALL         ) = throwIO SSLIOError-throwSSLException (#const SSL_ERROR_SSL             ) = throwIO ProtocolError-throwSSLException x = throwIO (UnknownError (fromIntegral x))+throwSSLException :: String -> CInt -> IO a+throwSSLException loc ret+    = do e <- getError+         if e == 0 then+             case ret of+               0 -> throwIO ConnectionAbruptlyTerminated+               _ -> throwErrno loc+           else+             errorString e >>= throwIO . ProtocolError --- | This is the type of an SSL IO operation. EOF and termination are handled---   by exceptions while everything else is one of these. Note that reading---   from an SSL socket can result in WantWrite and vice versa.+-- | This is the type of an SSL IO operation. Errors are handled by+-- exceptions while everything else is one of these. Note that reading+-- from an SSL socket can result in WantWrite and vice versa. data SSLResult a = SSLDone a  -- ^ operation finished successfully-                 | WantRead  -- ^ needs more data from the network+                 | WantRead   -- ^ needs more data from the network                  | WantWrite  -- ^ needs more outgoing buffer space                  deriving (Eq, Show, Functor, Foldable, Traversable)  -- | Block until the operation is finished. sslBlock :: (SSL -> IO (SSLResult a)) -> SSL -> IO a-sslBlock action ssl = do-  result <- action ssl-  case result of-    SSLDone r -> return r-    WantRead  -> threadWaitRead  (sslFd ssl) >> sslBlock action ssl-    WantWrite -> threadWaitWrite (sslFd ssl) >> sslBlock action ssl+sslBlock action ssl+    = do result <- action ssl+         case result of+           SSLDone r -> return r+           WantRead  -> threadWaitRead  (sslFd ssl) >> sslBlock action ssl+           WantWrite -> threadWaitWrite (sslFd ssl) >> sslBlock action ssl  -- | Perform an SSL operation which can return non-blocking error codes, thus --   requesting that the operation be performed when data or buffer space is --   availible.-sslTryHandshake :: (Ptr SSL_ -> IO CInt) -> SSL -> IO (SSLResult CInt)-sslTryHandshake action = flip withSSL $ \pSsl -> do-  n <- action pSsl-  if n >= 0-    then return $ SSLDone n-    else do-      err <- _ssl_get_error pSsl n-      case err of-        (#const SSL_ERROR_WANT_READ)  -> return WantRead-        (#const SSL_ERROR_WANT_WRITE) -> return WantWrite-        _ -> throwSSLException err+sslTryHandshake :: String+                -> (Ptr SSL_ -> IO CInt)+                -> SSL+                -> IO (SSLResult CInt)+sslTryHandshake loc action ssl+    = runInBoundThread $+      withSSL ssl $ \sslPtr ->+      do n <- action sslPtr+         if n == 1 then+             return $ SSLDone n+           else+             do err <- _ssl_get_error sslPtr n+                case err of+                  (#const SSL_ERROR_WANT_READ ) -> return WantRead+                  (#const SSL_ERROR_WANT_WRITE) -> return WantWrite+                  _ -> throwSSLException loc n  -- | Perform an SSL server handshake accept :: SSL -> IO ()@@ -378,9 +379,8 @@  -- | Try to perform an SSL server handshake without blocking tryAccept :: SSL -> IO (SSLResult ())-tryAccept ssl = do-  result <- sslTryHandshake _ssl_accept ssl-  forM result $ failIf_ (/= 1)+tryAccept ssl+    = (() <$) <$> sslTryHandshake "SSL_accept" _ssl_accept ssl  -- | Perform an SSL client handshake connect :: SSL -> IO ()@@ -388,79 +388,78 @@  -- | Try to perform an SSL client handshake without blocking tryConnect :: SSL -> IO (SSLResult ())-tryConnect ssl = do-  result <- sslTryHandshake _ssl_connect ssl-  forM result $ failIf_ (/= 1)+tryConnect ssl+    = (() <$) <$> sslTryHandshake "SSL_connect" _ssl_connect ssl  foreign import ccall "SSL_read" _ssl_read :: Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt foreign import ccall unsafe "SSL_get_shutdown" _ssl_get_shutdown :: Ptr SSL_ -> IO CInt  -- | Perform an SSL operation which operates of a buffer and can return --   non-blocking error codes, thus requesting that it be performed again when---   more data or buffer space is availible.+--   more data or buffer space is available. -- --   Note that these SSL functions generally require that the arguments to the --   repeated call be exactly the same. This presents an issue because multiple --   threads could try writing at the same time (with different buffers) so the --   calling function should probably hold the lock on the SSL object over the --   whole time (include repeated calls)-sslIOInner :: (Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt)  -- ^ the SSL IO function to call+sslIOInner :: String -- ^ the name of SSL IO function to call+           -> (Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt)  -- ^ the SSL IO function to call            -> Ptr CChar  -- ^ the buffer to pass            -> Int  -- ^ the length to pass-           -> Ptr SSL_+           -> SSL            -> IO (SSLResult CInt)-sslIOInner f ptr nbytes ssl = do-  n <- f ssl (castPtr ptr) $ fromIntegral nbytes-  case n of-       n | n > 0 -> return $ SSLDone $ fromIntegral n-         | n == 0 -> do-           shutdown <- _ssl_get_shutdown ssl-           if shutdown .&. (#const SSL_RECEIVED_SHUTDOWN) == 0-              then throw ConnectionAbruptlyTerminated-              else ioError $ mkIOError eofErrorType  "" Nothing Nothing-       _ -> do-           err <- _ssl_get_error ssl n-           case err of-                (#const SSL_ERROR_WANT_READ) -> return WantRead-                (#const SSL_ERROR_WANT_WRITE) -> return WantWrite-                _ -> throwSSLException err--catchEOF :: a -> IO a -> IO a-catchEOF x m = m `catch` \e -> if isEOFError e then return x else throwIO e+sslIOInner loc f ptr nbytes ssl+    = runInBoundThread $+      withSSL ssl      $ \sslPtr ->+      do n <- f sslPtr (castPtr ptr) $ fromIntegral nbytes+         if n > 0 then+             return $ SSLDone $ fromIntegral n+           else+             do err <- _ssl_get_error sslPtr n+                case err of+                  (#const SSL_ERROR_ZERO_RETURN) -> return $ SSLDone $ 0+                  (#const SSL_ERROR_WANT_READ  ) -> return WantRead+                  (#const SSL_ERROR_WANT_WRITE ) -> return WantWrite+                  _ -> throwSSLException loc n  -- | Try to read the given number of bytes from an SSL connection. On EOF an --   empty ByteString is returned. If the connection dies without a graceful --   SSL shutdown, an exception is raised. read :: SSL -> Int -> IO B.ByteString-read ssl nbytes = B.createAndTrim nbytes $ \ptr -> do-  let doRead = withSSL ssl $ sslIOInner _ssl_read (castPtr ptr) nbytes-  catchEOF 0 $ fromIntegral <$> sslBlock (const doRead) ssl+read ssl nBytes = sslBlock (`tryRead` nBytes) ssl  -- | Try to read the given number of bytes from an SSL connection --   without blocking. tryRead :: SSL -> Int -> IO (SSLResult B.ByteString)-tryRead ssl nbytes = do-  (bs, result) <- B.createAndTrim' nbytes $ \ptr -> do-    result <- catchEOF (SSLDone 0) $ withSSL ssl $-                sslIOInner _ssl_read (castPtr ptr) nbytes-    return $ case result of-      SSLDone n -> (0, fromIntegral n, SSLDone ())-      WantRead  -> (0, 0,              WantRead)-      WantWrite -> (0, 0,              WantWrite)-  return $ bs <$ result+tryRead ssl nBytes+    = do (bs, result) <- B.createAndTrim' nBytes $ \bufPtr ->+                         do result <- sslIOInner "SSL_read" _ssl_read (castPtr bufPtr) nBytes ssl+                            case result of+                              SSLDone n -> return (0, fromIntegral n, SSLDone ())+                              WantRead  -> return (0,              0, WantRead  )+                              WantWrite -> return (0,              0, WantWrite )+         return $ bs <$ result  foreign import ccall "SSL_write" _ssl_write :: Ptr SSL_ -> Ptr Word8 -> CInt -> IO CInt  -- | Write a given ByteString to the SSL connection. Either all the data is---   written or an exception is raised because of an error+--   written or an exception is raised because of an error. write :: SSL -> B.ByteString -> IO () write ssl bs = sslBlock (`tryWrite` bs) ssl >> return ()  -- | Try to write a given ByteString to the SSL connection without blocking. tryWrite :: SSL -> B.ByteString -> IO (SSLResult ())-tryWrite ssl bs =-  B.unsafeUseAsCStringLen bs $ \(ptr, len) ->-    ((() <$) <$>) $ withSSL ssl $ sslIOInner _ssl_write ptr len+tryWrite ssl bs+    | B.null bs = return $ SSLDone ()+    | otherwise+        = B.unsafeUseAsCStringLen bs $ \(ptr, len) ->+          do result <- sslIOInner "SSL_write" _ssl_write ptr len ssl+             case result of+               SSLDone 0 -> ioError $ errnoToIOError "SSL_write" ePIPE Nothing Nothing+               SSLDone _ -> return $ SSLDone ()+               WantRead  -> return WantRead+               WantWrite -> return WantWrite  -- | Lazily read all data until reaching EOF. If the connection dies --   without a graceful SSL shutdown, an exception is raised.@@ -491,6 +490,7 @@  data ShutdownType = Bidirectional  -- ^ wait for the peer to also shutdown                   | Unidirectional  -- ^ only send our shutdown+                    deriving (Eq, Show)  -- | Cleanly shutdown an SSL connection. Note that SSL has a concept of a --   secure shutdown, which is distinct from just closing the TCP connection.@@ -503,14 +503,33 @@  -- | Try to cleanly shutdown an SSL connection without blocking. tryShutdown :: SSL -> ShutdownType -> IO (SSLResult ())-tryShutdown ssl ty = do-  result <- sslTryHandshake _ssl_shutdown ssl-  case result of-    SSLDone n -> case ty of-      Bidirectional | n /= 1 -> tryShutdown ssl ty-      _ -> return $ SSLDone ()-    WantRead  -> return WantRead-    WantWrite -> return WantWrite+tryShutdown ssl ty+    = runInBoundThread $+      withSSL ssl $ \sslPtr ->+      do n <- _ssl_shutdown sslPtr+         case n of+           1 -> return $ SSLDone ()+           0 -> if ty == Bidirectional then+                    tryShutdown ssl ty+                else+                    return $ SSLDone ()+           _ -> do err <- _ssl_get_error sslPtr n+                   case err of+                     (#const SSL_ERROR_WANT_READ ) -> return WantRead+                     (#const SSL_ERROR_WANT_WRITE) -> return WantWrite+                     -- SSL_ERROR_SYSCALL/-1 happens when we are+                     -- trying to send the remote peer a "close+                     -- notify" alert but the underlying socket was+                     -- closed at the time. We don't treat this an+                     -- error /if and only if/ we have already+                     -- received a "close notify" from the peer.+                     (#const SSL_ERROR_SYSCALL)+                         -> do sd <- _ssl_get_shutdown sslPtr+                               if sd .&. (#const SSL_RECEIVED_SHUTDOWN) == 0 then+                                   throwSSLException "SSL_shutdown" n+                                 else+                                   return $ SSLDone ()+                     _   -> throwSSLException "SSL_shutdown" n  foreign import ccall "SSL_get_peer_certificate" _ssl_get_peer_cert :: Ptr SSL_ -> IO (Ptr X509_) @@ -559,20 +578,6 @@     = do SomeSSLException a <- fromException x          cast a --- | The TLS\/SSL connection has been closed. If the protocol version--- is SSL 3.0 or TLS 1.0, this result code is returned only if a--- closure alert has occurred in the protocol, i.e. if the connection--- has been closed cleanly. Note that in this case--- 'ConnectionCleanlyClosed' does not necessarily indicate that the--- underlying transport has been closed.-data ConnectionCleanlyClosed-    = ConnectionCleanlyClosed-      deriving (Typeable, Show, Eq)--instance Exception ConnectionCleanlyClosed where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException- -- | The peer uncleanly terminated the connection without sending the -- \"close notify\" alert. data ConnectionAbruptlyTerminated@@ -583,76 +588,12 @@     toException   = sslExceptionToException     fromException = sslExceptionFromException --- | The operation did not complete; the same TLS\/SSL I\/O function--- should be called again later. The underlying socket was not--- connected yet to the peer and the call would block in--- 'connect'. The SSL function should be called again when the--- connection is established. This message can only appear with--- 'connect'.-data WantConnect-    = WantConnect-      deriving (Typeable, Show, Eq)--instance Exception WantConnect where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException---- | The operation did not complete; the same TLS\/SSL I\/O function--- should be called again later. The underlying socket was not--- connected yet to the peer and the call would block in 'accept'. The--- SSL function should be called again when the connection is--- established. This message can only appear with 'accept'.-data WantAccept-    = WantAccept-      deriving (Typeable, Show, Eq)--instance Exception WantAccept where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException---- | The operation did not complete because an application callback--- set by SSL_CTX_set_client_cert_cb() has asked to be called--- again. The TLS\/SSL I\/O function should be called again--- later. Details depend on the application.-data WantX509Lookup-    = WantX509Lookup-      deriving (Typeable, Show, Eq)--instance Exception WantX509Lookup where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException---- | Some I\/O error occurred. The OpenSSL error queue may contain--- more information on the error. If the error queue is empty--- (i.e. ERR_get_error() returns 0), ret can be used to find out more--- about the error: If ret == 0, an EOF was observed that violates the--- protocol. If ret == -1, the underlying BIO reported an I\/O error--- (for socket I\/O on Unix systems, consult errno for details).-data SSLIOError-    = SSLIOError-      deriving (Typeable, Show, Eq)--instance Exception SSLIOError where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException- -- | A failure in the SSL library occurred, usually a protocol--- error. The OpenSSL error queue contains more information on the -- error. data ProtocolError-    = ProtocolError+    = ProtocolError !String       deriving (Typeable, Show, Eq)  instance Exception ProtocolError where-    toException   = sslExceptionToException-    fromException = sslExceptionFromException---- | SSL_get_error() returned an error code which is unknown to this--- library.-data UnknownError-    = UnknownError !Int-      deriving (Typeable, Show, Eq)--instance Exception UnknownError where     toException   = sslExceptionToException     fromException = sslExceptionFromException
OpenSSL/X509.hsc view
@@ -60,6 +60,7 @@ import           OpenSSL.EVP.Digest hiding (digest) import           OpenSSL.EVP.PKey import           OpenSSL.EVP.Verify+import           OpenSSL.EVP.Internal import           OpenSSL.Utils import           OpenSSL.Stack import           OpenSSL.X509.Name
OpenSSL/X509/Request.hsc view
@@ -41,6 +41,7 @@ import           OpenSSL.EVP.Digest hiding (digest) import           OpenSSL.EVP.PKey import           OpenSSL.EVP.Verify+import           OpenSSL.EVP.Internal import           OpenSSL.Utils import           OpenSSL.X509 (X509) import qualified OpenSSL.X509 as Cert
OpenSSL/X509/Revocation.hsc view
@@ -55,6 +55,7 @@ import OpenSSL.EVP.Digest hiding (digest) import OpenSSL.EVP.PKey import OpenSSL.EVP.Verify+import OpenSSL.EVP.Internal import OpenSSL.Stack import OpenSSL.Utils import OpenSSL.X509.Name
+ README.rst view
@@ -0,0 +1,8 @@+======+README+======++HsOpenSSL is an (incomplete) OpenSSL binding for Haskell. It can+generate RSA and DSA keys, read and write PEM files, generate message+digests, sign and verify messages, encrypt and decrypt messages. It+also has some capabilities of creating SSL clients and servers.