diff --git a/Benchs/Bench.hs b/Benchs/Bench.hs
new file mode 100644
--- /dev/null
+++ b/Benchs/Bench.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE BangPatterns #-}
+module Main where
+
+import Criterion.Main
+import PregenKeys
+
+import qualified Crypto.Hash.SHA1 as SHA1
+import Crypto.PubKey.RSA as RSA
+import Crypto.PubKey.RSA.PKCS15 as PKCS15
+import Crypto.PubKey.RSA.OAEP as OAEP
+import Crypto.PubKey.RSA.PSS as PSS
+import Crypto.PubKey.HashDescr
+
+import Crypto.Random.AESCtr
+import qualified Data.ByteString as B
+
+right (Right r) = r
+right (Left _)  = error "left received"
+
+main = do
+    rng <- makeSystem
+    let !bs = B.replicate 32 0
+        !encryptedMsgPKCS = (right . fst . PKCS15.encrypt rng rsaPublickey) bs
+        !encryptedMsgOAEP = (right . fst . OAEP.encrypt rng oaepParams rsaPublickey) bs
+        !signedMsgPKCS = (right . PKCS15.sign Nothing hashDescrSHA1 rsaPrivatekey) bs
+        !signedMsgPSS = (right . fst . PSS.sign rng Nothing pssParams rsaPrivatekey) bs
+        privateKeySlow = rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+        !blinder = fst $ generateBlinder rng (RSA.public_n rsaPublickey)
+        oaepParams = OAEP.defaultOAEPParams SHA1.hash
+        pssParams  = PSS.defaultPSSParamsSHA1
+    defaultMain
+        [ bgroup "RSA PKCS15"
+            [ bench "encryption" $ nf (right . fst . PKCS15.encrypt rng rsaPublickey) bs
+            , bgroup "decryption"
+                [ bench "slow" $ nf (right . PKCS15.decrypt Nothing privateKeySlow) encryptedMsgPKCS
+                , bench "fast" $ nf (right . PKCS15.decrypt Nothing rsaPrivatekey) encryptedMsgPKCS
+                , bench "slow+blinding" $ nf (right . PKCS15.decrypt (Just blinder) privateKeySlow) encryptedMsgPKCS
+                , bench "fast+blinding" $ nf (right . PKCS15.decrypt (Just blinder) rsaPrivatekey) encryptedMsgPKCS
+                ]
+            , bgroup "signing"
+                [ bench "slow" $ nf (right . PKCS15.sign Nothing hashDescrSHA1 privateKeySlow) bs
+                , bench "fast" $ nf (right . PKCS15.sign Nothing hashDescrSHA1 rsaPrivatekey) bs
+                , bench "slow+blinding" $ nf (right . PKCS15.sign (Just blinder) hashDescrSHA1 privateKeySlow) bs
+                , bench "fast+blinding" $ nf (right . PKCS15.sign (Just blinder) hashDescrSHA1 rsaPrivatekey) bs
+                ]
+            , bench "verify" $ nf (PKCS15.verify hashDescrSHA1 rsaPublickey bs) signedMsgPKCS
+            ]
+        , bgroup "RSA OAEP"
+            [ bench "encryption" $ nf (right . fst . OAEP.encrypt rng oaepParams rsaPublickey) bs
+            , bgroup "decryption"
+                [ bench "slow" $ nf (right . OAEP.decrypt Nothing oaepParams privateKeySlow) encryptedMsgOAEP
+                , bench "fast" $ nf (right . OAEP.decrypt Nothing oaepParams rsaPrivatekey) encryptedMsgOAEP
+                , bench "slow+blinding" $ nf (right . OAEP.decrypt (Just blinder) oaepParams privateKeySlow) encryptedMsgOAEP
+                , bench "fast+blinding" $ nf (right . OAEP.decrypt (Just blinder) oaepParams rsaPrivatekey) encryptedMsgOAEP
+                ]
+            ]
+        , bgroup "RSA PSS"
+            [ bgroup "signing"
+                [ bench "slow" $ nf (right . fst . PSS.sign rng Nothing pssParams privateKeySlow) bs
+                , bench "fast" $ nf (right . fst . PSS.sign rng Nothing pssParams rsaPrivatekey) bs
+                , bench "slow+blinding" $ nf (right . fst . PSS.sign rng (Just blinder) pssParams privateKeySlow) bs
+                , bench "fast+blinding" $ nf (right . fst . PSS.sign rng (Just blinder) pssParams rsaPrivatekey) bs
+                ]
+            , bench "verify" $ nf (PSS.verify pssParams rsaPublickey bs) signedMsgPSS
+            ]
+        ]
diff --git a/Crypto/PubKey/DH.hs b/Crypto/PubKey/DH.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/DH.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+-- |
+-- Module      : Crypto.PubKey.DH
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.DH
+    ( Params
+    , PublicNumber
+    , PrivateNumber
+    , SharedKey
+    , generateParams
+    , generatePrivate
+    , generatePublic
+    , getShared
+    ) where
+
+import Crypto.Number.ModArithmetic (exponantiation)
+import Crypto.Number.Prime (generateSafePrime)
+import Crypto.Number.Generate (generateOfSize)
+import Crypto.Types.PubKey.DH
+import Crypto.Random.API
+import Control.Arrow (first)
+
+-- | generate params from a specific generator (2 or 5 are common values)
+-- we generate a safe prime (a prime number of the form 2p+1 where p is also prime)
+generateParams :: CPRG g => g -> Int -> Integer -> (Params, g)
+generateParams rng bits generator =
+    first (\p -> (p, generator)) $ generateSafePrime rng bits
+
+-- | generate a private number with no specific property
+-- this number is usually called X in DH text.
+generatePrivate :: CPRG g => g -> Int -> (PrivateNumber, g)
+generatePrivate rng bits = first PrivateNumber $ generateOfSize rng bits
+
+-- | generate a public number that is for the other party benefits.
+-- this number is usually called Y in DH text.
+generatePublic :: Params -> PrivateNumber -> PublicNumber
+generatePublic (p,g) (PrivateNumber x) = PublicNumber $ exponantiation g x p
+
+-- | generate a shared key using our private number and the other party public number
+getShared :: Params -> PrivateNumber -> PublicNumber -> SharedKey
+getShared (p,_) (PrivateNumber x) (PublicNumber y) = SharedKey $ exponantiation y x p
diff --git a/Crypto/PubKey/DSA.hs b/Crypto/PubKey/DSA.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/DSA.hs
@@ -0,0 +1,73 @@
+-- |
+-- Module      : Crypto.PubKey.DSA
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+
+module Crypto.PubKey.DSA
+    ( Params
+    , Signature
+    , PublicKey(..)
+    , PrivateKey(..)
+    -- * signature primitive
+    , sign
+    , signWith
+    -- * verification primitive
+    , verify
+    ) where
+
+import Crypto.Random.API
+import Data.Maybe
+import Data.ByteString (ByteString)
+import Crypto.Number.ModArithmetic (exponantiation, inverse)
+import Crypto.Number.Serialize
+import Crypto.Number.Generate
+import Crypto.Types.PubKey.DSA
+import Crypto.PubKey.HashDescr
+
+-- | sign message using the private key and an explicit k number.
+signWith :: Integer         -- ^ k random number
+         -> PrivateKey      -- ^ private key
+         -> HashFunction    -- ^ hash function
+         -> ByteString      -- ^ message to sign
+         -> Maybe Signature
+signWith k pk hash msg
+    | r == 0 || s == 0  = Nothing
+    | otherwise         = Just (r,s)
+    where -- parameters
+          (p,g,q)   = private_params pk
+          x         = private_x pk
+          -- compute r,s
+          kInv      = fromJust $ inverse k q
+          hm        = os2ip $ hash msg
+          r         = expmod g k p `mod` q
+          s         = (kInv * (hm + x * r)) `mod` q
+
+-- | sign message using the private key.
+sign :: CPRG g => g -> PrivateKey -> HashFunction -> ByteString -> (Signature, g)
+sign rng pk hash msg =
+    case signWith k pk hash msg of
+        Nothing  -> sign rng' pk hash msg
+        Just sig -> (sig, rng')
+    where (_,_,q)   = private_params pk
+          (k, rng') = generateMax rng q
+
+-- | verify a bytestring using the public key.
+verify :: HashFunction -> PublicKey -> Signature -> ByteString -> Bool
+verify hash pk (r,s) m
+    -- Reject the signature if either 0 < r < q or 0 < s < q is not satisfied.
+    | r <= 0 || r >= q || s <= 0 || s >= q = False
+    | otherwise                            = 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
diff --git a/Crypto/PubKey/ElGamal.hs b/Crypto/PubKey/ElGamal.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/ElGamal.hs
@@ -0,0 +1,144 @@
+-- |
+-- Module      : Crypto.PubKey.ElGamal
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+-- This module is a work in progress. do not use:
+-- it might eat your dog, your data or even both.
+--
+-- TODO: provide a mapping between integer and ciphertext
+--       generate numbers correctly
+--
+module Crypto.PubKey.ElGamal
+    ( Params
+    , PublicNumber
+    , PrivateNumber
+    , EphemeralKey(..)
+    , SharedKey
+    , Signature
+    -- * generation
+    , generatePrivate
+    , generatePublic
+    -- * encryption and decryption with no scheme
+    , encryptWith
+    , encrypt
+    , decrypt
+    -- * signature primitives
+    , signWith
+    , sign
+    -- * verification primitives
+    , verify
+    ) where
+
+import Data.ByteString (ByteString)
+import Crypto.Number.ModArithmetic (exponantiation, inverse)
+import Crypto.Number.Generate (generateMax)
+import Crypto.Number.Serialize (os2ip)
+import Crypto.Number.Basic (gcde_binary)
+import Crypto.Types.PubKey.DH
+import Crypto.Random.API
+import Control.Arrow (first)
+import Data.Maybe (fromJust)
+import Crypto.PubKey.HashDescr (HashFunction)
+
+-- | ElGamal Signature
+data Signature = Signature (Integer, Integer)
+
+-- | ElGamal Ephemeral key. also called Temporary key.
+newtype EphemeralKey = EphemeralKey Integer
+
+-- | generate a private number with no specific property
+-- this number is usually called a and need to be between
+-- 0 and q (order of the group G).
+--
+generatePrivate :: CPRG g => g -> Integer -> (PrivateNumber, g)
+generatePrivate rng q = first PrivateNumber $ generateMax rng q
+
+-- | generate an ephemeral key which is a number with no specific property,
+-- and need to be between 0 and q (order of the group G).
+--
+generateEphemeral :: CPRG g => g -> Integer -> (EphemeralKey, g)
+generateEphemeral rng q = first toEphemeral $ generatePrivate rng q
+    where toEphemeral (PrivateNumber n) = EphemeralKey n
+
+-- | generate a public number that is for the other party benefits.
+-- this number is usually called h=g^a
+generatePublic :: Params -> PrivateNumber -> PublicNumber
+generatePublic (p,g) (PrivateNumber a) = PublicNumber $ exponantiation g a p
+
+-- | encrypt with a specified ephemeral key
+-- do not reuse ephemeral key.
+encryptWith :: EphemeralKey -> Params -> PublicNumber -> Integer -> (Integer,Integer)
+encryptWith (EphemeralKey b) (p,g) (PublicNumber h) m = (c1,c2)
+    where s  = exponantiation h b p
+          c1 = exponantiation g b p
+          c2 = (s * m) `mod` p
+
+-- | encrypt a message using params and public keys
+-- will generate b (called the ephemeral key)
+encrypt :: CPRG g => g -> Params -> PublicNumber -> Integer -> ((Integer,Integer), g)
+encrypt rng params@(p,_) public m = first (\b -> encryptWith b params public m) $ generateEphemeral rng q
+    where q = p-1 -- p is prime, hence order of the group is p-1
+
+-- | decrypt message
+decrypt :: Params -> PrivateNumber -> (Integer, Integer) -> Integer
+decrypt (p,_) (PrivateNumber a) (c1,c2) = (c2 * sm1) `mod` p
+    where s   = exponantiation c1 a p
+          sm1 = fromJust $ inverse s p -- always inversible in Zp
+
+-- | sign a message with an explicit k number
+--
+-- if k is not appropriate, then no signature is returned.
+--
+-- with some appropriate value of k, the signature generation can fail,
+-- and no signature is returned. User of this function need to retry
+-- with a different k value.
+signWith :: Integer         -- ^ random number k, between 0 and p-1 and gcd(k,p-1)=1
+         -> Params          -- ^ DH params (p,g)
+         -> PrivateNumber   -- ^ DH private key
+         -> HashFunction    -- ^ collision resistant hash function
+         -> ByteString      -- ^ message to sign
+         -> Maybe Signature
+signWith k (p,g) (PrivateNumber x) hashF msg
+    | k >= p-1 || d > 1 = Nothing -- gcd(k,p-1) is not 1
+    | s == 0            = Nothing
+    | otherwise         = Just $ Signature (r,s)
+    where r          = exponantiation g k p
+          h          = os2ip $ hashF msg
+          s          = ((h - x*r) * kInv) `mod` (p-1)
+          (kInv,_,d) = gcde_binary k (p-1)
+
+-- | sign message
+--
+-- This function will generate a random number, however
+-- as the signature might fail, the function will automatically retry
+-- until a proper signature has been created.
+--
+sign :: CPRG g
+     => g              -- ^ random number generator
+     -> Params         -- ^ DH params (p,g)
+     -> PrivateNumber  -- ^ DH private key
+     -> HashFunction   -- ^ collision resistant hash function
+     -> ByteString     -- ^ message to sign
+     -> (Signature, g)
+sign rng params@(p,_) priv hashF msg =
+    let (k, rng') = generateMax rng (p-1)
+     in case signWith k params priv hashF msg of
+             Nothing  -> sign rng' params priv hashF msg
+             Just sig -> (sig, rng')
+
+-- | verify a signature
+verify :: Params
+       -> PublicNumber
+       -> HashFunction
+       -> ByteString
+       -> Signature
+       -> Bool
+verify (p,g) (PublicNumber y) hashF msg (Signature (r,s))
+    | or [r <= 0,r >= p,s <= 0,s >= (p-1)] = False
+    | otherwise                            = lhs == rhs
+    where h   = os2ip $ hashF msg
+          lhs = exponantiation g h p
+          rhs = (exponantiation y r p * exponantiation r s p) `mod` p
diff --git a/Crypto/PubKey/HashDescr.hs b/Crypto/PubKey/HashDescr.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/HashDescr.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE OverloadedStrings #-}
+-- |
+-- Module      : Crypto.PubKey.HashDescr
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.HashDescr
+    ( HashFunction
+    , HashDescr(..)
+    , hashDescrMD2
+    , hashDescrMD5
+    , hashDescrSHA1
+    , hashDescrSHA224
+    , hashDescrSHA256
+    , hashDescrSHA384
+    , hashDescrSHA512
+    ) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Crypto.Hash
+
+-- | A standard hash function returning a digest object
+type HashFunction = ByteString -> ByteString
+
+-- | Describe a hash function and a way to wrap the digest into
+-- an DER encoded ASN1 marshalled structure.
+data HashDescr = HashDescr { hashFunction :: HashFunction
+                           , digestToASN1 :: ByteString -> ByteString
+                           }
+
+-- | Describe the MD2 hashing algorithm
+hashDescrMD2 :: HashDescr
+hashDescrMD2 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest MD2)
+              , digestToASN1 = toHashWithInfo "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10"
+              }
+-- | Describe the MD5 hashing algorithm
+hashDescrMD5 :: HashDescr
+hashDescrMD5 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest MD5)
+              , digestToASN1 = toHashWithInfo "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10"
+              }
+-- | Describe the SHA1 hashing algorithm
+hashDescrSHA1 :: HashDescr
+hashDescrSHA1 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest SHA1)
+              , digestToASN1 = toHashWithInfo "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14"
+              }
+-- | Describe the SHA224 hashing algorithm
+hashDescrSHA224 :: HashDescr
+hashDescrSHA224 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest SHA224)
+              , digestToASN1 = toHashWithInfo "\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c"
+              }
+-- | Describe the SHA256 hashing algorithm
+hashDescrSHA256 :: HashDescr
+hashDescrSHA256 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest SHA256)
+              , digestToASN1 = toHashWithInfo "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20"
+              }
+-- | Describe the SHA384 hashing algorithm
+hashDescrSHA384 :: HashDescr
+hashDescrSHA384 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest SHA384)
+              , digestToASN1 = toHashWithInfo "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30"
+              }
+-- | Describe the SHA512 hashing algorithm
+hashDescrSHA512 :: HashDescr
+hashDescrSHA512 =
+    HashDescr { hashFunction = digestToByteString . (hash :: ByteString -> Digest SHA512)
+              , digestToASN1 = toHashWithInfo "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40"
+              }
+
+-- | Generate the marshalled structure with the following ASN1 structure:
+--
+--   Start Sequence
+--     ,Start Sequence
+--       ,OID oid
+--       ,Null
+--     ,End Sequence
+--     ,OctetString digest
+--   ,End Sequence
+--
+toHashWithInfo :: ByteString -> ByteString -> ByteString
+toHashWithInfo pre digest = pre `B.append` digest
diff --git a/Crypto/PubKey/Internal.hs b/Crypto/PubKey/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/Internal.hs
@@ -0,0 +1,24 @@
+-- |
+-- Module      : Crypto.PubKey.Internal
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.Internal
+    ( and'
+    , (&&!)
+    ) where
+
+import Data.List (foldl')
+
+-- | This is a strict version of and
+and' :: [Bool] -> Bool
+and' l = foldl' (&&!) True l
+
+-- | This is a strict version of &&.
+(&&!) :: Bool -> Bool -> Bool
+True  &&! True  = True
+True  &&! False = False
+False &&! True  = False
+False &&! False = False
diff --git a/Crypto/PubKey/MaskGenFunction.hs b/Crypto/PubKey/MaskGenFunction.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/MaskGenFunction.hs
@@ -0,0 +1,31 @@
+-- |
+-- Module      : Crypto.PubKey.MaskGenFunction
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.MaskGenFunction
+    ( MaskGenAlgorithm
+    , mgf1
+    ) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Crypto.PubKey.HashDescr
+import Crypto.Number.Serialize (i2ospOf_)
+
+-- | Represent a mask generation algorithm
+type MaskGenAlgorithm = HashFunction -- ^ hash function to use
+                     -> ByteString   -- ^ seed
+                     -> Int          -- ^ length to generate
+                     -> ByteString
+
+-- | Mask generation algorithm MGF1
+mgf1 :: MaskGenAlgorithm
+mgf1 hashF seed len = loop B.empty 0
+    where loop t counter
+            | B.length t >= len = B.take len t
+            | otherwise         = let counterBS = i2ospOf_ 4 counter
+                                      newT = t `B.append` hashF (seed `B.append` counterBS)
+                                   in loop newT (counter+1)
diff --git a/Crypto/PubKey/RSA.hs b/Crypto/PubKey/RSA.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA.hs
@@ -0,0 +1,74 @@
+-- |
+-- Module      : Crypto.PubKey.RSA
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.RSA
+    ( Error(..)
+    , PublicKey(..)
+    , PrivateKey(..)
+    , Blinder(..)
+    -- * generation function
+    , generateWith
+    , generate
+    , generateBlinder
+    ) where
+
+import Crypto.Random.API
+import Crypto.Types.PubKey.RSA
+import Crypto.Number.ModArithmetic (inverseCoprimes)
+import Crypto.Number.Generate (generateMax)
+import Crypto.Number.Prime (generatePrime)
+import Crypto.PubKey.RSA.Types
+
+-- | generate a public key and private key with p and q.
+--
+-- p and q need to be distinct primes numbers.
+--
+-- e need to be coprime to phi=(p-1)*(q-1). a small hamming weight results in better performance.
+-- 0x10001 is a popular choice. 3 is popular as well, but proven to not be as secure for some cases.
+generateWith :: (Integer, Integer) -> Int -> Integer -> (PublicKey, PrivateKey)
+generateWith (p,q) size e = (pub,priv)
+    where n   = p*q
+          phi = (p-1)*(q-1)
+          d   = inverseCoprimes e phi -- e and phi need to be coprime
+          pub = PublicKey { public_size = size
+                          , public_n    = n
+                          , public_e    = e
+                          }
+          priv = PrivateKey { private_pub  = pub
+                            , private_d    = d
+                            , private_p    = p
+                            , private_q    = q
+                            , private_dP   = d `mod` (p-1)
+                            , private_dQ   = d `mod` (q-1)
+                            , private_qinv = inverseCoprimes q p -- q and p are coprime
+                            }
+
+-- | generate a pair of (private, public) key of size in bytes.
+generate :: CPRG g => g -> Int -> Integer -> ((PublicKey, PrivateKey), g)
+generate rng size e = do
+    let (pq, rng') = generatePQ rng
+     in (generateWith pq size e, rng')
+    where
+        generatePQ g =
+            let (p, g')  = generatePrime g (8 * (size `div` 2))
+                (q, g'') = generateQ p g'
+             in ((p,q), g'')
+        generateQ p h =
+            let (q, h') = generatePrime h (8 * (size - (size `div` 2)))
+             in if p == q then generateQ p h' else (q, h')
+
+-- | Generate a blinder to use with decryption and signing operation
+--
+-- the unique parameter apart from the random number generator is the
+-- public key value N.
+generateBlinder :: CPRG g
+                => g       -- ^ CPRG to use.
+                -> Integer -- ^ RSA public N parameters.
+                -> (Blinder, g)
+generateBlinder rng n =
+    let (r, rng') = generateMax rng n
+     in (Blinder r (inverseCoprimes r n), rng')
diff --git a/Crypto/PubKey/RSA/OAEP.hs b/Crypto/PubKey/RSA/OAEP.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA/OAEP.hs
@@ -0,0 +1,140 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Crypto.PubKey.RSA.OAEP
+    (
+      OAEPParams(..)
+    , defaultOAEPParams
+    -- * OAEP encryption
+    , encryptWithSeed
+    , encrypt
+    -- * OAEP decryption
+    , decrypt
+    , decryptSafer
+    ) where
+
+import Crypto.Random.API
+import Crypto.Types.PubKey.RSA
+import Crypto.PubKey.HashDescr
+import Crypto.PubKey.MaskGenFunction
+import Crypto.PubKey.RSA.Prim
+import Crypto.PubKey.RSA.Types
+import Crypto.PubKey.RSA (generateBlinder)
+import Crypto.PubKey.Internal (and')
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Data.Bits (xor)
+
+-- | Parameters for OAEP encryption/decryption
+data OAEPParams = OAEPParams
+    { oaepHash       :: HashFunction     -- ^ Hash function to use.
+    , oaepMaskGenAlg :: MaskGenAlgorithm -- ^ Mask Gen algorithm to use.
+    , oaepLabel      :: Maybe ByteString -- ^ Optional label prepended to message.
+    }
+
+-- | Default Params with a specified hash function
+defaultOAEPParams :: HashFunction -> OAEPParams
+defaultOAEPParams hashF =
+    OAEPParams { oaepHash         = hashF
+               , oaepMaskGenAlg   = mgf1
+               , oaepLabel        = Nothing
+               }
+
+encryptWithSeed :: ByteString
+                -> OAEPParams
+                -> PublicKey
+                -> ByteString
+                -> Either Error ByteString
+encryptWithSeed seed oaep pk msg
+    | k < 2*hashLen+2          = Left InvalidParameters
+    | B.length seed /= hashLen = Left InvalidParameters
+    | mLen > k - 2*hashLen-2   = Left MessageTooLong
+    | otherwise                = Right $ ep pk em
+    where -- parameters
+          k          = public_size pk
+          mLen       = B.length msg
+          hashF      = oaepHash oaep
+          mgf        = (oaepMaskGenAlg oaep) hashF
+          labelHash  = hashF $ maybe B.empty id $ oaepLabel oaep
+          hashLen    = B.length labelHash
+
+          -- put fields
+          ps         = B.replicate (k - mLen - 2*hashLen - 2) 0
+          db         = B.concat [labelHash, ps, B.singleton 0x1, msg]
+          dbmask     = mgf seed (k - hashLen - 1)
+          maskedDB   = B.pack $ B.zipWith xor db dbmask
+          seedMask   = mgf maskedDB hashLen
+          maskedSeed = B.pack $ B.zipWith xor seed seedMask
+          em         = B.concat [B.singleton 0x0,maskedSeed,maskedDB]
+
+-- | Encrypt a message using OAEP
+encrypt :: CPRG g
+        => g          -- ^ random number generator.
+        -> OAEPParams -- ^ OAEP params to use for encryption.
+        -> PublicKey  -- ^ Public key.
+        -> ByteString -- ^ Message to encrypt
+        -> (Either Error ByteString, g)
+encrypt g oaep pk msg = (encryptWithSeed seed oaep pk msg, g')
+    where hashF      = oaepHash oaep
+          hashLen    = B.length (hashF B.empty)
+          (seed, g') = genRandomBytes g hashLen
+
+-- | un-pad a OAEP encoded message.
+--
+-- It doesn't apply the RSA decryption primitive
+unpad :: OAEPParams  -- ^ OAEP params to use
+      -> Int         -- ^ size of the key in bytes
+      -> ByteString  -- ^ encoded message (not encrypted)
+      -> Either Error ByteString
+unpad oaep k em
+    | paddingSuccess = Right msg
+    | otherwise      = Left MessageNotRecognized
+    where -- parameters
+          hashF      = oaepHash oaep
+          mgf        = (oaepMaskGenAlg oaep) hashF
+          labelHash  = hashF $ maybe B.empty id $ oaepLabel oaep
+          hashLen    = B.length labelHash
+          -- getting em's fields
+          (pb, em0)  = B.splitAt 1 em
+          (maskedSeed,maskedDB) = B.splitAt hashLen em0
+          seedMask   = mgf maskedDB hashLen
+          seed       = B.pack $ B.zipWith xor maskedSeed seedMask
+          dbmask     = mgf seed (k - hashLen - 1)
+          db         = B.pack $ B.zipWith xor maskedDB dbmask
+          -- getting db's fields
+          (labelHash',db1) = B.splitAt hashLen db
+          (_,db2)    = B.break (/= 0) db1
+          (ps1,msg)  = B.splitAt 1 db2
+
+          paddingSuccess = and' [ labelHash' == labelHash -- no need for constant eq
+                                , ps1        == "\x01"
+                                , pb         == "\x00"
+                                ]
+
+-- | Decrypt a ciphertext using OAEP
+--
+-- When the signature is not in a context where an attacker could gain
+-- information from the timing of the operation, the blinder can be set to None.
+--
+-- If unsure always set a blinder or use decryptSafer
+decrypt :: Maybe Blinder -- ^ Optional blinder
+        -> OAEPParams    -- ^ OAEP params to use for decryption
+        -> PrivateKey    -- ^ Private key
+        -> ByteString    -- ^ Cipher text
+        -> Either Error ByteString
+decrypt blinder oaep pk cipher
+    | B.length cipher /= k = Left MessageSizeIncorrect
+    | k < 2*hashLen+2      = Left InvalidParameters
+    | otherwise            = unpad oaep (private_size pk) $ dp blinder pk cipher
+    where -- parameters
+          k          = private_size pk
+          hashF      = oaepHash oaep
+          hashLen    = B.length (hashF B.empty)
+
+-- | Decrypt a ciphertext using OAEP and by automatically generating a blinder.
+decryptSafer :: CPRG g
+             => g          -- ^ random number generator
+             -> OAEPParams -- ^ OAEP params to use for decryption
+             -> PrivateKey -- ^ Private key
+             -> ByteString -- ^ Cipher text
+             -> (Either Error ByteString, g)
+decryptSafer rng oaep pk cipher = (decrypt (Just blinder) oaep pk cipher, rng')
+    where (blinder, rng') = generateBlinder rng (private_n pk)
diff --git a/Crypto/PubKey/RSA/PKCS15.hs b/Crypto/PubKey/RSA/PKCS15.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA/PKCS15.hs
@@ -0,0 +1,140 @@
+-- |
+-- Module      : Crypto.PubKey.RSA.PKCS15
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+{-# LANGUAGE OverloadedStrings #-}
+module Crypto.PubKey.RSA.PKCS15
+    (
+    -- * padding and unpadding
+      pad
+    , padSignature
+    , unpad
+    -- * private key operations
+    , decrypt
+    , decryptSafer
+    , sign
+    , signSafer
+    -- * public key operations
+    , encrypt
+    , verify
+    ) where
+
+import Crypto.Random.API
+import Crypto.Types.PubKey.RSA
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Crypto.PubKey.RSA.Prim
+import Crypto.PubKey.RSA.Types
+import Crypto.PubKey.RSA (generateBlinder)
+import Crypto.PubKey.HashDescr
+
+-- | This produce a standard PKCS1.5 padding for encryption
+pad :: CPRG g => g -> Int -> ByteString -> Either Error (ByteString, g)
+pad rng len m
+    | B.length m > len - 11 = Left MessageTooLong
+    | otherwise             =
+        let (padding, rng') = getNonNullRandom rng (len - B.length m - 3)
+         in Right (B.concat [ B.singleton 0, B.singleton 2, padding, B.singleton 0, m ], rng')
+
+        where {- get random non-null bytes -}
+              getNonNullRandom :: CPRG g => g -> Int -> (ByteString, g)
+              getNonNullRandom g n =
+                    let (bs0,g') = genRandomBytes g n
+                        bytes    = B.pack $ filter (/= 0) $ B.unpack $ bs0
+                        left     = (n - B.length bytes)
+                     in if left == 0
+                        then (bytes, g')
+                        else let (bend, g'') = getNonNullRandom g' left
+                              in (bytes `B.append` bend, g'')
+
+-- | Produce a standard PKCS1.5 padding for signature
+padSignature :: Int -> ByteString -> Either Error ByteString
+padSignature klen signature
+    | klen < siglen+1 = Left SignatureTooLong
+    | otherwise       = Right $ B.concat [B.singleton 0,B.singleton 1,padding,B.singleton 0,signature]
+    where
+        siglen    = B.length signature
+        padding   = B.replicate (klen - siglen - 3) 0xff
+
+-- | Try to remove a standard PKCS1.5 encryption padding.
+unpad :: ByteString -> Either Error ByteString
+unpad packed
+    | signal_error = Left MessageNotRecognized
+    | otherwise    = Right m
+    where
+        (zt, ps0m)   = B.splitAt 2 packed
+        (ps, zm)     = B.span (/= 0) ps0m
+        (z, m)       = B.splitAt 1 zm
+        signal_error = zt /= "\x00\x02" || z /= "\x00" || (B.length ps < 8)
+
+-- | decrypt message using the private key.
+--
+-- When the decryption is not in a context where an attacker could gain
+-- information from the timing of the operation, the blinder can be set to None.
+--
+-- If unsure always set a blinder or use decryptSafer
+decrypt :: Maybe Blinder -- ^ optional blinder
+        -> PrivateKey    -- ^ RSA private key
+        -> ByteString    -- ^ cipher text
+        -> Either Error ByteString
+decrypt blinder pk c
+    | B.length c /= (private_size pk) = Left MessageSizeIncorrect
+    | otherwise                       = unpad $ dp blinder pk c
+
+-- | decrypt message using the private key and by automatically generating a blinder.
+decryptSafer :: CPRG g
+             => g          -- ^ random generator
+             -> PrivateKey -- ^ RSA private key
+             -> ByteString -- ^ cipher text
+             -> (Either Error ByteString, g)
+decryptSafer rng pk b =
+    let (blinder, rng') = generateBlinder rng (private_n pk)
+     in (decrypt (Just blinder) pk b, rng')
+
+-- | encrypt a bytestring using the public key and a CPRG random generator.
+--
+-- the message need to be smaller than the key size - 11
+encrypt :: CPRG g => g -> PublicKey -> ByteString -> (Either Error ByteString, g)
+encrypt rng pk m = do
+    case pad rng (public_size pk) m of
+        Left err         -> (Left err, rng)
+        Right (em, rng') -> (Right (ep pk em), rng')
+
+-- | sign message using private key, a hash and its ASN1 description
+--
+-- When the signature is not in a context where an attacker could gain
+-- information from the timing of the operation, the blinder can be set to None.
+--
+-- If unsure always set a blinder or use signSafer
+sign :: Maybe Blinder -- ^ optional blinder
+     -> HashDescr     -- ^ hash descriptor
+     -> PrivateKey    -- ^ private key
+     -> ByteString    -- ^ message to sign
+     -> Either Error ByteString
+sign blinder hashDescr pk m = dp blinder pk `fmap` makeSignature hashDescr (private_size pk) m
+
+-- | sign message using the private key and by automatically generating a blinder.
+signSafer :: CPRG g
+          => g          -- ^ random generator
+          -> HashDescr  -- ^ Hash descriptor
+          -> PrivateKey -- ^ private key
+          -> ByteString -- ^ message to sign
+          -> (Either Error ByteString, g)
+signSafer rng hashDescr pk m =
+    let (blinder, rng') = generateBlinder rng (private_n pk)
+     in (sign (Just blinder) hashDescr pk m, rng')
+
+-- | verify message with the signed message
+verify :: HashDescr -> PublicKey -> ByteString -> ByteString -> Bool
+verify hashDescr pk m sm =
+    case makeSignature hashDescr (public_size pk) m of
+        Left _  -> False
+        Right s -> s == (ep pk sm)
+
+{- makeSignature for sign and verify -}
+makeSignature :: HashDescr -> Int -> ByteString -> Either Error ByteString
+makeSignature hashDescr klen m = padSignature klen signature
+    where signature = (digestToASN1 hashDescr) $ (hashFunction hashDescr) m
diff --git a/Crypto/PubKey/RSA/PSS.hs b/Crypto/PubKey/RSA/PSS.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA/PSS.hs
@@ -0,0 +1,122 @@
+-- |
+-- Module      : Crypto.PubKey.RSA.PSS
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.RSA.PSS
+    ( PSSParams(..)
+    , defaultPSSParams
+    , defaultPSSParamsSHA1
+    -- * Sign and verify functions
+    , signWithSalt
+    , sign
+    , verify
+    ) where
+
+import Crypto.Random.API
+import Crypto.Types.PubKey.RSA
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import Crypto.PubKey.RSA.Prim
+import Crypto.PubKey.RSA.Types
+import Crypto.PubKey.HashDescr
+import Crypto.PubKey.MaskGenFunction
+import Crypto.Hash
+import Data.Bits (xor, shiftR, (.&.))
+import Data.Word
+
+-- | Parameters for PSS signature/verification.
+data PSSParams = PSSParams { pssHash         :: HashFunction     -- ^ Hash function to use
+                           , pssMaskGenAlg   :: MaskGenAlgorithm -- ^ Mask Gen algorithm to use
+                           , pssSaltLength   :: Int              -- ^ Length of salt. need to be <= to hLen.
+                           , pssTrailerField :: Word8            -- ^ Trailer field, usually 0xbc
+                           }
+
+-- | Default Params with a specified hash function
+defaultPSSParams :: HashFunction -> PSSParams
+defaultPSSParams hashF =
+    PSSParams { pssHash         = hashF
+              , pssMaskGenAlg   = mgf1
+              , pssSaltLength   = B.length $ hashF B.empty
+              , pssTrailerField = 0xbc
+              }
+
+-- | Default Params using SHA1 algorithm.
+defaultPSSParamsSHA1 :: PSSParams
+defaultPSSParamsSHA1 = defaultPSSParams (digestToByteString . (hash :: ByteString -> Digest SHA1))
+
+-- | Sign using the PSS parameters and the salt explicitely passed as parameters.
+--
+-- the function ignore SaltLength from the PSS Parameters
+signWithSalt :: ByteString    -- ^ Salt to use
+             -> Maybe Blinder -- ^ optional blinder to use
+             -> PSSParams     -- ^ PSS Parameters to use
+             -> PrivateKey    -- ^ RSA Private Key
+             -> ByteString    -- ^ Message to sign
+             -> Either Error ByteString
+signWithSalt salt blinder params pk m
+    | k < hashLen + saltLen + 2 = Left InvalidParameters
+    | otherwise                 = Right $ dp blinder pk em
+    where mHash    = (pssHash params) m
+          k        = private_size pk
+          dbLen    = k - hashLen - 1
+          saltLen  = B.length salt
+          hashLen  = B.length (hashF B.empty)
+          hashF    = pssHash params
+          pubBits  = private_size pk * 8 -- to change if public_size is converted in bytes
+
+          m'       = B.concat [B.replicate 8 0,mHash,salt]
+          h        = hashF m'
+          db       = B.concat [B.replicate (dbLen - saltLen - 1) 0,B.singleton 1,salt]
+          dbmask   = (pssMaskGenAlg params) hashF h dbLen
+          maskedDB = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor db dbmask
+          em       = B.concat [maskedDB, h, B.singleton (pssTrailerField params)]
+
+-- | Sign using the PSS Parameters
+sign :: CPRG g
+     => g               -- ^ random generator to use to generate the salt
+     -> Maybe Blinder   -- ^ optional blinder to use
+     -> PSSParams       -- ^ PSS Parameters to use
+     -> PrivateKey      -- ^ RSA Private Key
+     -> ByteString      -- ^ Message to sign
+     -> (Either Error ByteString, g)
+sign rng blinder params pk m = (signWithSalt salt blinder params pk m, rng')
+    where (salt,rng') = genRandomBytes rng (pssSaltLength params)
+
+-- | Verify a signature using the PSS Parameters
+verify :: PSSParams  -- ^ PSS Parameters to use to verify,
+                     --   this need to be identical to the parameters when signing
+       -> PublicKey  -- ^ RSA Public Key
+       -> ByteString -- ^ Message to verify
+       -> ByteString -- ^ Signature
+       -> Bool
+verify params pk m s
+    | public_size pk /= B.length s        = False
+    | B.last em /= pssTrailerField params = False
+    | not (B.all (== 0) ps0)              = False
+    | b1 /= B.singleton 1                 = False
+    | otherwise                           = h == h'
+        where -- parameters
+              hashF     = pssHash params
+              hashLen   = B.length (hashF B.empty)
+              dbLen     = public_size pk - hashLen - 1
+              pubBits   = public_size pk * 8 -- to change if public_size is converted in bytes
+              -- unmarshall fields
+              em        = ep pk s
+              maskedDB  = B.take (B.length em - hashLen - 1) em
+              h         = B.take hashLen $ B.drop (B.length maskedDB) em
+              dbmask    = (pssMaskGenAlg params) hashF h dbLen
+              db        = B.pack $ normalizeToKeySize pubBits $ B.zipWith xor maskedDB dbmask
+              (ps0,z)   = B.break (== 1) db
+              (b1,salt) = B.splitAt 1 z
+              mHash     = hashF m
+              m'        = B.concat [B.replicate 8 0,mHash,salt]
+              h'        = hashF m'
+
+normalizeToKeySize :: Int -> [Word8] -> [Word8]
+normalizeToKeySize _    []     = [] -- very unlikely
+normalizeToKeySize bits (x:xs) = x .&. mask : xs
+    where mask = if sh > 0 then 0xff `shiftR` (8-sh) else 0xff
+          sh   = ((bits-1) .&. 0x7)
diff --git a/Crypto/PubKey/RSA/Prim.hs b/Crypto/PubKey/RSA/Prim.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA/Prim.hs
@@ -0,0 +1,64 @@
+-- |
+-- Module      : Crypto.PubKey.RSA.Prim
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.RSA.Prim
+    (
+    -- * decrypt primitive
+      dp
+    -- * encrypt primitive
+    , ep
+    ) where
+
+import Data.ByteString (ByteString)
+import Crypto.PubKey.RSA.Types (Blinder(..))
+import Crypto.Types.PubKey.RSA
+import Crypto.Number.ModArithmetic (exponantiation)
+import Crypto.Number.Serialize (os2ip, i2ospOf_)
+
+{- dpSlow computes the decrypted message not using any precomputed cache value.
+   only n and d need to valid. -}
+dpSlow :: PrivateKey -> ByteString -> ByteString
+dpSlow pk c = i2ospOf_ (private_size pk) $ expmod (os2ip c) (private_d pk) (private_n pk)
+
+{- dpFast computes the decrypted message more efficiently if the
+   precomputed private values are available. mod p and mod q are faster
+   to compute than mod pq -}
+dpFast :: Blinder -> PrivateKey -> ByteString -> ByteString
+dpFast (Blinder r rm1) pk c =
+    i2ospOf_ (private_size pk) (multiplication rm1 (m2 + h * (private_q pk)) (private_n pk))
+    where
+        re  = expmod r (public_e $ private_pub pk) (private_n pk)
+        iC  = multiplication re (os2ip c) (private_n pk)
+        m1  = expmod iC (private_dP pk) (private_p pk)
+        m2  = expmod iC (private_dQ pk) (private_q pk)
+        h   = ((private_qinv pk) * (m1 - m2)) `mod` (private_p pk)
+
+dpFastNoBlinder :: PrivateKey -> ByteString -> ByteString
+dpFastNoBlinder pk c = i2ospOf_ (private_size pk) (m2 + h * (private_q pk))
+     where iC = os2ip c
+           m1 = expmod iC (private_dP pk) (private_p pk)
+           m2 = expmod iC (private_dQ pk) (private_q pk)
+           h  = ((private_qinv pk) * (m1 - m2)) `mod` (private_p pk)
+
+-- | Compute the RSA decrypt primitive.
+-- if the p and q numbers are available, then dpFast is used
+-- otherwise, we use dpSlow which only need d and n.
+dp :: Maybe Blinder -> PrivateKey -> ByteString -> ByteString
+dp blinder pk
+    | private_p pk /= 0 && private_q pk /= 0 = maybe dpFastNoBlinder dpFast blinder $ pk
+    | otherwise                              = dpSlow pk
+
+-- | Compute the RSA encrypt primitive
+ep :: PublicKey -> ByteString -> ByteString
+ep pk m = i2ospOf_ (public_size pk) $ expmod (os2ip m) (public_e pk) (public_n pk)
+
+expmod :: Integer -> Integer -> Integer -> Integer
+expmod = exponantiation
+
+-- | multiply 2 integers in Zm only performing the modulo operation if necessary
+multiplication :: Integer -> Integer -> Integer -> Integer
+multiplication a b m = (a * b) `mod` m
diff --git a/Crypto/PubKey/RSA/Types.hs b/Crypto/PubKey/RSA/Types.hs
new file mode 100644
--- /dev/null
+++ b/Crypto/PubKey/RSA/Types.hs
@@ -0,0 +1,26 @@
+-- |
+-- Module      : Crypto.PubKey.RSA.Types
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : Good
+--
+module Crypto.PubKey.RSA.Types
+    ( Error(..)
+    , Blinder(..)
+    ) where
+
+-- | Blinder which is used to obfuscate the timing
+-- of the decryption primitive (used by decryption and signing).
+data Blinder = Blinder !Integer !Integer
+             deriving (Show,Eq)
+
+-- | error possible during encryption, decryption or signing.
+data Error =
+      MessageSizeIncorrect -- ^ the message to decrypt is not of the correct size (need to be == private_size)
+    | MessageTooLong       -- ^ the message to encrypt is too long (>= private_size - 11)
+    | MessageNotRecognized -- ^ the message decrypted doesn't have a PKCS15 structure (0 2 .. 0 msg)
+    | SignatureTooLong     -- ^ the signature generated through the hash is too long to process with this key
+    | InvalidParameters    -- ^ some parameters lead to breaking assumptions.
+    deriving (Show,Eq)
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2010-2012 Vincent Hanquez <vincent@snarc.org>
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Tests/KAT.hs b/Tests/KAT.hs
new file mode 100644
--- /dev/null
+++ b/Tests/KAT.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE OverloadedStrings #-}
+module KAT where
+
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.HUnit (testCase)
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import Test.HUnit
+import System.IO (hFlush, stdout)
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+
+import Crypto.PubKey.RSA
+import Crypto.PubKey.MaskGenFunction
+import qualified Crypto.Hash.SHA1 as SHA1
+
+import KAT.OAEP
+import KAT.PSS
+import KAT.DSA
+
+data VectorMgf = VectorMgf { seed :: ByteString
+                           , dbMask :: ByteString
+                           }
+
+doMGFTest (i, vmgf) = testCase (show i) (dbMask vmgf @=? actual)
+    where actual = mgf1 SHA1.hash (seed vmgf) (B.length $ dbMask vmgf)
+
+vectorsMGF =
+    [ VectorMgf
+        { seed = "\xdf\x1a\x89\x6f\x9d\x8b\xc8\x16\xd9\x7c\xd7\xa2\xc4\x3b\xad\x54\x6f\xbe\x8c\xfe"
+        , dbMask = "\x66\xe4\x67\x2e\x83\x6a\xd1\x21\xba\x24\x4b\xed\x65\x76\xb8\x67\xd9\xa4\x47\xc2\x8a\x6e\x66\xa5\xb8\x7d\xee\x7f\xbc\x7e\x65\xaf\x50\x57\xf8\x6f\xae\x89\x84\xd9\xba\x7f\x96\x9a\xd6\xfe\x02\xa4\xd7\x5f\x74\x45\xfe\xfd\xd8\x5b\x6d\x3a\x47\x7c\x28\xd2\x4b\xa1\xe3\x75\x6f\x79\x2d\xd1\xdc\xe8\xca\x94\x44\x0e\xcb\x52\x79\xec\xd3\x18\x3a\x31\x1f\xc8\x97\x39\xa9\x66\x43\x13\x6e\x8b\x0f\x46\x5e\x87\xa4\x53\x5c\xd4\xc5\x9b\x10\x02\x8d"
+        }
+    ]
+
+katTests =
+    [ testGroup "MGF1" $ map doMGFTest (zip [0..] vectorsMGF)
+    , pssTests
+    , oaepTests
+    , dsaTests
+    ]
diff --git a/Tests/PregenKeys.hs b/Tests/PregenKeys.hs
new file mode 100644
--- /dev/null
+++ b/Tests/PregenKeys.hs
@@ -0,0 +1,38 @@
+module PregenKeys where
+
+import qualified Crypto.PubKey.RSA as RSA
+import qualified Crypto.PubKey.DSA as DSA
+import qualified Crypto.PubKey.DH as DH
+
+rsaPrivatekey = RSA.PrivateKey
+    { RSA.private_pub  = rsaPublickey
+    , RSA.private_d    = 133764127300370985476360382258931504810339098611363623122953018301285450176037234703101635770582297431466449863745848961134143024057267778947569638425565153896020107107895924597628599677345887446144410702679470631826418774397895304952287674790343620803686034122942606764275835668353720152078674967983573326257
+    , RSA.private_p    = 12909745499610419492560645699977670082358944785082915010582495768046269235061708286800087976003942261296869875915181420265794156699308840835123749375331319
+    , RSA.private_q    = 10860278066550210927914375228722265675263011756304443428318337179619069537063135098400347475029673115805419186390580990519363257108008103841271008948795129
+    , RSA.private_dP   = 5014229697614831746694710412330921341325464081424013940131184365711243776469716106024020620858146547161326009604054855316321928968077674343623831428796843
+    , RSA.private_dQ   = 3095337504083058271243917403868092841421453478127022884745383831699720766632624326762288333095492075165622853999872779070009098364595318242383709601515849
+    , RSA.private_qinv = 11136639099661288633118187183300604127717437440459572124866697429021958115062007251843236337586667012492941414990095176435990146486852255802952814505784196
+    }
+
+rsaPublickey = RSA.PublicKey
+    { RSA.public_size = 128
+    , RSA.public_n    = 140203425894164333410594309212077886844966070748523642084363106504571537866632850620326769291612455847330220940078873180639537021888802572151020701352955762744921926221566899281852945861389488419179600933178716009889963150132778947506523961974222282461654256451508762805133855866018054403911588630700228345151
+    , RSA.public_e    = 65537
+    }
+
+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
+    }
+
diff --git a/Tests/RNG.hs b/Tests/RNG.hs
new file mode 100644
--- /dev/null
+++ b/Tests/RNG.hs
@@ -0,0 +1,38 @@
+module RNG where
+
+import Data.Word
+import Data.List (foldl')
+import qualified Data.ByteString as B
+import Crypto.Random.API
+import Control.Arrow (first)
+
+{- 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)) = (r, g)
+    where mz2 = 36969 * (mz `mod` 65536)
+          mw2 = 18070 * (mw `mod` 65536)
+          r   = fromIntegral (mz2 + mw2)
+          g   = Rng (mz2, mw2)
+
+getBytes :: Int -> Rng -> ([Word8], Rng)
+getBytes 0 g = ([], g)
+getBytes n g =
+    let (b, g')  = getByte g
+        (l, g'') = getBytes (n-1) g'
+     in (b:l, g'')
+
+instance CPRG Rng where
+    cprgGenBytes g len    = first B.pack $ getBytes len g
+    cprgSupplyEntropy g e = reseed e g
+    cprgNeedReseed _      = maxBound
+
+reseed :: B.ByteString -> Rng -> Rng
+reseed bs (Rng (a,b)) = Rng (fromIntegral a', b')
+        where a' = foldl' (\v i -> ((fromIntegral v) + (fromIntegral i) * 36969) `mod` 65536) a l
+              b' = foldl' (\v i -> ((fromIntegral v) + (fromIntegral i) * 18070) `mod` 65536) b l
+              l  = B.unpack bs
+
+rng :: Rng
+rng = Rng (1,2) 
diff --git a/Tests/Tests.hs b/Tests/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests/Tests.hs
@@ -0,0 +1,157 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Test.Framework (Test, defaultMain, testGroup)
+import Test.Framework.Providers.QuickCheck2 (testProperty)
+
+import Test.QuickCheck
+import Test.QuickCheck.Test
+import System.IO (hFlush, stdout)
+
+import Control.Monad
+import Control.Arrow (first)
+import Control.Applicative ((<$>))
+
+import Data.List (intercalate)
+import Data.Char
+import Data.Bits
+import Data.Word
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC
+
+import qualified Crypto.PubKey.RSA as RSA
+import qualified Crypto.PubKey.RSA.PKCS15 as RSAPKCS15
+import qualified Crypto.PubKey.RSA.OAEP as RSAOAEP
+import qualified Crypto.PubKey.DSA as DSA
+import qualified Crypto.PubKey.DH as DH
+import Crypto.Number.Serialize (i2osp)
+import Crypto.PubKey.HashDescr
+
+import qualified Crypto.Hash.SHA1 as SHA1
+import RNG
+import KAT
+import PregenKeys
+
+withAleasInteger :: Rng -> Seed -> (Rng -> (a,Rng)) -> a
+withAleasInteger rng (Seed i) f = fst $ f $ reseed (i2osp (if i < 0 then -i else i)) rng
+
+withRNG :: Seed -> (Rng -> (a,Rng)) -> a
+withRNG seed f = withAleasInteger rng seed f
+
+--withArbitraryRNG :: (Rng -> (a,Rng)) -> Arbitrary a
+withArbitraryRNG f = arbitrary >>= \seed -> return (withAleasInteger rng seed f)
+
+newtype PositiveSmall = PositiveSmall Integer
+                      deriving (Show,Eq)
+
+instance Arbitrary PositiveSmall where
+    arbitrary = PositiveSmall `fmap` (resize (2^5) (arbitrarySizedIntegral `suchThat` (\i -> i > 0 && i < 2^5)))
+
+data Range = Range Integer Integer
+           deriving (Show,Eq)
+
+instance Arbitrary Range where
+    arbitrary = do x <- resize (2^30) (arbitrarySizedIntegral `suchThat` (\i -> i >= 40000 && i < 2^30))
+                   o <- resize (2^10) (arbitrarySizedIntegral `suchThat` (\i -> i >= 1000 && i < 2^10))
+                   return $ Range x (x+o)
+
+newtype Seed = Seed Integer
+             deriving (Eq)
+
+instance Show Seed where
+    show s = "Seed " ++ show s -- "seed"
+
+instance Arbitrary Seed where
+    arbitrary = Seed `fmap` (resize (2^30) (arbitrarySizedIntegral `suchThat` (\x -> x > 2^6 && x < 2^30)))
+
+data RSAMessage = RSAMessage RSA.Blinder B.ByteString deriving (Show, Eq)
+
+data RSAOAEPMessage = RSAOAEPMessage RSA.Blinder B.ByteString RSAOAEP.OAEPParams
+
+instance Show RSAOAEPMessage where
+    show (RSAOAEPMessage a1 b1 _) = "RSAOAEPMessage " ++ show a1 ++ " " ++ show b1
+
+instance Eq RSAOAEPMessage where
+    (RSAOAEPMessage a1 b1 _) == (RSAOAEPMessage a2 b2 _) = a1 == a2 && b1 == b2
+
+genBS :: Int -> Gen B.ByteString
+genBS sz = (B.pack . map fromIntegral) `fmap` replicateM sz (choose (0,255) :: Gen Int)
+
+instance Arbitrary RSAOAEPMessage where
+    arbitrary = do
+        let hashLen = B.length (SHA1.hash B.empty)
+        sz <- choose (0, 128 - 2*hashLen - 2)
+        blinder <- withArbitraryRNG (\g -> RSA.generateBlinder g (RSA.public_n rsaPublickey))
+        ws <- genBS sz
+        return $ RSAOAEPMessage blinder ws (RSAOAEP.defaultOAEPParams SHA1.hash)
+
+instance Arbitrary RSAMessage where
+    arbitrary = do
+        sz <- choose (0, 128 - 11)
+        blinder <- withArbitraryRNG (\g -> RSA.generateBlinder g (RSA.public_n rsaPublickey))
+        ws <- genBS sz
+        return $ RSAMessage blinder ws
+
+prop_rsa_pkcs15_valid fast blinding (RSAMessage blindR msg) =
+    (either Left (doDecrypt pk) $ fst $ RSAPKCS15.encrypt rng rsaPublickey msg) == Right msg
+    where pk = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+          doDecrypt = RSAPKCS15.decrypt (if blinding then Just blindR else Nothing)
+
+prop_rsa_oaep_valid fast blinding (RSAOAEPMessage blindR msg oaepParams) =
+    (either Left (doDecrypt oaepParams pk) $ fst $ RSAOAEP.encrypt rng oaepParams rsaPublickey msg) `assertEq` Right msg
+    where pk        = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+          doDecrypt = RSAOAEP.decrypt (if blinding then Just blindR else Nothing)
+
+assertEq (Right got) (Right exp) = if got == exp then True else error ("got: " ++ show got ++ "\nexp: " ++ show exp)
+assertEq (Left got) (Right exp) = error ("got Left: " ++ show got)
+
+prop_rsa_sign_valid fast (RSAMessage _ msg) = (either (const False) (\smsg -> verify msg smsg) $ sign msg) == True
+    where
+        verify   = RSAPKCS15.verify hashDescrSHA1 rsaPublickey
+        sign     = RSAPKCS15.sign Nothing hashDescrSHA1 pk
+        pk       = if fast then rsaPrivatekey else rsaPrivatekey { RSA.private_p = 0, RSA.private_q = 0 }
+
+prop_rsa_sign_fast_valid = prop_rsa_sign_valid True
+prop_rsa_sign_slow_valid = prop_rsa_sign_valid False
+
+prop_dsa_valid (RSAMessage _ msg) = DSA.verify (SHA1.hash) dsaPublickey signature msg
+    where (signature, rng') = DSA.sign rng dsaPrivatekey (SHA1.hash) msg
+
+instance Arbitrary DH.PrivateNumber where
+    arbitrary = fromIntegral <$> (suchThat (arbitrary :: Gen Integer) (\x -> x >= 1))
+
+prop_dh_valid (xa, xb) = sa == sb
+    where
+        sa = DH.getShared dhparams xa yb
+        sb = DH.getShared dhparams xb ya
+        yb = DH.generatePublic dhparams xb
+        ya = DH.generatePublic dhparams xa
+        dhparams = (11, 7)
+
+
+asymEncryptionTests = testGroup "assymmetric cipher encryption"
+    [ testProperty "RSA(PKCS15) (slow)" (prop_rsa_pkcs15_valid False False)
+    , testProperty "RSA(PKCS15) (fast)" (prop_rsa_pkcs15_valid True  False)
+    , testProperty "RSA(PKCS15) (slow+blind)" (prop_rsa_pkcs15_valid False True)
+    , testProperty "RSA(PKCS15) (fast+blind)" (prop_rsa_pkcs15_valid True  True)
+    , testProperty "RSA(OAEP) (slow)" (prop_rsa_oaep_valid False False)
+    , testProperty "RSA(OAEP) (fast)" (prop_rsa_oaep_valid True  False)
+    , testProperty "RSA(OAEP) (slow+blind)" (prop_rsa_oaep_valid False True)
+    , testProperty "RSA(OAEP) (fast+blind)" (prop_rsa_oaep_valid True  True)
+    ]
+
+asymSignatureTests = testGroup "assymmetric cipher signature"
+    [ testProperty "RSA(PKCS15) (slow)" prop_rsa_sign_slow_valid
+    , testProperty "RSA(PKCS15) (fast)" prop_rsa_sign_fast_valid
+    , testProperty "DSA" prop_dsa_valid
+    ]
+
+asymOtherTests = testGroup "assymetric other tests"
+    [ testProperty "DH valid" prop_dh_valid
+    ]
+
+main = defaultMain
+    [ asymEncryptionTests
+    , asymSignatureTests
+    , asymOtherTests
+    , testGroup "KATs" katTests
+    ]
diff --git a/crypto-pubkey.cabal b/crypto-pubkey.cabal
new file mode 100644
--- /dev/null
+++ b/crypto-pubkey.cabal
@@ -0,0 +1,77 @@
+Name:                crypto-pubkey
+Version:             0.1.0
+Description:
+    Public Key cryptography
+    .
+    Supports RSA PKCS15, RSA OAEP, RSA PSS, DSA, ElGamal signature.
+    .
+    Also have primitive support for Diffie Hellman, and ElGamal encryption
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          Vincent Hanquez <vincent@snarc.org>
+Synopsis:            Public Key cryptography
+Category:            Cryptography
+Build-Type:          Simple
+Homepage:            http://github.com/vincenthz/hs-crypto-pubkey
+Cabal-Version:       >=1.8
+Extra-Source-Files:  Tests/*.hs
+
+Flag benchmark
+  Description:       Build benchmarks
+  Default:           False
+
+Library
+  Build-Depends:     base >= 4 && < 5
+                   , bytestring
+                   , crypto-random-api
+                   , crypto-pubkey-types >= 0.2 && < 0.3
+                   , cryptohash >= 0.8
+                   , crypto-numbers
+  Exposed-modules:   Crypto.PubKey.RSA
+                     Crypto.PubKey.RSA.PKCS15
+                     Crypto.PubKey.RSA.OAEP
+                     Crypto.PubKey.RSA.PSS
+                     Crypto.PubKey.RSA.Prim
+                     Crypto.PubKey.DSA
+                     Crypto.PubKey.DH
+                     Crypto.PubKey.HashDescr
+                     Crypto.PubKey.MaskGenFunction
+  other-modules:     Crypto.PubKey.ElGamal
+                     Crypto.PubKey.RSA.Types
+                     Crypto.PubKey.Internal
+  ghc-options:       -Wall -O2
+
+Test-Suite test-crypto-pubkey
+  type:              exitcode-stdio-1.0
+  hs-source-dirs:    Tests
+  Main-Is:           Tests.hs
+  Build-depends:     base >= 4 && < 5
+                   , bytestring
+                   , cryptohash
+                   , crypto-pubkey
+                   , crypto-numbers
+                   , crypto-random-api
+                   , QuickCheck >= 2
+                   , HUnit
+                   , test-framework >= 0.3.3
+                   , test-framework-quickcheck2 >= 0.2.9
+                   , test-framework-hunit
+
+Benchmark bench-crypto-pubkey
+  hs-source-dirs:    Benchs
+  Main-Is:           Bench.hs
+  type:              exitcode-stdio-1.0
+  Build-depends:     base >= 4 && < 5
+                   , bytestring
+                   , cprng-aes >= 0.3.0
+                   , cryptohash
+                   , crypto-random-api
+                   , crypto-pubkey
+                   , criterion
+                   , mtl
+
+source-repository head
+  type:     git
+  location: git://github.com/vincenthz/hs-cryptocipher
