HsOpenSSL 0.2 → 0.3
raw patch · 12 files changed
+462/−59 lines, 12 files
Files
- HsOpenSSL.cabal +4/−2
- NEWS +14/−0
- OpenSSL.hsc +4/−9
- OpenSSL/ASN1.hsc +4/−4
- OpenSSL/BN.hsc +188/−22
- OpenSSL/Cipher.hsc +116/−0
- OpenSSL/DSA.hsc +28/−19
- OpenSSL/EVP/Digest.hsc +24/−0
- OpenSSL/EVP/PKey.hsc +20/−1
- OpenSSL/RSA.hsc +2/−2
- OpenSSL/Random.hsc +56/−0
- cbits/HsOpenSSL.h +2/−0
HsOpenSSL.cabal view
@@ -5,7 +5,7 @@ generate RSA and DSA keys, read and write PEM files, generate message digests, sign and verify messages, encrypt and decrypt messages.-Version: 0.2+Version: 0.3 License: PublicDomain License-File: COPYING Author: PHO <phonohawk at ps dot sakura dot ne dot jp>@@ -18,6 +18,7 @@ base, time >= 1.1.1 Exposed-Modules: OpenSSL+ OpenSSL.BN OpenSSL.EVP.Base64 OpenSSL.EVP.Cipher OpenSSL.EVP.Digest@@ -26,8 +27,10 @@ OpenSSL.EVP.Seal OpenSSL.EVP.Sign OpenSSL.EVP.Verify+ OpenSSL.Cipher OpenSSL.PEM OpenSSL.PKCS7+ OpenSSL.Random OpenSSL.DSA OpenSSL.RSA OpenSSL.X509@@ -37,7 +40,6 @@ Other-Modules: OpenSSL.ASN1 OpenSSL.BIO- OpenSSL.BN OpenSSL.ERR OpenSSL.Objects OpenSSL.SSL
NEWS view
@@ -1,3 +1,17 @@+Changes from 0.2 to 0.3+--------------------------------------+* Applied patches by Adam Langley:+ - tests/DSA.hs: Add a DSA test: this just adds a binary which tests+ a few simple DSA cases (and runs a timing test) and prints "PASS"+ as the last line of stdout in the case that everything looks good.+ It doesn't include any hooks nor framework for running these.+ - Bug fix for fast Integer<->BN functions+ - OpenSSL.Cipher: Add non-EVP cipher support+ - OpenSSL.EVP.Digest: Add HMAC support in EVP+ - OpenSSL.Random: Add OpenSSL.Random+ - OpenSSL.BN: Additional utility functions in BN and exposing BN++ Changes from 0.1.1. to 0.2 -------------------------- * Applied patches by Adam Langley:
OpenSSL.hsc view
@@ -11,9 +11,9 @@ -- [/TLS\/SSL network connection/] ssl(3) functionalities are -- totally uncovered. They should be covered someday. ----- [/Low-level API to symmetric ciphers/] Only high-level APIs (EVP--- and BIO) are available. But I believe no one will be lost without--- functions like @DES_set_odd_parity@.+-- [/Complete coverage of Low-level API to symmetric ciphers/] Only+-- high-level APIs (EVP and BIO) are fully available. But I believe+-- no one will be lost without functions like @DES_set_odd_parity@. -- -- [/Low-level API to asymmetric ciphers/] Only a high-level API -- (EVP) is available. But I believe no one will complain about the@@ -25,13 +25,8 @@ -- [/X.509 v3 extension handling/] It should be supported in the -- future. ----- [/HMAC message authentication/] ------ [/Low-level API to message digest functions/] Just use EVP or BIO+-- [/Low-level API to message digest functions/] Just use EVP -- instead of something like @MD5_Update@.------ [/pseudo-random number generator/] rand(3) functionalities are--- uncovered, but OpenSSL works very well by default. -- -- [/API to PKCS\#12 functionality/] It should be covered someday. --
OpenSSL/ASN1.hsc view
@@ -76,16 +76,16 @@ _ASN1_INTEGER_free :: Ptr ASN1_INTEGER -> IO () foreign import ccall unsafe "ASN1_INTEGER_to_BN"- _ASN1_INTEGER_to_BN :: Ptr ASN1_INTEGER -> BigNum -> IO BigNum+ _ASN1_INTEGER_to_BN :: Ptr ASN1_INTEGER -> Ptr BIGNUM -> IO (Ptr BIGNUM) foreign import ccall unsafe "BN_to_ASN1_INTEGER"- _BN_to_ASN1_INTEGER :: BigNum -> Ptr ASN1_INTEGER -> IO (Ptr ASN1_INTEGER)+ _BN_to_ASN1_INTEGER :: Ptr BIGNUM -> Ptr ASN1_INTEGER -> IO (Ptr ASN1_INTEGER) peekASN1Integer :: Ptr ASN1_INTEGER -> IO Integer peekASN1Integer intPtr = allocaBN $ \ bn ->- do _ASN1_INTEGER_to_BN intPtr bn+ do _ASN1_INTEGER_to_BN intPtr (unwrapBN bn) >>= failIfNull peekBN bn @@ -99,7 +99,7 @@ withASN1Integer int m = withBN int $ \ bn -> allocaASN1Integer $ \ intPtr ->- do _BN_to_ASN1_INTEGER bn intPtr+ do _BN_to_ASN1_INTEGER (unwrapBN bn) intPtr >>= failIfNull m intPtr
OpenSSL/BN.hsc view
@@ -1,29 +1,52 @@ #include "HsOpenSSL.h" +-- #prune++-- |BN - multiprecision integer arithmetics+ module OpenSSL.BN- ( BigNum+ ( -- * Type+ BigNum , BIGNUM + -- * Allocation , allocaBN , withBN- , peekBN+ , newBN+ , wrapBN -- private+ , unwrapBN -- private + -- * Conversion from\/to Integer+ , peekBN #ifdef __GLASGOW_HASKELL__ , integerToBN , bnToInteger #endif+ , integerToMPI+ , mpiToInteger++ -- * Computation+ , modexp++ -- * Random number generation+ , randIntegerUptoNMinusOneSuchThat+ , prandIntegerUptoNMinusOneSuchThat+ , randIntegerZeroToNMinusOne+ , prandIntegerZeroToNMinusOne+ , randIntegerOneToNMinusOne+ , prandIntegerOneToNMinusOne ) where import Control.Exception import Foreign-+import qualified Data.ByteString as BS+import OpenSSL.Utils #ifndef __GLASGOW_HASKELL__ import Control.Monad import Foreign.C-import OpenSSL.Utils #else import Foreign.C.Types import Data.Word (Word32)@@ -33,35 +56,47 @@ import GHC.IOBase (IO(..)) #endif -type BigNum = Ptr BIGNUM-data BIGNUM = BIGNUM+-- |'BigNum' is an opaque object representing a big number.+newtype BigNum = BigNum (Ptr BIGNUM)+data BIGNUM foreign import ccall unsafe "BN_new"- _new :: IO BigNum+ _new :: IO (Ptr BIGNUM) foreign import ccall unsafe "BN_free"- _free :: BigNum -> IO ()-+ _free :: Ptr BIGNUM -> IO () +-- |@'allocaBN' f@ allocates a 'BigNum' and computes @f@. Then it+-- frees the 'BigNum'. allocaBN :: (BigNum -> IO a) -> IO a allocaBN m- = bracket _new _free m+ = bracket _new _free (m . wrapBN) +unwrapBN :: BigNum -> Ptr BIGNUM+unwrapBN (BigNum p) = p+++wrapBN :: Ptr BIGNUM -> BigNum+wrapBN = BigNum++ #ifndef __GLASGOW_HASKELL__ {- slow, safe functions ----------------------------------------------------- -} foreign import ccall unsafe "BN_bn2dec"- _bn2dec :: BigNum -> IO CString+ _bn2dec :: Ptr BIGNUM -> IO CString foreign import ccall unsafe "BN_dec2bn"- _dec2bn :: Ptr BigNum -> CString -> IO Int+ _dec2bn :: Ptr (Ptr BIGNUM) -> CString -> IO Int foreign import ccall unsafe "HsOpenSSL_OPENSSL_free" _openssl_free :: Ptr a -> IO () +-- |@'withBN' n f@ converts n to a 'BigNum' and computes @f@. Then it+-- frees the 'BigNum'. withBN :: Integer -> (BigNum -> IO a) -> IO a withBN dec m = withCString (show dec) $ \ strPtr ->@@ -71,7 +106,7 @@ >>= failIf (== 0) bracket (peek bnPtr) _free m -+-- |@'peekBN' bn@ converts a 'BigNum' to an 'Prelude.Integer'. peekBN :: BigNum -> IO Integer peekBN bn = do strPtr <- _bn2dec bn@@ -127,11 +162,11 @@ -- | Convert a BIGNUM to an Integer bnToInteger :: BigNum -> IO Integer bnToInteger bn = do- nlimbs <- (#peek BIGNUM, top) bn :: IO CSize+ nlimbs <- (#peek BIGNUM, top) (unwrapBN bn) :: IO CSize case nlimbs of 0 -> return 0- 1 -> do (I## i) <- (#peek BIGNUM, d) bn >>= peek- negative <- (#peek BIGNUM, neg) bn :: IO Word32+ 1 -> do (I## i) <- (#peek BIGNUM, d) (unwrapBN bn) >>= peek+ negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO Word32 if negative == 0 then return $ S## i else return $ 0 - (S## i)@@ -140,9 +175,9 @@ (I## limbsize) = (#size unsigned long) (MBA arr) <- newByteArray (nlimbsi *## limbsize) (BA ba) <- freezeByteArray arr- limbs <- (#peek BIGNUM, d) bn+ limbs <- (#peek BIGNUM, d) (unwrapBN bn) _copy_in ba limbs $ fromIntegral $ nlimbs * (#size unsigned long)- negative <- (#peek BIGNUM, neg) bn :: IO Word32+ negative <- (#peek BIGNUM, neg) (unwrapBN bn) :: IO Word32 if negative == 0 then return $ J## nlimbsi ba else return $ 0 - (J## nlimbsi ba)@@ -150,6 +185,20 @@ -- | This is a GHC specific, fast conversion between Integers and OpenSSL -- bignums. It returns a malloced BigNum. integerToBN :: Integer -> IO BigNum+integerToBN 0 = do+ bnptr <- mallocBytes (#size BIGNUM)+ (#poke BIGNUM, d) bnptr nullPtr+ -- This is needed to give GHC enough type information+ let one :: Word32+ one = 1+ zero :: Word32+ zero = 0+ (#poke BIGNUM, flags) bnptr one+ (#poke BIGNUM, top) bnptr zero+ (#poke BIGNUM, dmax) bnptr zero+ (#poke BIGNUM, neg) bnptr zero+ return (wrapBN bnptr)+ integerToBN (S## v) = do bnptr <- mallocBytes (#size BIGNUM) limbs <- malloc :: IO (Ptr Word32)@@ -163,7 +212,7 @@ (#poke BIGNUM, top) bnptr one (#poke BIGNUM, dmax) bnptr one (#poke BIGNUM, neg) bnptr (if (I## v) < 0 then one else 0)- return bnptr+ return (wrapBN bnptr) integerToBN v@(J## nlimbs_ bytearray) | v >= 0 = do@@ -176,9 +225,9 @@ (#poke BIGNUM, top) bnptr ((fromIntegral nlimbs) :: Word32) (#poke BIGNUM, dmax) bnptr ((fromIntegral nlimbs) :: Word32) (#poke BIGNUM, neg) bnptr (0 :: Word32)- return bnptr+ return (wrapBN bnptr) | otherwise = do bnptr <- integerToBN (0-v)- (#poke BIGNUM, neg) bnptr (1 :: Word32)+ (#poke BIGNUM, neg) (unwrapBN bnptr) (1 :: Word32) return bnptr -- TODO: we could make a function which doesn't even allocate BN data if we@@ -186,12 +235,129 @@ -- Integer's data. However, I'm not sure about the semantics of the GC; which -- might move the Integer data around. +-- |@'withBN' n f@ converts n to a 'BigNum' and computes @f@. Then it+-- frees the 'BigNum'. withBN :: Integer -> (BigNum -> IO a) -> IO a-withBN dec m = bracket (integerToBN dec) _free m+withBN dec m = bracket (integerToBN dec) (_free . unwrapBN) m +-- |This is an alias to 'bnToInteger'. peekBN :: BigNum -> IO Integer peekBN = bnToInteger +-- |This is an alias to 'integerToBN'.+newBN :: Integer -> IO BigNum newBN = integerToBN +foreign import ccall unsafe "BN_bn2mpi"+ _bn2mpi :: Ptr BIGNUM -> Ptr CChar -> IO CInt++foreign import ccall unsafe "BN_mpi2bn"+ _mpi2bn :: Ptr CChar -> CInt -> Ptr BIGNUM -> IO (Ptr BIGNUM)+ #endif++-- | Convert a BigNum to an MPI: a serialisation of large ints which has a+-- 4-byte, big endian length followed by the bytes of the number in+-- most-significant-first order.+bnToMPI :: BigNum -> IO BS.ByteString+bnToMPI bn = do+ bytes <- _bn2mpi (unwrapBN bn) nullPtr+ allocaBytes (fromIntegral bytes) (\buffer -> do+ _bn2mpi (unwrapBN bn) buffer+ BS.copyCStringLen (buffer, fromIntegral bytes))++-- | Convert an MPI into a BigNum. See bnToMPI for details of the format+mpiToBN :: BS.ByteString -> IO BigNum+mpiToBN mpi = do+ BS.useAsCStringLen mpi (\(ptr, len) -> do+ _mpi2bn ptr (fromIntegral len) nullPtr) >>= return . wrapBN++-- | Convert an Integer to an MPI. SEe bnToMPI for the format+integerToMPI :: Integer -> IO BS.ByteString+integerToMPI v = bracket (integerToBN v) (_free . unwrapBN) bnToMPI++-- | Convert an MPI to an Integer. SEe bnToMPI for the format+mpiToInteger :: BS.ByteString -> IO Integer+mpiToInteger mpi = do+ bn <- mpiToBN mpi+ v <- bnToInteger bn+ _free (unwrapBN bn)+ return v++foreign import ccall unsafe "BN_mod_exp"+ _mod_exp :: Ptr BIGNUM -> Ptr BIGNUM -> Ptr BIGNUM -> Ptr BIGNUM -> BNCtx -> IO (Ptr BIGNUM)++type BNCtx = Ptr BNCTX+data BNCTX = BNCTX++foreign import ccall unsafe "BN_CTX_new"+ _BN_ctx_new :: IO BNCtx++foreign import ccall unsafe "BN_CTX_free"+ _BN_ctx_free :: BNCtx -> IO ()++withBNCtx :: (BNCtx -> IO a) -> IO a+withBNCtx f = bracket _BN_ctx_new _BN_ctx_free f++-- |@'modexp' a p m@ computes @a@ to the @p@-th power modulo @m@.+modexp :: Integer -> Integer -> Integer -> Integer+modexp a p m = unsafePerformIO (do+ withBN a (\bnA -> (do+ withBN p (\bnP -> (do+ withBN m (\bnM -> (do+ withBNCtx (\ctx -> (do+ r <- newBN 0+ _mod_exp (unwrapBN r) (unwrapBN bnA) (unwrapBN bnP) (unwrapBN bnM) ctx+ bnToInteger r >>= return)))))))))++{- Random Integer generation ------------------------------------------------ -}++foreign import ccall unsafe "BN_rand_range"+ _BN_rand_range :: Ptr BIGNUM -> Ptr BIGNUM -> IO CInt++foreign import ccall unsafe "BN_pseudo_rand_range"+ _BN_pseudo_rand_range :: Ptr BIGNUM -> Ptr BIGNUM -> IO CInt++-- | Return a strongly random number in the range 0 <= x < n where the given+-- filter function returns true.+randIntegerUptoNMinusOneSuchThat :: (Integer -> Bool) -- ^ a filter function+ -> Integer -- ^ one plus the upper limit+ -> IO Integer+randIntegerUptoNMinusOneSuchThat f range = withBN range (\bnRange -> (do+ r <- newBN 0+ let try = do+ _BN_rand_range (unwrapBN r) (unwrapBN bnRange) >>= failIf (/= 1)+ i <- bnToInteger r+ if f i+ then return i+ else try+ try))++-- | Return a random number in the range 0 <= x < n where the given+-- filter function returns true.+prandIntegerUptoNMinusOneSuchThat :: (Integer -> Bool) -- ^ a filter function+ -> Integer -- ^ one plus the upper limit+ -> IO Integer+prandIntegerUptoNMinusOneSuchThat f range = withBN range (\bnRange -> (do+ r <- newBN 0+ let try = do+ _BN_rand_range (unwrapBN r) (unwrapBN bnRange) >>= failIf (/= 1)+ i <- bnToInteger r+ if f i+ then return i+ else try+ try))++-- | Return a strongly random number in the range 0 <= x < n+randIntegerZeroToNMinusOne :: Integer -> IO Integer+randIntegerZeroToNMinusOne = randIntegerUptoNMinusOneSuchThat (const True)+-- | Return a strongly random number in the range 0 < x < n+randIntegerOneToNMinusOne :: Integer -> IO Integer+randIntegerOneToNMinusOne = randIntegerUptoNMinusOneSuchThat (/= 0)++-- | Return a random number in the range 0 <= x < n+prandIntegerZeroToNMinusOne :: Integer -> IO Integer+prandIntegerZeroToNMinusOne = prandIntegerUptoNMinusOneSuchThat (const True)+-- | Return a random number in the range 0 < x < n+prandIntegerOneToNMinusOne :: Integer -> IO Integer+prandIntegerOneToNMinusOne = prandIntegerUptoNMinusOneSuchThat (/= 0)
+ OpenSSL/Cipher.hsc view
@@ -0,0 +1,116 @@+#include "HsOpenSSL.h"+#include "openssl/aes.h"++-- | This module interfaces to some of the OpenSSL ciphers without using+-- EVP (see OpenSSL.EVP.Cipher). The EVP ciphers are easier to use,+-- however, in some cases you cannot do without using the OpenSSL+-- fuctions directly.+--+-- One of these cases (and the motivating example+-- for this module) is that the EVP CBC functions try to encode the+-- length of the input string in the output (thus hiding the fact that the+-- cipher is, in fact, block based and needs padding). This means that the+-- EVP CBC functions cannot, in some cases, interface with other users+-- which don't use that system (like SSH).+module OpenSSL.Cipher+ ( Mode(..)+ , newAESCtx+ , aesCBC+ , aesCTR)+ where++import Control.Monad (when)+import Data.IORef+import Foreign+import Foreign.C.Types+import qualified Data.ByteString as BS+import qualified Data.ByteString.Base as BSB+import OpenSSL.Utils++data Mode = Encrypt | Decrypt deriving (Eq, Show)++modeToInt Encrypt = 1+modeToInt Decrypt = 0++data AES_KEY+data AESCtx = AESCtx+ (ForeignPtr AES_KEY) -- the key schedule+ (ForeignPtr CUChar) -- the IV / counter+ (ForeignPtr CUChar) -- the encrypted counter (CTR mode)+ (IORef CUInt) -- the number of bytes of the encrypted counter used+ Mode++foreign import ccall unsafe "memcpy"+ _memcpy :: Ptr CUChar -> Ptr CChar -> CSize -> IO ()++foreign import ccall unsafe "memset"+ _memset :: Ptr CUChar -> CChar -> CSize -> IO ()++foreign import ccall unsafe "AES_set_encrypt_key"+ _AES_set_encrypt_key :: Ptr CChar -> CInt -> Ptr AES_KEY -> IO CInt+foreign import ccall unsafe "AES_set_decrypt_key"+ _AES_set_decrypt_key :: Ptr CChar -> CInt -> Ptr AES_KEY -> IO CInt++foreign import ccall unsafe "AES_cbc_encrypt"+ _AES_cbc_encrypt :: Ptr CChar -> Ptr Word8 -> CULong -> Ptr AES_KEY -> Ptr CUChar -> CInt -> IO ()++foreign import ccall unsafe "AES_ctr128_encrypt"+ _AES_ctr_encrypt :: Ptr CChar -> Ptr Word8 -> CULong -> Ptr AES_KEY -> Ptr CUChar -> Ptr CUChar -> Ptr CUInt -> IO ()++foreign import ccall unsafe "&free"+ _free :: FunPtr (Ptr a -> IO ())++-- | Construct a new context which holds the key schedule and IV.+newAESCtx :: Mode -- ^ For CTR mode, this must always be Encrypt+ -> BS.ByteString -- ^ Key: 128, 192 or 256 bits long+ -> BS.ByteString -- ^ IV: 16 bytes long+ -> IO AESCtx+newAESCtx mode key iv = do+ let keyLen = BS.length key * 8+ when (not $ any ((==) keyLen) [128, 192, 256]) $ fail "Bad AES key length"+ when (BS.length iv /= 16) $ fail "Bad AES128 iv length"+ ctx <- mallocForeignPtrBytes (#size AES_KEY)+ withForeignPtr ctx $ \ctxPtr ->+ BS.useAsCStringLen key (\(ptr, len) ->+ case mode of+ Encrypt -> _AES_set_encrypt_key ptr (fromIntegral keyLen) ctxPtr >>= failIf (/= 0)+ Decrypt -> _AES_set_decrypt_key ptr (fromIntegral keyLen) ctxPtr >>= failIf (/= 0))+ ivbytes <- mallocForeignPtrBytes 16+ ecounter <- mallocForeignPtrBytes 16+ nref <- newIORef 0+ withForeignPtr ecounter (\ecptr -> _memset ecptr 0 16)+ withForeignPtr ivbytes $ \ivPtr ->+ BS.useAsCStringLen iv $ \(ptr, len) ->+ do _memcpy ivPtr ptr 16+ return $ AESCtx ctx ivbytes ecounter nref mode++-- | Encrypt some number of blocks using CBC. This is an IO function because+-- the context is destructivly updated.+aesCBC :: AESCtx -- ^ context+ -> BS.ByteString -- ^ input, must be multiple of block size (16 bytes)+ -> IO BS.ByteString+aesCBC (AESCtx ctx iv _ _ mode) input = do+ when (BS.length input `mod` 16 /= 0) $ fail "Bad input length to aesCBC"+ withForeignPtr ctx $ \ctxPtr ->+ withForeignPtr iv $ \ivPtr ->+ BS.useAsCStringLen input $ \(ptr, len) ->+ BSB.create (BS.length input) $ \out ->+ _AES_cbc_encrypt ptr out (fromIntegral len) ctxPtr ivPtr $ modeToInt mode++-- | Encrypt some number of bytes using CTR mode. This is an IO function+-- because the context is destructivly updated.+aesCTR :: AESCtx -- ^ context+ -> BS.ByteString -- ^ input, any number of bytes+ -> IO BS.ByteString+aesCTR (AESCtx ctx iv ecounter nref Encrypt) input = do+ withForeignPtr ctx $ \ctxPtr ->+ withForeignPtr iv $ \ivPtr ->+ withForeignPtr ecounter $ \ecptr ->+ BS.useAsCStringLen input $ \(ptr, len) ->+ BSB.create (BS.length input) $ \out ->+ alloca $ \nptr -> do+ n <- readIORef nref+ poke nptr n+ _AES_ctr_encrypt ptr out (fromIntegral len) ctxPtr ivPtr ecptr nptr+ n' <- peek nptr+ writeIORef nref n'
OpenSSL/DSA.hsc view
@@ -1,5 +1,7 @@ {- -*- haskell -*- -} +-- #prune+ -- | The Digital Signature Algorithm (FIPS 186-2). -- See <http://www.openssl.org/docs/crypto/dsa.html> @@ -8,6 +10,8 @@ module OpenSSL.DSA ( -- * Type DSA+ , DSA_ -- private+ , withDSAPtr -- private -- * Key and parameter generation , generateParameters@@ -48,7 +52,7 @@ dsa_free :: Ptr DSA_ -> IO () foreign import ccall unsafe "BN_free"- _bn_free :: BigNum -> IO ()+ _bn_free :: Ptr BIGNUM -> IO () foreign import ccall unsafe "DSA_new" _dsa_new :: IO (Ptr DSA_)@@ -57,10 +61,10 @@ _dsa_generate_key :: Ptr DSA_ -> IO () foreign import ccall unsafe "HsOpenSSL_dsa_sign"- _dsa_sign :: Ptr DSA_ -> CString -> Int -> Ptr BigNum -> Ptr BigNum -> IO Int+ _dsa_sign :: Ptr DSA_ -> CString -> Int -> Ptr (Ptr BIGNUM) -> Ptr (Ptr BIGNUM) -> IO Int foreign import ccall unsafe "HsOpenSSL_dsa_verify"- _dsa_verify :: Ptr DSA_ -> CString -> Int -> BigNum -> BigNum -> IO Int+ _dsa_verify :: Ptr DSA_ -> CString -> Int -> Ptr BIGNUM -> Ptr BIGNUM -> IO Int withDSAPtr :: DSA -> (Ptr DSA_ -> IO a) -> IO a withDSAPtr (DSA ptr) = withForeignPtr ptr@@ -68,13 +72,13 @@ foreign import ccall safe "DSA_generate_parameters" _generate_params :: Int -> Ptr CChar -> Int -> Ptr CInt -> Ptr CInt -> Ptr () -> Ptr () -> IO (Ptr DSA_) -peekDSA :: (Ptr DSA_ -> IO BigNum) -> DSA -> IO (Maybe Integer)+peekDSA :: (Ptr DSA_ -> IO (Ptr BIGNUM)) -> DSA -> IO (Maybe Integer) peekDSA peeker (DSA dsa) = do withForeignPtr dsa (\ptr -> do bn <- peeker ptr if bn == nullPtr then return Nothing- else peekBN bn >>= return . Just)+ else peekBN (wrapBN bn) >>= return . Just) -- | Generate DSA parameters (*not* a key, but required for a key). This is a -- compute intensive operation. See FIPS 186-2, app 2. This agrees with the@@ -93,9 +97,9 @@ failIfNull ptr itcount <- peek i1 gencount <- peek i2- p <- (#peek DSA, p) ptr >>= peekBN- q <- (#peek DSA, q) ptr >>= peekBN- g <- (#peek DSA, g) ptr >>= peekBN+ p <- (#peek DSA, p) ptr >>= peekBN . wrapBN+ q <- (#peek DSA, q) ptr >>= peekBN . wrapBN+ g <- (#peek DSA, g) ptr >>= peekBN . wrapBN dsa_free ptr return (fromIntegral itcount, fromIntegral gencount, p, q, g)))) @@ -126,24 +130,29 @@ -> IO DSA generateKey p q g = do ptr <- _dsa_new- newBN p >>= (#poke DSA, p) ptr- newBN q >>= (#poke DSA, q) ptr- newBN g >>= (#poke DSA, g) ptr+ newBN p >>= return . unwrapBN >>= (#poke DSA, p) ptr+ newBN q >>= return . unwrapBN >>= (#poke DSA, q) ptr+ newBN g >>= return . unwrapBN >>= (#poke DSA, g) ptr _dsa_generate_key ptr newForeignPtr _free ptr >>= return . DSA +-- |Return the public prime number of the key. dsaP :: DSA -> IO (Maybe Integer) dsaP = peekDSA (#peek DSA, p) +-- |Return the public 160-bit subprime, @q | p-1@ of the key. dsaQ :: DSA -> IO (Maybe Integer) dsaQ = peekDSA (#peek DSA, q) +-- |Return the public generator of subgroup of the key. dsaG :: DSA -> IO (Maybe Integer) dsaG = peekDSA (#peek DSA, g) +-- |Return the public key @y = g^x@. dsaPublic :: DSA -> IO (Maybe Integer) dsaPublic = peekDSA (#peek DSA, pub_key) +-- |Return the private key @x@. dsaPrivate :: DSA -> IO (Maybe Integer) dsaPrivate = peekDSA (#peek DSA, priv_key) @@ -164,12 +173,12 @@ tupleToDSA :: (Integer, Integer, Integer, Integer, Maybe Integer) -> IO DSA tupleToDSA (p, q, g, pub, mpriv) = do ptr <- _dsa_new- newBN p >>= (#poke DSA, p) ptr- newBN q >>= (#poke DSA, q) ptr- newBN g >>= (#poke DSA, g) ptr- newBN pub >>= (#poke DSA, pub_key) ptr+ newBN p >>= return . unwrapBN >>= (#poke DSA, p) ptr+ newBN q >>= return . unwrapBN >>= (#poke DSA, q) ptr+ newBN g >>= return . unwrapBN >>= (#poke DSA, g) ptr+ newBN pub >>= return . unwrapBN >>= (#poke DSA, pub_key) ptr case mpriv of- Just priv -> newBN priv >>= (#poke DSA, priv_key) ptr+ Just priv -> newBN priv >>= return . unwrapBN >>= (#poke DSA, priv_key) ptr Nothing -> (#poke DSA, priv_key) ptr nullPtr newForeignPtr _free ptr >>= return . DSA @@ -197,9 +206,9 @@ alloca (\sptr -> do withDSAPtr dsa (\dsaptr -> do _dsa_sign dsaptr ptr len rptr sptr >>= failIf (== 0)- r <- peek rptr >>= peekBN+ r <- peek rptr >>= peekBN . wrapBN peek rptr >>= _bn_free- s <- peek sptr >>= peekBN+ s <- peek sptr >>= peekBN . wrapBN peek sptr >>= _bn_free return (r, s))))) @@ -210,4 +219,4 @@ withBN r (\bnR -> do withBN s (\bnS -> do withDSAPtr dsa (\dsaptr -> do- _dsa_verify dsaptr ptr len bnR bnS >>= return . (== 1)))))+ _dsa_verify dsaptr ptr len (unwrapBN bnR) (unwrapBN bnS) >>= return . (== 1)))))
OpenSSL/EVP/Digest.hsc view
@@ -24,11 +24,14 @@ , digest , digestBS , digestLBS++ , hmacBS ) where import Control.Monad import Data.ByteString.Base+import Data.ByteString (copyCStringLen) import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy.Char8 as L8 import Foreign@@ -173,3 +176,24 @@ = unsafePerformIO $ do ctx <- digestLazily md input digestFinal ctx++{- HMAC ---------------------------------------------------------------------- -}++foreign import ccall unsafe "HMAC"+ _HMAC :: Ptr EVP_MD -> Ptr CChar -> CInt -> Ptr CChar -> CInt+ -> Ptr CChar -> Ptr CUInt -> IO ()++-- | Perform a private key signing using the HMAC template with a given hash+hmacBS :: Digest -- ^ the hash function to use in the HMAC calculation+ -> ByteString -- ^ the HMAC key+ -> ByteString -- ^ the data to be signed+ -> ByteString -- ^ resulting HMAC+hmacBS (Digest md) key input =+ unsafePerformIO $+ 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+ copyCStringLen (bufPtr, bufLen)
OpenSSL/EVP/PKey.hsc view
@@ -21,10 +21,14 @@ #ifndef OPENSSL_NO_RSA , newPKeyRSA #endif+#ifndef OPENSSL_NO_DSA+ , newPKeyDSA+#endif ) where import Foreign+import OpenSSL.DSA import OpenSSL.EVP.Digest import OpenSSL.RSA import OpenSSL.Utils@@ -91,12 +95,27 @@ foreign import ccall unsafe "EVP_PKEY_set1_RSA" _set1_RSA :: Ptr EVP_PKEY -> Ptr RSA_ -> IO Int --- |@'newPKeyRSA' rsa@ encapsulates an 'RSA' key into 'PKey'.+-- |@'newPKeyRSA' rsa@ encapsulates an RSA key into 'PKey'. newPKeyRSA :: RSA -> PKey newPKeyRSA rsa = unsafePerformIO $ withRSAPtr rsa $ \ rsaPtr -> do pkeyPtr <- _pkey_new >>= failIfNull _set1_RSA pkeyPtr rsaPtr >>= failIf (/= 1)+ wrapPKeyPtr pkeyPtr+#endif+++#ifndef OPENSSL_NO_DSA+foreign import ccall unsafe "EVP_PKEY_set1_DSA"+ _set1_DSA :: Ptr EVP_PKEY -> Ptr DSA_ -> IO Int++-- |@'newPKeyDSA' dsa@ encapsulates an 'DSA' key into 'PKey'.+newPKeyDSA :: DSA -> PKey+newPKeyDSA dsa+ = unsafePerformIO $+ withDSAPtr dsa $ \ dsaPtr ->+ do pkeyPtr <- _pkey_new >>= failIfNull+ _set1_DSA pkeyPtr dsaPtr >>= failIf (/= 1) wrapPKeyPtr pkeyPtr #endif
OpenSSL/RSA.hsc view
@@ -106,7 +106,7 @@ = withRSAPtr rsa $ \ rsaPtr -> do bn <- peeker rsaPtr when (bn == nullPtr) $ fail "peekRSAPublic: got a nullPtr"- peekBN bn+ peekBN (wrapBN bn) peekRSAPrivate :: (Ptr RSA_ -> IO (Ptr BIGNUM)) -> RSA -> IO (Maybe Integer)@@ -116,7 +116,7 @@ if bn == nullPtr then return Nothing else- peekBN bn >>= return . Just+ peekBN (wrapBN bn) >>= return . Just -- |@'rsaN' pubKey@ returns the public modulus of the key. rsaN :: RSA -> IO Integer
+ OpenSSL/Random.hsc view
@@ -0,0 +1,56 @@+{- -*- haskell -*- -}++-- | PRNG services+-- See <http://www.openssl.org/docs/crypto/rand.html>+-- For random Integer generation, see "OpenSSL.BN"++#include "HsOpenSSL.h"++module OpenSSL.Random+ ( -- * Random byte generation+ randBytes+ , prandBytes+ , add+ ) where++import Foreign+import Foreign.C.Types+import qualified Data.ByteString as BS+import OpenSSL.Utils++foreign import ccall unsafe "RAND_bytes"+ _RAND_bytes :: Ptr CChar -> CInt -> IO CInt++foreign import ccall unsafe "RAND_pseudo_bytes"+ _RAND_pseudo_bytes :: Ptr CChar -> CInt -> IO ()++foreign import ccall unsafe "RAND_add"+ _RAND_add :: Ptr CChar -> CInt -> CInt -> IO ()++-- | Return a bytestring consisting of the given number of strongly random+-- bytes+randBytes :: Int -- ^ the number of bytes requested+ -> IO BS.ByteString+randBytes n =+ allocaArray n $ \bufPtr ->+ do _RAND_bytes bufPtr (fromIntegral n) >>= failIf (/= 1)+ BS.copyCStringLen (bufPtr, n)++-- | Return a bytestring consisting of the given number of pseudo random+-- bytes+prandBytes :: Int -- ^ the number of bytes requested+ -> IO BS.ByteString+prandBytes n =+ allocaArray n $ \bufPtr ->+ do _RAND_pseudo_bytes bufPtr (fromIntegral n)+ BS.copyCStringLen (bufPtr, n)++-- | Add data to the entropy pool. It's safe to add sensitive information+-- (e.g. user passwords etc) to the pool. Also, adding data with an entropy+-- of 0 can never hurt.+add :: BS.ByteString -- ^ random data to be added to the pool+ -> Int -- ^ the number of bits of entropy in the first argument+ -> IO ()+add bs entropy =+ BS.useAsCStringLen bs $ \(ptr, len) ->+ _RAND_add ptr (fromIntegral len) (fromIntegral entropy)
cbits/HsOpenSSL.h view
@@ -5,6 +5,8 @@ #include <openssl/bn.h> #include <openssl/err.h> #include <openssl/evp.h>+#include <openssl/hmac.h>+#include <openssl/rand.h> #include <openssl/objects.h> #include <openssl/opensslconf.h> #include <openssl/pem.h>