{-# OPTIONS_GHC -optc-D__GLASGOW_HASKELL__=606 #-} {-# INCLUDE "HsOpenSSL.h" #-} {-# LINE 1 "OpenSSL/DSA.hsc" #-} {- -*- haskell -*- -} {-# LINE 2 "OpenSSL/DSA.hsc" #-} -- #prune -- | The Digital Signature Algorithm (FIPS 186-2). -- See {-# LINE 9 "OpenSSL/DSA.hsc" #-} module OpenSSL.DSA ( -- * Type DSA , DSA_ -- private , withDSAPtr -- private -- * Key and parameter generation , generateParameters , generateKey , generateParametersAndKey -- * Signing and verification , signDigestedData , verifyDigestedData -- * Extracting fields of DSA objects , dsaP , dsaQ , dsaG , dsaPrivate , dsaPublic , dsaToTuple , tupleToDSA ) where import Control.Monad import Foreign import Foreign.C (CString) import Foreign.C.Types import OpenSSL.BN import OpenSSL.Utils import qualified Data.ByteString as BS -- | The type of a DSA key, includes parameters p, q, g. newtype DSA = DSA (ForeignPtr DSA_) data DSA_ foreign import ccall unsafe "&DSA_free" _free :: FunPtr (Ptr DSA_ -> IO ()) foreign import ccall unsafe "DSA_free" dsa_free :: Ptr DSA_ -> IO () foreign import ccall unsafe "BN_free" _bn_free :: Ptr BIGNUM -> IO () foreign import ccall unsafe "DSA_new" _dsa_new :: IO (Ptr DSA_) foreign import ccall unsafe "DSA_generate_key" _dsa_generate_key :: Ptr DSA_ -> IO () foreign import ccall unsafe "HsOpenSSL_dsa_sign" _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 -> Ptr BIGNUM -> Ptr BIGNUM -> IO Int withDSAPtr :: DSA -> (Ptr DSA_ -> IO a) -> IO a withDSAPtr (DSA ptr) = withForeignPtr ptr 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 (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 (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 -- test vectors given in FIP 186-2, app 5 generateParameters :: Int -- ^ The number of bits in the generated prime: 512 <= x <= 1024 -> Maybe BS.ByteString -- ^ optional seed, its length must be 20 bytes -> IO (Int, Int, Integer, Integer, Integer) -- ^ (iteration count, generator count, p, q, g) generateParameters nbits mseed = do when (nbits < 512 || nbits > 1024) $ fail "Invalid DSA bit size" alloca (\i1 -> do alloca (\i2 -> do (\x -> case mseed of Nothing -> x (nullPtr, 0) Just seed -> BS.useAsCStringLen seed x) (\(seedptr, seedlen) -> do ptr <- _generate_params nbits seedptr seedlen i1 i2 nullPtr nullPtr failIfNull ptr itcount <- peek i1 gencount <- peek i2 p <- ((\hsc_ptr -> peekByteOff hsc_ptr 12)) ptr >>= peekBN . wrapBN {-# LINE 101 "OpenSSL/DSA.hsc" #-} q <- ((\hsc_ptr -> peekByteOff hsc_ptr 16)) ptr >>= peekBN . wrapBN {-# LINE 102 "OpenSSL/DSA.hsc" #-} g <- ((\hsc_ptr -> peekByteOff hsc_ptr 20)) ptr >>= peekBN . wrapBN {-# LINE 103 "OpenSSL/DSA.hsc" #-} dsa_free ptr return (fromIntegral itcount, fromIntegral gencount, p, q, g)))) {- -- | This function just runs the example DSA generation, as given in FIP 186-2, -- app 5. The return values should be: -- (105, -- "8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7cbb8324f0d7882e5d0762fc5b7210 -- eafc2e9adac32ab7aac49693dfbf83724c2ec0736ee31c80291", -- "c773218c737ec8ee993b4f2ded30f48edace915f", -- "626d027839ea0a13413163a55b4cb500299d5522956cefcb3bff10f399ce2c2e71cb9de5fa24 -- babf58e5b79521925c9cc42e9f6f464b088cc572af53e6d78802"), as given at the bottom of -- page 21 test_generateParameters = do let seed = BS.pack [0xd5, 0x01, 0x4e, 0x4b, 0x60, 0xef, 0x2b, 0xa8, 0xb6, 0x21, 0x1b, 0x40, 0x62, 0xba, 0x32, 0x24, 0xe0, 0x42, 0x7d, 0xd3] (a, b, p, q, g) <- generateParameters 512 $ Just seed return (a, toHex p, toHex q, g) -} -- | Generate a new DSA key, given valid parameters generateKey :: Integer -- ^ p -> Integer -- ^ q -> Integer -- ^ g -> IO DSA generateKey p q g = do ptr <- _dsa_new newBN p >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 12)) ptr {-# LINE 134 "OpenSSL/DSA.hsc" #-} newBN q >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 16)) ptr {-# LINE 135 "OpenSSL/DSA.hsc" #-} newBN g >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 20)) ptr {-# LINE 136 "OpenSSL/DSA.hsc" #-} _dsa_generate_key ptr newForeignPtr _free ptr >>= return . DSA -- |Return the public prime number of the key. dsaP :: DSA -> IO (Maybe Integer) dsaP = peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 12)) {-# LINE 142 "OpenSSL/DSA.hsc" #-} -- |Return the public 160-bit subprime, @q | p-1@ of the key. dsaQ :: DSA -> IO (Maybe Integer) dsaQ = peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 16)) {-# LINE 146 "OpenSSL/DSA.hsc" #-} -- |Return the public generator of subgroup of the key. dsaG :: DSA -> IO (Maybe Integer) dsaG = peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 20)) {-# LINE 150 "OpenSSL/DSA.hsc" #-} -- |Return the public key @y = g^x@. dsaPublic :: DSA -> IO (Maybe Integer) dsaPublic = peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 24)) {-# LINE 154 "OpenSSL/DSA.hsc" #-} -- |Return the private key @x@. dsaPrivate :: DSA -> IO (Maybe Integer) dsaPrivate = peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 28)) {-# LINE 158 "OpenSSL/DSA.hsc" #-} -- | Convert a DSA object to a tuple of its members in the order p, q, g, -- public, private. If this is a public key, private will be Nothing dsaToTuple :: DSA -> IO (Integer, Integer, Integer, Integer, Maybe Integer) dsaToTuple dsa = do Just p <- peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 12)) dsa {-# LINE 164 "OpenSSL/DSA.hsc" #-} Just q <- peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 16)) dsa {-# LINE 165 "OpenSSL/DSA.hsc" #-} Just g <- peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 20)) dsa {-# LINE 166 "OpenSSL/DSA.hsc" #-} Just pub <- peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 24)) dsa {-# LINE 167 "OpenSSL/DSA.hsc" #-} private <- peekDSA ((\hsc_ptr -> peekByteOff hsc_ptr 28)) dsa {-# LINE 168 "OpenSSL/DSA.hsc" #-} return (p, q, g, pub, private) -- | Convert a tuple of members (in the same format as from dsaToTuple) into a -- DSA object tupleToDSA :: (Integer, Integer, Integer, Integer, Maybe Integer) -> IO DSA tupleToDSA (p, q, g, pub, mpriv) = do ptr <- _dsa_new newBN p >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 12)) ptr {-# LINE 177 "OpenSSL/DSA.hsc" #-} newBN q >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 16)) ptr {-# LINE 178 "OpenSSL/DSA.hsc" #-} newBN g >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 20)) ptr {-# LINE 179 "OpenSSL/DSA.hsc" #-} newBN pub >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 24)) ptr {-# LINE 180 "OpenSSL/DSA.hsc" #-} case mpriv of Just priv -> newBN priv >>= return . unwrapBN >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 28)) ptr {-# LINE 182 "OpenSSL/DSA.hsc" #-} Nothing -> ((\hsc_ptr -> pokeByteOff hsc_ptr 28)) ptr nullPtr {-# LINE 183 "OpenSSL/DSA.hsc" #-} newForeignPtr _free ptr >>= return . DSA -- | A utility function to generate both the parameters and the key pair at the -- same time. Saves serialising and deserialising the parameters too generateParametersAndKey :: Int -- ^ The number of bits in the generated prime: 512 <= x <= 1024 -> Maybe BS.ByteString -- ^ optional seed, its length must be 20 bytes -> IO DSA generateParametersAndKey nbits mseed = do (\x -> case mseed of Nothing -> x (nullPtr, 0) Just seed -> BS.useAsCStringLen seed x) (\(seedptr, seedlen) -> do ptr <- _generate_params nbits seedptr seedlen nullPtr nullPtr nullPtr nullPtr failIfNull ptr _dsa_generate_key ptr newForeignPtr _free ptr >>= return . DSA) -- | Sign pre-digested data. The DSA specs call for SHA1 to be used so, if you -- use anything else, YMMV. Returns a pair of Integers which, together, are -- the signature signDigestedData :: DSA -> BS.ByteString -> IO (Integer, Integer) signDigestedData dsa bytes = do BS.useAsCStringLen bytes (\(ptr, len) -> do alloca (\rptr -> do alloca (\sptr -> do withDSAPtr dsa (\dsaptr -> do _dsa_sign dsaptr ptr len rptr sptr >>= failIf (== 0) r <- peek rptr >>= peekBN . wrapBN peek rptr >>= _bn_free s <- peek sptr >>= peekBN . wrapBN peek sptr >>= _bn_free return (r, s))))) -- | Verify pre-digested data given a signature. verifyDigestedData :: DSA -> BS.ByteString -> (Integer, Integer) -> IO Bool verifyDigestedData dsa bytes (r, s) = do BS.useAsCStringLen bytes (\(ptr, len) -> do withBN r (\bnR -> do withBN s (\bnS -> do withDSAPtr dsa (\dsaptr -> do _dsa_verify dsaptr ptr len (unwrapBN bnR) (unwrapBN bnS) >>= return . (== 1)))))