module OpenSSL.DSA
(
DSAKey(..)
, DSAPubKey
, DSAKeyPair
, DSA
, generateDSAParameters
, generateDSAKey
, generateDSAParametersAndKey
, signDigestedDataWithDSA
, verifyDigestedDataWithDSA
, dsaPrivate
, dsaPubKeyToTuple
, dsaKeyPairToTuple
, tupleToDSAPubKey
, tupleToDSAKeyPair
) where
import Control.Monad
import qualified Data.ByteString as BS
import Data.Typeable
import Foreign.C.String (CString)
import Foreign.C.Types (CChar(..), CInt(..))
import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr (FunPtr, Ptr, nullPtr)
import Foreign.Storable (Storable(..))
import OpenSSL.BN
import OpenSSL.Utils
import System.IO.Unsafe (unsafePerformIO)
newtype DSAPubKey = DSAPubKey (ForeignPtr DSA)
deriving Typeable
newtype DSAKeyPair = DSAKeyPair (ForeignPtr DSA)
deriving Typeable
data DSA
class DSAKey k where
dsaSize :: k -> Int
dsaSize dsa
= unsafePerformIO $
withDSAPtr dsa $ \ dsaPtr ->
fmap fromIntegral (_size dsaPtr)
dsaP :: k -> Integer
dsaP = peekI dsa_p
dsaQ :: k -> Integer
dsaQ = peekI dsa_q
dsaG :: k -> Integer
dsaG = peekI dsa_g
dsaPublic :: k -> Integer
dsaPublic = peekI dsa_pub_key
withDSAPtr :: k -> (Ptr DSA -> IO a) -> IO a
peekDSAPtr :: Ptr DSA -> IO (Maybe k)
absorbDSAPtr :: Ptr DSA -> IO (Maybe k)
instance DSAKey DSAPubKey where
withDSAPtr (DSAPubKey fp) = withForeignPtr fp
peekDSAPtr dsaPtr = _pubDup dsaPtr >>= absorbDSAPtr
absorbDSAPtr dsaPtr = fmap (Just . DSAPubKey) (newForeignPtr _free dsaPtr)
instance DSAKey DSAKeyPair where
withDSAPtr (DSAKeyPair fp) = withForeignPtr fp
peekDSAPtr dsaPtr
= do hasP <- hasDSAPrivateKey dsaPtr
if hasP then
_privDup dsaPtr >>= absorbDSAPtr
else
return Nothing
absorbDSAPtr dsaPtr
= do hasP <- hasDSAPrivateKey dsaPtr
if hasP then
fmap (Just . DSAKeyPair) (newForeignPtr _free dsaPtr)
else
return Nothing
hasDSAPrivateKey :: Ptr DSA -> IO Bool
hasDSAPrivateKey dsaPtr
= fmap (/= nullPtr) (dsa_priv_key dsaPtr)
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 -> CInt -> Ptr (Ptr BIGNUM) -> Ptr (Ptr BIGNUM) -> IO CInt
foreign import ccall unsafe "HsOpenSSL_dsa_verify"
_dsa_verify :: Ptr DSA -> CString -> CInt -> Ptr BIGNUM -> Ptr BIGNUM -> IO CInt
foreign import ccall safe "DSA_generate_parameters"
_generate_params :: CInt -> Ptr CChar -> CInt -> Ptr CInt -> Ptr CInt -> Ptr () -> Ptr () -> IO (Ptr DSA)
foreign import ccall unsafe "HsOpenSSL_DSAPublicKey_dup"
_pubDup :: Ptr DSA -> IO (Ptr DSA)
foreign import ccall unsafe "HsOpenSSL_DSAPrivateKey_dup"
_privDup :: Ptr DSA -> IO (Ptr DSA)
foreign import ccall unsafe "DSA_size"
_size :: Ptr DSA -> IO CInt
dsa_p, dsa_q, dsa_g, dsa_pub_key, dsa_priv_key :: Ptr DSA -> IO (Ptr BIGNUM)
setPQG :: Ptr DSA -> Integer -> Integer -> Integer -> IO ()
setKey :: Ptr DSA -> Ptr BIGNUM -> Ptr BIGNUM -> IO ()
dsa_p = ((\hsc_ptr -> peekByteOff hsc_ptr 24))
dsa_q = ((\hsc_ptr -> peekByteOff hsc_ptr 32))
dsa_g = ((\hsc_ptr -> peekByteOff hsc_ptr 40))
dsa_pub_key = ((\hsc_ptr -> peekByteOff hsc_ptr 48))
dsa_priv_key = ((\hsc_ptr -> peekByteOff hsc_ptr 56))
setPQG ptr p q g = do
fmap unwrapBN (newBN p) >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 24)) ptr
fmap unwrapBN (newBN q) >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 32)) ptr
fmap unwrapBN (newBN g) >>= ((\hsc_ptr -> pokeByteOff hsc_ptr 40)) ptr
setKey ptr pub priv = do
((\hsc_ptr -> pokeByteOff hsc_ptr 48)) ptr pub
((\hsc_ptr -> pokeByteOff hsc_ptr 56)) ptr priv
peekI :: DSAKey k => (Ptr DSA -> IO (Ptr BIGNUM)) -> k -> Integer
peekI peeker dsa
= unsafePerformIO $
withDSAPtr dsa $ \ dsaPtr ->
do bn <- peeker dsaPtr
when (bn == nullPtr) $ fail "peekI: got a nullPtr"
peekBN (wrapBN bn)
generateDSAParameters :: Int
-> Maybe BS.ByteString
-> IO (Int, Int, Integer, Integer, Integer)
generateDSAParameters nbits mseed = do
when (nbits < 512 || nbits > 1024) $ fail "Invalid DSA bit size"
alloca (\i1 ->
alloca (\i2 ->
(\x -> case mseed of
Nothing -> x (nullPtr, 0)
Just seed -> BS.useAsCStringLen seed x) (\(seedptr, seedlen) -> do
ptr <- _generate_params (fromIntegral nbits) seedptr (fromIntegral seedlen) i1 i2 nullPtr nullPtr
failIfNull_ ptr
itcount <- peek i1
gencount <- peek i2
p <- dsa_p ptr >>= peekBN . wrapBN
q <- dsa_q ptr >>= peekBN . wrapBN
g <- dsa_g ptr >>= peekBN . wrapBN
dsa_free ptr
return (fromIntegral itcount, fromIntegral gencount, p, q, g))))
generateDSAKey :: Integer
-> Integer
-> Integer
-> IO DSAKeyPair
generateDSAKey p q g = do
ptr <- _dsa_new
setPQG ptr p q g
_dsa_generate_key ptr
fmap DSAKeyPair (newForeignPtr _free ptr)
dsaPrivate :: DSAKeyPair -> Integer
dsaPrivate = peekI dsa_priv_key
dsaPubKeyToTuple :: DSAKeyPair -> (Integer, Integer, Integer, Integer)
dsaPubKeyToTuple dsa
= let p = peekI dsa_p dsa
q = peekI dsa_q dsa
g = peekI dsa_g dsa
pub = peekI dsa_pub_key dsa
in
(p, q, g, pub)
dsaKeyPairToTuple :: DSAKeyPair -> (Integer, Integer, Integer, Integer, Integer)
dsaKeyPairToTuple dsa
= let p = peekI dsa_p dsa
q = peekI dsa_q dsa
g = peekI dsa_g dsa
pub = peekI dsa_pub_key dsa
pri = peekI dsa_priv_key dsa
in
(p, q, g, pub, pri)
tupleToDSAPubKey :: (Integer, Integer, Integer, Integer) -> DSAPubKey
tupleToDSAPubKey (p, q, g, pub) = unsafePerformIO $ do
ptr <- _dsa_new
setPQG ptr p q g
pub' <- fmap unwrapBN (newBN pub)
setKey ptr pub' nullPtr
fmap DSAPubKey (newForeignPtr _free ptr)
tupleToDSAKeyPair :: (Integer, Integer, Integer, Integer, Integer) -> DSAKeyPair
tupleToDSAKeyPair (p, q, g, pub, pri) = unsafePerformIO $ do
ptr <- _dsa_new
setPQG ptr p q g
pub' <- fmap unwrapBN (newBN pub)
priv' <- fmap unwrapBN (newBN pri)
setKey ptr pub' priv'
fmap DSAKeyPair (newForeignPtr _free ptr)
generateDSAParametersAndKey :: Int
-> Maybe BS.ByteString
-> IO DSAKeyPair
generateDSAParametersAndKey nbits mseed =
(\x -> case mseed of
Nothing -> x (nullPtr, 0)
Just seed -> BS.useAsCStringLen seed x) (\(seedptr, seedlen) -> do
ptr <- _generate_params (fromIntegral nbits) seedptr (fromIntegral seedlen) nullPtr nullPtr nullPtr nullPtr
failIfNull_ ptr
_dsa_generate_key ptr
fmap DSAKeyPair (newForeignPtr _free ptr))
signDigestedDataWithDSA :: DSAKeyPair -> BS.ByteString -> IO (Integer, Integer)
signDigestedDataWithDSA dsa bytes =
BS.useAsCStringLen bytes (\(ptr, len) ->
alloca (\rptr ->
alloca (\sptr ->
withDSAPtr dsa (\dsaptr -> do
_dsa_sign dsaptr ptr (fromIntegral 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)))))
verifyDigestedDataWithDSA :: DSAKey k => k -> BS.ByteString -> (Integer, Integer) -> IO Bool
verifyDigestedDataWithDSA dsa bytes (r, s) =
BS.useAsCStringLen bytes (\(ptr, len) ->
withBN r (\bnR ->
withBN s (\bnS ->
withDSAPtr dsa (\dsaptr ->
fmap (== 1)
(_dsa_verify dsaptr ptr (fromIntegral len) (unwrapBN bnR) (unwrapBN bnS))))))
instance Eq DSAPubKey where
a == b
= dsaP a == dsaP b &&
dsaQ a == dsaQ b &&
dsaG a == dsaG b &&
dsaPublic a == dsaPublic b
instance Eq DSAKeyPair where
a == b
= dsaP a == dsaP b &&
dsaQ a == dsaQ b &&
dsaG a == dsaG b &&
dsaPublic a == dsaPublic b &&
dsaPrivate a == dsaPrivate b
instance Ord DSAPubKey where
a `compare` b
| dsaP a < dsaP b = LT
| dsaP a > dsaP b = GT
| dsaQ a < dsaQ b = LT
| dsaQ a > dsaQ b = GT
| dsaG a < dsaG b = LT
| dsaG a > dsaG b = GT
| dsaPublic a < dsaPublic b = LT
| dsaPublic a > dsaPublic b = GT
| otherwise = EQ
instance Ord DSAKeyPair where
a `compare` b
| dsaP a < dsaP b = LT
| dsaP a > dsaP b = GT
| dsaQ a < dsaQ b = LT
| dsaQ a > dsaQ b = GT
| dsaG a < dsaG b = LT
| dsaG a > dsaG b = GT
| dsaPublic a < dsaPublic b = LT
| dsaPublic a > dsaPublic b = GT
| dsaPrivate a < dsaPrivate b = LT
| dsaPrivate a > dsaPrivate b = GT
| otherwise = EQ
instance Show DSAPubKey where
show a
= concat [ "DSAPubKey {"
, "dsaP = ", show (dsaP a), ", "
, "dsaQ = ", show (dsaQ a), ", "
, "dsaG = ", show (dsaG a), ", "
, "dsaPublic = ", show (dsaPublic a)
, "}"
]
instance Show DSAKeyPair where
show a
= concat [ "DSAPubKey {"
, "dsaP = ", show (dsaP a), ", "
, "dsaQ = ", show (dsaQ a), ", "
, "dsaG = ", show (dsaG a), ", "
, "dsaPublic = ", show (dsaPublic a), ", "
, "dsaPrivate = ", show (dsaPrivate a)
, "}"
]