diff --git a/Crypto/Cipher/DSA.hs b/Crypto/Cipher/DSA.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/Cipher/DSA.hs
@@ -0,0 +1,79 @@
+-- |
+-- Module      : Crypto.Cipher.DSA
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+
+module Crypto.Cipher.DSA
+	( Error(..)
+	, Params
+	, Signature
+	, PublicKey(..)
+	, PrivateKey(..)
+	, sign
+	, verify
+	) where
+
+import Crypto.Random
+import Data.Maybe
+import Data.ByteString (ByteString)
+import Number.ModArithmetic (exponantiation_rtl_binary, inverse)
+import Number.Serialize
+import Number.Generate
+
+data Error = 
+	  InvalidSignature          -- ^ signature is not valid r or s is not between the bound 0..q
+	| RandomGenFailure GenError -- ^ the random generator returns an error. give the opportunity to reseed for example.
+	deriving (Show,Eq)
+
+type Params = (Integer,Integer,Integer) {- P, G, Q -}
+type Signature = (Integer,Integer) {- R,S -}
+
+data PublicKey = PublicKey
+	{ public_params :: Params {- P, G, Q -}
+	, public_y      :: Integer
+	} deriving (Show)
+
+data PrivateKey = PrivateKey
+	{ private_params :: Params {- P, G, Q -}
+	, private_x      :: Integer
+	} deriving (Show)
+
+{-| sign message using the private key. -}
+sign :: CryptoRandomGen g => g -> (ByteString -> ByteString) -> PrivateKey -> ByteString -> Either GenError (Signature, g)
+sign rng hash pk m =
+	-- Recalculate the signature in the unlikely case that r = 0 or s = 0
+	case generateMax rng q of
+		Left err        -> Left err
+		Right (k, rng') ->
+			let kinv = fromJust $ inverse k q in
+			let r    = expmod g k p `mod` q in
+			let s    = (kinv * (hm + x * r)) `mod` q in
+			if r == 0 || s == 0
+				then sign rng' hash pk m
+				else Right ((r, s), rng')
+	where
+		(p,g,q)   = private_params pk
+		x         = private_x pk
+		hm        = os2ip $ hash m
+
+{- | verify a bytestring using the public key. -}
+verify :: Signature -> (ByteString -> ByteString) -> PublicKey -> ByteString -> Either Error Bool
+verify (r,s) hash pk m
+	-- Reject the signature if either 0 < r <q or 0 < s < q is not satisfied.
+	| r <= 0 || r >= q || s <= 0 || s >= q = Left InvalidSignature
+	| otherwise                            = Right $ v == r
+	where
+		(p,g,q) = public_params pk
+		y       = public_y pk
+		hm      = os2ip $ hash m
+
+		w       = fromJust $ inverse s q
+		u1      = (hm*w) `mod` q
+		u2      = (r*w) `mod` q
+		v       = ((expmod g u1 p) * (expmod y u2 p)) `mod` p `mod` q
+
+expmod :: Integer -> Integer -> Integer -> Integer
+expmod = exponantiation_rtl_binary
diff --git a/Crypto/Cipher/RSA.hs b/Crypto/Cipher/RSA.hs
--- a/Crypto/Cipher/RSA.hs
+++ b/Crypto/Cipher/RSA.hs
@@ -17,10 +17,10 @@
 
 import Control.Arrow (first)
 import Crypto.Random
-import Data.Bits
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as B
 import Number.ModArithmetic (exponantiation_rtl_binary)
+import Number.Serialize
 
 data Error =
 	  MessageSizeIncorrect      -- ^ the message to decrypt is not of the correct size (need to be == private_size)
@@ -124,17 +124,6 @@
 	where
 		lenbytes = B.length bytes
 		bytes    = i2osp m
-
--- | os2ip converts a byte string into a positive integer
-os2ip :: ByteString -> Integer
-os2ip = B.foldl' (\a b -> (256 * a) .|. (fromIntegral b)) 0
-
--- | i2osp converts a positive integer into a byte string
-i2osp :: Integer -> ByteString
-i2osp m = B.reverse $ B.unfoldr divMod256 m
-	where
-		divMod256 0 = Nothing
-		divMod256 n = Just (fromIntegral a,b) where (b,a) = n `divMod` 256
 
 expmod :: Integer -> Integer -> Integer -> Integer
 expmod = exponantiation_rtl_binary
diff --git a/Number/Generate.hs b/Number/Generate.hs
new file mode 100644
--- /dev/null
+++ b/Number/Generate.hs
@@ -0,0 +1,22 @@
+module Number.Generate
+	( generateMax
+	) where
+
+import Number.Serialize
+import Crypto.Random
+
+{- a bit too simplitic and probably not very good. need to have a serious look
+ - on how to generate random integer. -}
+generateMax :: CryptoRandomGen g => g -> Integer -> Either GenError (Integer, g)
+generateMax rng m =
+	let nbbytes = nbBytes m in
+	case genBytes nbbytes rng of
+		Left err         -> Left err
+		Right (bs, rng') ->
+			let n = os2ip bs in
+			if n < m then Right (n, rng') else generateMax rng' m
+
+nbBytes :: Integer -> Int
+nbBytes n
+	| n < 256   = 1
+	| otherwise = 1 + nbBytes (n `div` 256)
diff --git a/Number/Serialize.hs b/Number/Serialize.hs
new file mode 100644
--- /dev/null
+++ b/Number/Serialize.hs
@@ -0,0 +1,19 @@
+module Number.Serialize
+	( i2osp
+	, os2ip
+	) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Data.Bits
+
+-- | os2ip converts a byte string into a positive integer
+os2ip :: ByteString -> Integer
+os2ip = B.foldl' (\a b -> (256 * a) .|. (fromIntegral b)) 0
+
+-- | i2osp converts a positive integer into a byte string
+i2osp :: Integer -> ByteString
+i2osp m = B.reverse $ B.unfoldr divMod256 m
+	where
+		divMod256 0 = Nothing
+		divMod256 n = Just (fromIntegral a,b) where (b,a) = n `divMod` 256
diff --git a/Tests.hs b/Tests.hs
--- a/Tests.hs
+++ b/Tests.hs
@@ -8,6 +8,7 @@
 import System.IO (hFlush, stdout)
 
 import Control.Monad
+import Control.Arrow (first)
 
 import Data.List (intercalate)
 import Data.Char
@@ -15,6 +16,8 @@
 import Data.Word
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as BC
+-- for DSA
+import qualified Crypto.Hash.SHA1 as SHA1
 
 -- numbers
 import Number.ModArithmetic
@@ -22,6 +25,7 @@
 import qualified Crypto.Cipher.RC4 as RC4
 import qualified Crypto.Cipher.Camellia as Camellia
 import qualified Crypto.Cipher.RSA as RSA
+import qualified Crypto.Cipher.DSA as DSA
 import Crypto.Random
 
 encryptStream fi fc key plaintext = B.unpack $ snd $ fc (fi key) plaintext
@@ -108,23 +112,40 @@
 		ws <- replicateM sz (choose (0,255) :: Gen Int)
 		return $ RSAMessage $ B.pack $ map fromIntegral ws
 
-data Rng = Rng Int
+{- this is a just test rng. this is absolutely not a serious RNG. DO NOT use elsewhere -}
+data Rng = Rng (Int, Int)
 
+getByte :: Rng -> (Word8, Rng)
+getByte (Rng (mz, mw)) =
+	let mz2 = 36969 * (mz `mod` 65536) in
+	let mw2 = 18000 * (mw `mod` 65536) in
+	(fromIntegral (mz2 + mw2), Rng (mz2, mw2))
+
+getBytes 0 rng = ([], rng)
+getBytes n rng =
+	let (b, rng')  = getByte rng in
+	let (l, rng'') = getBytes (n-1) rng' in
+	(b:l, rng'')
+
 instance CryptoRandomGen Rng where
-	newGen _       = Right (Rng 0)
+	newGen _       = Right (Rng (2,3))
 	genSeedLength  = 0
-	genBytes len g = Right (B.pack $ replicate len 0x2d, g)
+	genBytes len g = Right $ first B.pack $ getBytes len g
 
-rng = Rng 0
+rng = Rng (1,2) 
 
+{-----------------------------------------------------------------------------------------------}
+{- testing RSA -}
+{-----------------------------------------------------------------------------------------------}
+
 prop_rsa_fast_valid (RSAMessage msg) =
-	(either Left (RSA.decrypt privatekey . fst) $ RSA.encrypt rng publickey msg) == Right msg
+	(either Left (RSA.decrypt rsaPrivatekey . fst) $ RSA.encrypt rng rsaPublickey msg) == Right msg
 
 prop_rsa_slow_valid (RSAMessage msg) =
-	(either Left (RSA.decrypt pk . fst) $ RSA.encrypt rng publickey msg) == Right msg
-	where pk = privatekey { RSA.private_p = 0, RSA.private_q = 0 }
+	(either Left (RSA.decrypt pk . fst) $ RSA.encrypt rng rsaPublickey msg) == Right msg
+	where pk = rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
 
-privatekey = RSA.PrivateKey
+rsaPrivatekey = RSA.PrivateKey
 	{ RSA.private_sz   = 128
 	, RSA.private_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
 	, RSA.private_d    = 133764127300370985476360382258931504810339098611363623122953018301285450176037234703101635770582297431466449863745848961134143024057267778947569638425565153896020107107895924597628599677345887446144410702679470631826418774397895304952287674790343620803686034122942606764275835668353720152078674967983573326257
@@ -135,17 +156,49 @@
 	, RSA.private_qinv = 11136639099661288633118187183300604127717437440459572124866697429021958115062007251843236337586667012492941414990095176435990146486852255802952814505784196
 	}
 
-publickey = RSA.PublicKey
+rsaPublickey = RSA.PublicKey
 	{ RSA.public_sz = 128
 	, RSA.public_n  = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
 	, RSA.public_e  = 65537
 	}
 
+{-----------------------------------------------------------------------------------------------}
+{- testing DSA -}
+{-----------------------------------------------------------------------------------------------}
+
+
+dsaParams = (p,g,q)
+	where
+		p = 0x00a8c44d7d0bbce69a39008948604b9c7b11951993a5a1a1fa995968da8bb27ad9101c5184bcde7c14fb79f7562a45791c3d80396cefb328e3e291932a17e22edd
+		g = 0x0bf9fe6c75d2367b88912b2252d20fdcad06b3f3a234b92863a1e30a96a123afd8e8a4b1dd953e6f5583ef8e48fc7f47a6a1c8f24184c76dba577f0fec2fcd1c
+		q = 0x0096674b70ef58beaaab6743d6af16bb862d18d119
+
+dsaPrivatekey = DSA.PrivateKey
+	{ DSA.private_params = dsaParams
+	, DSA.private_x      = 0x229bac7aa1c7db8121bfc050a3426eceae23fae8
+	}
+
+dsaPublickey = DSA.PublicKey
+	{ DSA.public_params = dsaParams
+	, DSA.public_y      = 0x4fa505e86e32922f1fa1702a120abdba088bb4be801d4c44f7fc6b9094d85cd52c429cbc2b39514e30909b31e2e2e0752b0fc05c1a7d9c05c3e52e49e6edef4c
+	}
+
+prop_dsa_valid (RSAMessage msg) =
+	case DSA.verify signature (SHA1.hash) dsaPublickey msg of
+		Left err -> False
+		Right b  -> b
+	where
+		Right (signature, rng') = DSA.sign rng (SHA1.hash) dsaPrivatekey msg
+
+{-----------------------------------------------------------------------------------------------}
+{- main -}
+{-----------------------------------------------------------------------------------------------}
+
 args = stdArgs
 	{ replay     = Nothing
-	, maxSuccess = 1000
-	, maxDiscard = 4000
-	, maxSize    = 1000
+	, maxSuccess = 200
+	, maxDiscard = 1000
+	, maxSize    = 200
 	}
 
 run_test n t = putStr ("  " ++ n ++ " ... ") >> hFlush stdout >> quickCheckWith args t
@@ -159,3 +212,5 @@
 
 	run_test "RSA decrypt(slow).encrypt = id" prop_rsa_slow_valid
 	run_test "RSA decrypt(fast).encrypt = id" prop_rsa_fast_valid
+
+	run_test "DSA verify . sign = valid" prop_dsa_valid
diff --git a/cryptocipher.cabal b/cryptocipher.cabal
--- a/cryptocipher.cabal
+++ b/cryptocipher.cabal
@@ -1,12 +1,12 @@
 Name:                cryptocipher
-Version:             0.2.1
-Description:         Symmetrical Block and Stream Ciphers
+Version:             0.2.2
+Description:         Symmetrical Block, Stream and PubKey Ciphers
 License:             BSD3
 License-file:        LICENSE
 Copyright:           Vincent Hanquez <vincent@snarc.org>
 Author:              Vincent Hanquez <vincent@snarc.org>
 Maintainer:          Vincent Hanquez <vincent@snarc.org>
-Synopsis:            Symmetrical Block and Stream Ciphers
+Synopsis:            Symmetrical Block, Stream and PubKey Ciphers
 Category:            Cryptography
 Build-Type:          Simple
 Homepage:            http://github.com/vincenthz/hs-cryptocipher
@@ -21,14 +21,17 @@
   Exposed-modules:   Crypto.Cipher.RC4
                      Crypto.Cipher.Camellia
                      Crypto.Cipher.RSA
+                     Crypto.Cipher.DSA
   other-modules:     Number.ModArithmetic
+                     Number.Serialize
+                     Number.Generate
   ghc-options:       -Wall
 
 Executable           Tests
   Main-Is:           Tests.hs
   if flag(test)
     Buildable:       True
-    Build-depends:   base >= 4 && < 7, HUnit, bytestring, QuickCheck >= 2
+    Build-depends:   base >= 4 && < 7, HUnit, bytestring, cryptohash, QuickCheck >= 2
   else
     Buildable:       False
 
