diff --git a/Data/ASN1/BER.hs b/Data/ASN1/BER.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/BER.hs
@@ -0,0 +1,107 @@
+-- |
+-- Module      : Data.ASN1.BER
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- A module containing ASN1 BER specification serialization/derialization tools
+--
+module Data.ASN1.BER (
+	TagClass(..),
+	ASN1(..),
+
+	-- * BER interface when using directly Raw objects
+	ofRaw,
+	toRaw,
+
+	-- * BER serial functions
+	decodeASN1Get,
+	decodeASN1,
+	encodeASN1Put,
+	encodeASN1
+	) where
+
+import Data.ASN1.Raw
+import Data.ASN1.Prim
+import Data.Either
+import Data.Binary.Get
+import Data.Binary.Put
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString as B
+
+ofRaws :: [Value] -> Either ASN1Err [ASN1]
+ofRaws x = if l == [] then Right r else Left $ ASN1Multiple l
+	where
+		(l, r) = partitionEithers $ map ofRaw x
+
+ofRaw :: Value -> Either ASN1Err ASN1
+ofRaw (Value Universal 0x0 (Primitive b))    = getEOC b
+ofRaw (Value Universal 0x1 (Primitive b))    = getBoolean False b
+ofRaw (Value Universal 0x2 (Primitive b))    = getInteger b
+ofRaw (Value Universal 0x3 v)                = getBitString v
+ofRaw (Value Universal 0x4 v)                = getOctetString v
+ofRaw (Value Universal 0x5 (Primitive b))    = getNull b
+ofRaw (Value Universal 0x6 (Primitive b))    = getOID b
+ofRaw (Value Universal 0x7 (Primitive _))    = Left $ ASN1NotImplemented "Object Descriptor"
+ofRaw (Value Universal 0x8 (Constructed _))  = Left $ ASN1NotImplemented "External"
+ofRaw (Value Universal 0x9 (Primitive _))    = Left $ ASN1NotImplemented "real"
+ofRaw (Value Universal 0xa (Primitive _))    = Left $ ASN1NotImplemented "enumerated"
+ofRaw (Value Universal 0xb (Constructed _))  = Left $ ASN1NotImplemented "EMBEDDED PDV"
+ofRaw (Value Universal 0xc v)                = getUTF8String v
+ofRaw (Value Universal 0xd (Primitive _))    = Left $ ASN1NotImplemented "RELATIVE-OID"
+ofRaw (Value Universal 0x10 (Constructed l)) = either Left (Right . Sequence) $ ofRaws l
+ofRaw (Value Universal 0x11 (Constructed l)) = either Left (Right . Set) $ ofRaws l
+ofRaw (Value Universal 0x12 v)               = getNumericString v
+ofRaw (Value Universal 0x13 v)               = getPrintableString v
+ofRaw (Value Universal 0x14 v)               = getT61String v
+ofRaw (Value Universal 0x15 v)               = getVideoTexString v
+ofRaw (Value Universal 0x16 v)               = getIA5String v
+ofRaw (Value Universal 0x17 x)               = getUTCTime x
+ofRaw (Value Universal 0x18 x)               = getGeneralizedTime x
+ofRaw (Value Universal 0x19 _)               = Left $ ASN1NotImplemented "x"
+ofRaw (Value Universal 0x1a _)               = Left $ ASN1NotImplemented "x"
+ofRaw (Value Universal 0x1b _)               = Left $ ASN1NotImplemented "x"
+ofRaw (Value Universal 0x1c _)               = Left $ ASN1NotImplemented "x"
+ofRaw (Value Universal 0x1d _)               = Left $ ASN1NotImplemented "CHARACTER STRING"
+ofRaw (Value Universal 0x1e _)               = Left $ ASN1NotImplemented "BMPString"
+ofRaw (Value tc tn (Primitive b))            = Right $ Other tc tn (Left b)
+ofRaw (Value tc tn (Constructed l))          = either Left (Right . Other tc tn . Right) $ ofRaws l
+
+toRaw :: ASN1 -> Value
+toRaw EOC                    = Value Universal 0x0 (Primitive B.empty)
+toRaw (Boolean v)            = Value Universal 0x1 (Primitive $ B.singleton (if v then 0xff else 0))
+toRaw (IntVal i)             = Value Universal 0x2 (putInteger i)
+toRaw (BitString i bits)     = Value Universal 0x3 (putBitString i bits)
+toRaw (OctetString b)        = Value Universal 0x4 (putString b)
+toRaw Null                   = Value Universal 0x5 (Primitive $ B.empty)
+toRaw (OID oid)              = Value Universal 0x6 (putOID oid)
+toRaw (Real f)               = Value Universal 0x9 (Constructed []) -- not implemented
+toRaw Enumerated             = Value Universal 0xa (Constructed []) -- not implemented
+toRaw (UTF8String b)         = Value Universal 0xc (putString b)
+toRaw (Sequence children)    = Value Universal 0x10 (Constructed $ map toRaw children)
+toRaw (Set children)         = Value Universal 0x11 (Constructed $ map toRaw children)
+toRaw (NumericString b)      = Value Universal 0x12 (putString b)
+toRaw (PrintableString b)    = Value Universal 0x13 (putString b)
+toRaw (T61String b)          = Value Universal 0x14 (putString b)
+toRaw (VideoTexString b)     = Value Universal 0x15 (putString b)
+toRaw (IA5String b)          = Value Universal 0x16 (putString b)
+toRaw (UTCTime time)         = Value Universal 0x17 (putUTCTime time)
+toRaw (GeneralizedTime time) = Value Universal 0x18 (putGeneralizedTime time)
+toRaw (GraphicString b)      = Value Universal 0x19 (putString b)
+toRaw (VisibleString b)      = Value Universal 0x1a (putString b)
+toRaw (GeneralString b)      = Value Universal 0x1b (putString b)
+toRaw (UniversalString b)    = Value Universal 0x1c (putString b)
+toRaw (Other tc tn c)        = Value tc tn (either Primitive (Constructed . map toRaw) c)
+
+decodeASN1Get :: Get (Either ASN1Err ASN1)
+decodeASN1Get = runGetErrInGet getValue >>= return . either Left ofRaw
+
+decodeASN1 :: L.ByteString -> Either ASN1Err ASN1
+decodeASN1 b = either Left ofRaw $ runGetErr getValue b
+
+encodeASN1Put :: ASN1 -> Put
+encodeASN1Put d = putValue $ toRaw d
+
+encodeASN1 :: ASN1 -> L.ByteString
+encodeASN1 = runPut . encodeASN1Put
diff --git a/Data/ASN1/CER.hs b/Data/ASN1/CER.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/CER.hs
@@ -0,0 +1,91 @@
+-- |
+-- Module      : Data.ASN1.CER
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- A module containing ASN1 CER specification serialization/derialization tools
+--
+module Data.ASN1.CER (
+	TagClass(..),
+	ASN1(..),
+
+	-- * CER serial functions
+	decodeASN1Get,
+	decodeASN1,
+	encodeASN1Put,
+	encodeASN1
+	) where
+
+import Data.ASN1.Raw
+import Data.ASN1.Prim
+import Data.Binary.Get
+import Data.Binary.Put
+import qualified Data.ASN1.BER as BER
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+
+{- | check if the value is bounded by CER policies -}
+check :: (TagClass, Bool, TagNumber) -> ValLength -> Maybe ASN1Err
+check (_, _, _) _ = Nothing
+
+{- | ofRaw same as BER.ofRAW but check some additional CER constraint. -}
+ofRaw :: Value -> Either ASN1Err ASN1
+ofRaw v = BER.ofRaw v
+
+
+{-
+- 9.2        String encoding forms
+string values shall be encoded with a primitive encoding if they would
+require no more than 1000 contents octets, and as a constructed encoding otherwise.
+The string fragments contained in the constructed encoding shall be encoded with a primitive encoding.
+The encoding of each fragment, except possibly the last, shall have 1000 contents octets.
+-}
+putStringCER :: (L.ByteString -> ASN1) -> L.ByteString -> ValStruct
+putStringCER obj l =
+	if L.length l > 1000
+		then Constructed $ map (toRaw . obj) $ repack1000 l
+		else Primitive $ B.concat $ L.toChunks l
+	where
+		repack1000 x =
+			if L.length x > 1000
+				then 
+					let (x1, x2) = L.splitAt 1000 x in
+					x1 : repack1000 x2
+				else
+					[ x ]
+
+{- | toRaw create a CER encoded value ready -}
+toRaw :: ASN1 -> Value
+toRaw (BitString i bits)     = Value Universal 0x3 (putBitString i bits)
+toRaw (OctetString b)        = Value Universal 0x4 (putStringCER OctetString b)
+toRaw (UTF8String b)         = Value Universal 0xc (putStringCER UTF8String b)
+toRaw (NumericString b)      = Value Universal 0x12 (putStringCER NumericString b)
+toRaw (PrintableString b)    = Value Universal 0x13 (putStringCER PrintableString b)
+toRaw (T61String b)          = Value Universal 0x14 (putStringCER T61String b)
+toRaw (VideoTexString b)     = Value Universal 0x15 (putStringCER VideoTexString b)
+toRaw (IA5String b)          = Value Universal 0x16 (putStringCER IA5String b)
+toRaw (GraphicString b)      = Value Universal 0x19 (putStringCER GraphicString b)
+toRaw (VisibleString b)      = Value Universal 0x1a (putStringCER VisibleString b)
+toRaw (GeneralString b)      = Value Universal 0x1b (putStringCER GeneralString b)
+toRaw (UniversalString b)    = Value Universal 0x1c (putStringCER UniversalString b)
+toRaw v                      = BER.toRaw v
+
+decodeASN1Get :: Get (Either ASN1Err ASN1)
+decodeASN1Get = runGetErrInGet (getValueCheck check) >>= return . either Left ofRaw
+
+decodeASN1 :: L.ByteString -> Either ASN1Err ASN1
+decodeASN1 b = either Left ofRaw $ runGetErr (getValueCheck check) b
+
+encodePolicyCER :: Value -> Int -> ValLength
+encodePolicyCER (Value _ _ (Primitive _)) len
+	| len < 0x80   = LenShort len
+	| otherwise    = LenLong 0 len
+encodePolicyCER (Value _ _ (Constructed _)) _ = LenIndefinite
+
+encodeASN1Put :: ASN1 -> Put
+encodeASN1Put d = putValuePolicy encodePolicyCER $ toRaw d
+
+encodeASN1 :: ASN1 -> L.ByteString
+encodeASN1 = runPut . encodeASN1Put
diff --git a/Data/ASN1/DER.hs b/Data/ASN1/DER.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/DER.hs
@@ -0,0 +1,65 @@
+-- |
+-- Module      : Data.ASN1.DER
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- A module containing ASN1 DER specification serialization/derialization tools
+--
+module Data.ASN1.DER (
+	TagClass(..),
+	ASN1(..),
+
+	-- * DER serialize functions
+	decodeASN1Get,
+	decodeASN1,
+	encodeASN1Put,
+	encodeASN1
+	) where
+
+import Data.ASN1.Raw
+import Data.ASN1.Prim
+import Data.Binary.Get
+import Data.Binary.Put
+import Control.Monad (mplus)
+import qualified Data.ASN1.BER as BER
+import qualified Data.ByteString.Lazy as L
+
+{- | Check if the length is the minimum possible and it's not indefinite -}
+checkLength :: ValLength -> Maybe ASN1Err
+checkLength LenIndefinite = Just $ ASN1PolicyFailed "DER" "indefinite length not allowed"
+checkLength (LenShort _)  = Nothing
+checkLength (LenLong n i)
+	| n == 1 && i < 0x80  = Just $ ASN1PolicyFailed "DER" "long length should be a short length"
+	| n == 1 && i >= 0x80 = Nothing
+	| otherwise           = if i >= 2^((n-1)*8) && i < 2^(n*8) then Nothing else Just $ ASN1PolicyFailed "DER" "long length is not shortest"
+
+{- | check if the value type is correct -}
+checkType :: TagClass -> TagNumber -> Maybe ASN1Err
+checkType _ _ = Nothing
+
+{- | check if the value is bounded by DER policies -}
+check :: (TagClass, Bool, TagNumber) -> ValLength -> Maybe ASN1Err
+check (tc,_,tn) vallen = checkLength vallen `mplus` checkType tc tn
+
+{- | ofRaw same as BER.ofRAW but check some additional DER constraint. -}
+ofRaw :: Value -> Either ASN1Err ASN1
+ofRaw (Value Universal 0x1 (Primitive b)) = getBoolean True b
+ofRaw v                                   = BER.ofRaw v
+
+{- | toRaw create a DER encoded value ready -}
+toRaw :: ASN1 -> Value
+toRaw = BER.toRaw
+
+decodeASN1Get :: Get (Either ASN1Err ASN1)
+decodeASN1Get = runGetErrInGet (getValueCheck check) >>= return . either Left ofRaw
+
+decodeASN1 :: L.ByteString -> Either ASN1Err ASN1
+decodeASN1 b = either Left BER.ofRaw $ runGetErr (getValueCheck check) b
+
+encodeASN1Put :: ASN1 -> Put
+encodeASN1Put d = putValue $ toRaw d
+
+encodeASN1 :: ASN1 -> L.ByteString
+encodeASN1 = runPut . encodeASN1Put
diff --git a/Data/ASN1/Internal.hs b/Data/ASN1/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/Internal.hs
@@ -0,0 +1,45 @@
+module Data.ASN1.Internal (
+	uintOfBytes,
+	intOfBytes,
+	bytesOfUInt,
+	bytesOfInt
+	) where
+
+import Data.Word
+import Data.Bits
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+
+{- | uintOfBytes returns the number of bytes and the unsigned integer represented by the bytes -}
+uintOfBytes :: ByteString -> (Int, Integer)
+uintOfBytes b = (B.length b, B.foldl (\acc n -> (acc `shiftL` 8) + fromIntegral n) 0 b)
+
+--bytesOfUInt i = B.unfoldr (\x -> if x == 0 then Nothing else Just (fromIntegral (x .&. 0xff), x `shiftR` 8)) i
+bytesOfUInt :: Integer -> [Word8]
+bytesOfUInt x = reverse (list x)
+	where
+		list i = if i <= 0xff then [fromIntegral i] else (fromIntegral i .&. 0xff) : list (i `shiftR` 8)
+
+{- | intOfBytes returns the number of bytes in the list and
+   the represented integer by a two's completement list of bytes -}
+intOfBytes :: ByteString -> (Int, Integer)
+intOfBytes b
+	| B.length b == 0   = (0, 0)
+	| otherwise         = (len, if isNeg then -(maxIntLen - v + 1) else v)
+	where
+		(len, v)  = uintOfBytes b
+		maxIntLen = 2 ^ (8 * len) - 1
+		isNeg     = testBit (B.head b) 7
+
+{- | bytesOfInt convert an integer into a two's completemented list of bytes -}
+bytesOfInt :: Integer -> [Word8]
+bytesOfInt i
+	| i > 0      = ints
+	| i == 0     = [0]
+	| otherwise  = reverse $ plusOne $ reverse $ map complement $ ints
+	where
+		uints = bytesOfUInt (abs i)
+		isNeg = testBit (head uints) 7
+		ints  = if isNeg then 0 : uints else uints
+		plusOne []     = []
+		plusOne (x:xs) = if x == 0xff then 0 : plusOne xs else (x+1) : xs
diff --git a/Data/ASN1/Prim.hs b/Data/ASN1/Prim.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/Prim.hs
@@ -0,0 +1,253 @@
+-- |
+-- Module      : Data.ASN1.Prim
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- Tools to read ASN1 primitive (e.g. boolean, int)
+--
+
+module Data.ASN1.Prim (
+	-- | ASN1 high level algebraic type
+	ASN1(..),
+
+	-- | marshall an ASN1 type from a val struct or a bytestring
+	getEOC,
+	getBoolean,
+	getInteger,
+	getBitString,
+	getOctetString,
+	getUTF8String,
+	getNumericString,
+	getPrintableString,
+	getT61String,
+	getVideoTexString,
+	getIA5String,
+	getNull,
+	getOID,
+	getUTCTime,
+	getGeneralizedTime,
+
+	-- | marshall an ASN1 type to a bytestring
+	putUTCTime,
+	putGeneralizedTime,
+	putInteger,
+	putBitString,
+	putString,
+	putOID
+	) where
+
+import Data.ASN1.Internal
+import Data.ASN1.Raw
+import Data.Bits
+import Data.Word
+import Data.Maybe (catMaybes)
+import Data.List (unfoldr)
+import Data.ByteString (ByteString)
+import Data.Char (ord)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+
+data ASN1 =
+	  EOC
+	| Boolean Bool
+	| IntVal Integer
+	| BitString Int L.ByteString
+	| OctetString L.ByteString
+	| Null
+	| OID [Integer]
+	| Real Double
+	| Enumerated
+	| UTF8String L.ByteString
+	| Sequence [ASN1]
+	| Set [ASN1]
+	| NumericString L.ByteString
+	| PrintableString L.ByteString
+	| T61String L.ByteString
+	| VideoTexString L.ByteString
+	| IA5String L.ByteString
+	| UTCTime (Int, Int, Int, Int, Int, Int, Bool)
+	| GeneralizedTime (Int, Int, Int, Int, Int, Int, Bool)
+	| GraphicString L.ByteString
+	| VisibleString L.ByteString
+	| GeneralString L.ByteString
+	| UniversalString L.ByteString
+	| Other TagClass TagNumber (Either ByteString [ASN1])
+	deriving (Show, Eq)
+
+getEOC :: ByteString -> Either ASN1Err ASN1
+getEOC s =
+	if B.length s == 0
+		then Right $ EOC
+		else Left $ ASN1Misc "EOC: data length not within bound"
+
+getBoolean :: Bool -> ByteString -> Either ASN1Err ASN1
+getBoolean isDer s =
+	if B.length s == 1
+		then
+			case B.head s of
+				0    -> Right (Boolean False)
+				0xff -> Right (Boolean True)
+				_    -> if isDer then Left $ ASN1PolicyFailed "DER" "boolean value not canonical" else Right (Boolean True)
+		else Left $ ASN1Misc "boolean: length not within bound"
+
+{- | getInteger, parse a value bytestring and get the integer out of the two complement encoded bytes -}
+getInteger :: ByteString -> Either ASN1Err ASN1
+getInteger s
+	| B.length s == 0 = Left $ ASN1Misc "integer: null encoding"
+	| B.length s == 1 = Right $ IntVal $ snd $ intOfBytes s
+	| otherwise       =
+		if (v1 == 0xff && testBit v2 7) || (v1 == 0x0 && (not $ testBit v2 7))
+			then Left $ ASN1Misc "integer: not shortest encoding"
+			else Right $ IntVal $ snd $ intOfBytes s
+		where
+			v1 = s `B.index` 0
+			v2 = s `B.index` 1
+
+
+getBitString :: ValStruct -> Either ASN1Err ASN1
+getBitString (Primitive s) =
+	let toSkip = B.head s in
+	let toSkip' = if toSkip >= 48 && toSkip <= 48 + 7 then toSkip - (fromIntegral $ ord '0') else toSkip in
+	let xs = B.tail s in
+	if toSkip' >= 0 && toSkip' <= 7
+		then Right $ BitString (fromIntegral toSkip') (L.fromChunks [xs])
+		else Left $ ASN1Misc ("bitstring: skip number not within bound " ++ show toSkip' ++ " " ++  show s)
+
+getBitString (Constructed _) = Left $ ASN1NotImplemented "bitstring"
+
+getString :: (Either ByteString Value -> Maybe ASN1Err) -> ValStruct -> Either ASN1Err L.ByteString
+getString check (Primitive s) =
+	case check (Left s) of
+		Nothing  -> Right $ L.fromChunks [s]
+		Just err -> Left err
+
+getString check (Constructed l) =
+	case catMaybes $ map (check . Right) l of
+		[]   -> Right $ L.fromChunks $ map catPrimitiveString l
+		errs -> Left $ ASN1Multiple errs
+	where
+		catPrimitiveString (Value _ _ (Primitive b)) = b
+		catPrimitiveString (Value _ _ (Constructed _)) = B.empty {- FIXME -}
+
+getOctetString :: ValStruct -> Either ASN1Err ASN1
+getOctetString = either Left (Right . OctetString) . getString (\_ -> Nothing)
+
+getNumericString :: ValStruct -> Either ASN1Err ASN1
+getNumericString = either Left (Right . NumericString) . getString (\_ -> Nothing)
+
+getPrintableString :: ValStruct -> Either ASN1Err ASN1
+getPrintableString = either Left (Right . PrintableString) . getString (\_ -> Nothing)
+
+getUTF8String :: ValStruct -> Either ASN1Err ASN1
+getUTF8String = either Left (Right . UTF8String) . getString (\_ -> Nothing)
+
+getT61String :: ValStruct -> Either ASN1Err ASN1
+getT61String = either Left (Right . T61String) . getString (\_ -> Nothing)
+
+getVideoTexString :: ValStruct -> Either ASN1Err ASN1
+getVideoTexString = either Left (Right . VideoTexString) . getString (\_ -> Nothing)
+
+getIA5String :: ValStruct -> Either ASN1Err ASN1
+getIA5String = either Left (Right . IA5String) . getString (\_ -> Nothing)
+
+getNull :: ByteString -> Either ASN1Err ASN1
+getNull s = if B.length s == 0 then Right Null else Left $ ASN1Misc "Null: data length not within bound"
+
+{- | return an OID -}
+getOID :: ByteString -> Either ASN1Err ASN1
+getOID s = Right $ OID $ (fromIntegral (x `div` 40) : fromIntegral (x `mod` 40) : groupOID xs)
+	where
+		(x:xs) = B.unpack s
+
+		groupOID :: [Word8] -> [Integer]
+		groupOID = map (foldl (\acc n -> (acc `shiftL` 7) + fromIntegral n) 0) . groupSubOID
+
+		groupSubOIDHelper [] = Nothing
+		groupSubOIDHelper l  = Just $ spanSubOIDbound l
+
+		groupSubOID :: [Word8] -> [[Word8]]
+		groupSubOID = unfoldr groupSubOIDHelper
+
+		spanSubOIDbound [] = ([], [])
+		spanSubOIDbound (a:as) = if testBit a 7 then (clearBit a 7 : ys, zs) else ([a], as)
+			where (ys, zs) = spanSubOIDbound as
+
+getUTCTime :: ValStruct -> Either ASN1Err ASN1
+getUTCTime (Primitive s) =
+	case B.unpack s of
+		[y1, y2, m1, m2, d1, d2, h1, h2, mi1, mi2, s1, s2, z] ->
+			let y = integerise y1 y2 in
+			let year = 1900 + (if y <= 50 then y + 100 else y) in
+			let month = integerise m1 m2 in
+			let day = integerise d1 d2 in
+			let hour = integerise h1 h2 in
+			let minute = integerise mi1 mi2 in
+			let second = integerise s1 s2 in
+			Right $ UTCTime (year, month, day, hour, minute, second, z == 90)
+		_                                                     -> Left $ ASN1Misc "utctime unexpected format"
+	where
+		integerise a b = ((fromIntegral a) - (ord '0')) * 10 + ((fromIntegral b) - (ord '0'))
+
+getUTCTime (Constructed _) = Left $ ASN1NotImplemented "utctime constructed"
+
+getGeneralizedTime :: ValStruct -> Either ASN1Err ASN1
+getGeneralizedTime (Primitive s) =
+	case B.unpack s of
+		[y1, y2, y3, y4, m1, m2, d1, d2, h1, h2, mi1, mi2, s1, s2, z] ->
+			let year = (integerise y1 y2) * 100 + (integerise y3 y4) in
+			let month = integerise m1 m2 in
+			let day = integerise d1 d2 in
+			let hour = integerise h1 h2 in
+			let minute = integerise mi1 mi2 in
+			let second = integerise s1 s2 in
+			Right $ GeneralizedTime (year, month, day, hour, minute, second, z == 90)
+		_                                                     -> Left $ ASN1Misc "utctime unexpected format"
+	where
+		integerise a b = ((fromIntegral a) - (ord '0')) * 10 + ((fromIntegral b) - (ord '0'))
+getGeneralizedTime (Constructed _) = Left $ ASN1NotImplemented "generalizedtime constructed"
+
+putTime :: Bool -> (Int, Int, Int, Int, Int, Int, Bool) -> ValStruct
+putTime generalized (y,m,d,h,mi,s,z) =
+	Primitive $ B.pack etime
+	where
+		etime =
+			if generalized
+				then [y1, y2, y3, y4, m1, m2, d1, d2, h1, h2, mi1, mi2, s1, s2, if z then 90 else 0 ]
+				else [y3, y4, m1, m2, d1, d2, h1, h2, mi1, mi2, s1, s2, if z then 90 else 0 ]
+		split2 n          = (fromIntegral $ n `div` 10 + ord '0', fromIntegral $ n `mod` 10 + ord '0')
+		((y1,y2),(y3,y4)) = (split2 (y `div` 100), split2 (y `mod` 100))
+		(m1, m2)          = split2 m
+		(d1, d2)          = split2 d
+		(h1, h2)          = split2 h
+		(mi1, mi2)        = split2 mi
+		(s1, s2)          = split2 s
+
+putUTCTime :: (Int, Int, Int, Int, Int, Int, Bool) -> ValStruct
+putUTCTime time = putTime False time
+
+putGeneralizedTime :: (Int, Int, Int, Int, Int, Int, Bool) -> ValStruct
+putGeneralizedTime time = putTime True time
+
+putInteger :: Integer -> ValStruct
+putInteger i = Primitive $ B.pack $ bytesOfInt i
+
+putBitString :: Int -> L.ByteString -> ValStruct
+putBitString i bits = Primitive $ B.concat $ B.singleton (fromIntegral i) : L.toChunks bits
+
+putString :: L.ByteString -> ValStruct
+putString l = Primitive $ B.concat $ L.toChunks l
+
+{- no enforce check that we oid1 is between [0..2] and oid2 is between [0..39] -}
+putOID :: [Integer] -> ValStruct
+putOID oids = Primitive $ B.pack $ eoid
+	where
+		(oid1:oid2:suboids) = oids
+		eoidclass           = fromIntegral (oid1 * 40 + oid2)
+		ungroupSubOID x     = unfoldr (\i -> if i == 0 then Nothing else Just (fromIntegral (i .&. 0x7f), i `shiftR` 7)) x
+		setHighBits []      = []
+		setHighBits [x]     = [x]
+		setHighBits (x:xs)  = setBit x 7 : setHighBits xs
+		subeoids            = concatMap (setHighBits . reverse . ungroupSubOID) suboids
+		eoid                = eoidclass : subeoids
diff --git a/Data/ASN1/Raw.hs b/Data/ASN1/Raw.hs
new file mode 100644
--- /dev/null
+++ b/Data/ASN1/Raw.hs
@@ -0,0 +1,267 @@
+{-#  LANGUAGE GeneralizedNewtypeDeriving #-}
+-- |
+-- Module      : Data.ASN1.Raw
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- A module containing raw ASN1 serialization/derialization tools
+--
+module Data.ASN1.Raw (
+	GetErr,
+	runGetErr,
+	runGetErrInGet,
+	ASN1Err(..),
+	CheckFn,
+	TagClass(..),
+	TagNumber,
+	ValLength(..),
+	ValStruct(..),
+	Value(..),
+	getValueCheck,
+	getValue,
+	putValuePolicy,
+	putValue
+	) where
+
+import Data.Bits
+import Data.ASN1.Internal
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.ByteString (ByteString)
+import Data.Word
+import Control.Monad.Error
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+
+data TagClass =
+	  Universal
+	| Application
+	| Context
+	| Private
+	deriving (Show, Eq)
+
+data ValLength =
+	  LenShort Int      -- | Short form with only one byte. length has to be < 127.
+	| LenLong Int Int   -- | Long form of N bytes
+	| LenIndefinite     -- | Length is indefinite expect an EOC in the stream to finish the type
+	deriving (Show, Eq)
+
+type TagNumber = Int
+type TagConstructed = Bool
+type Identifier = (TagClass, TagConstructed, TagNumber)
+
+data ValStruct =
+	  Primitive ByteString -- | Primitive of a strict value
+	| Constructed [Value]  -- | Constructed of a list of values
+	deriving (Show, Eq)
+
+data Value = Value TagClass TagNumber ValStruct
+	deriving (Show, Eq)
+
+data ASN1Err =
+	  ASN1LengthDecodingLongContainsZero
+	| ASN1PolicyFailed String String
+	| ASN1NotImplemented String
+	| ASN1Multiple [ASN1Err]
+	| ASN1Misc String
+	deriving (Show, Eq)
+
+type CheckFn = (TagClass, Bool, TagNumber) -> ValLength -> Maybe ASN1Err
+
+instance Error ASN1Err where
+	noMsg = ASN1Misc ""
+	strMsg = ASN1Misc
+
+newtype GetErr a = GE { runGE :: ErrorT ASN1Err Get a }
+	deriving (Monad, MonadError ASN1Err)
+
+instance Functor GetErr where
+	fmap f = GE . fmap f . runGE
+
+runGetErr :: GetErr a -> L.ByteString -> Either ASN1Err a
+runGetErr f b = runGet (runErrorT (runGE f)) b
+
+runGetErrInGet :: GetErr a -> Get (Either ASN1Err a)
+runGetErrInGet f = runErrorT (runGE f)
+
+liftGet :: Get a -> GetErr a
+liftGet = GE . lift
+
+geteWord8 :: GetErr Word8
+geteWord8 = liftGet getWord8
+
+geteBytes :: Int -> GetErr ByteString
+geteBytes = liftGet . getBytes
+
+{- marshall helper for getIdentifier to unserialize long tag number -}
+getTagNumberLong :: Bool -> GetErr TagNumber
+getTagNumberLong nz = do
+	t <- geteWord8
+	let tval = fromIntegral (t .&. 0x7f)
+	when (nz && tval == 0) $ error "long tag encoding failure: first value is 0"
+	if (t .&. 0x80) > 0
+		then do
+			trest <- getTagNumberLong False
+			return ((tval `shiftL` 7) + trest)
+		else
+			return tval
+
+{- marshall helper for putIdentifier to serialize long tag number -}
+putTagNumberLong :: TagNumber -> Put
+putTagNumberLong i = do
+	if i > 0x7f
+		then do
+			putWord8 $ fromIntegral (0x80 .|. (i .&. 0x7f))
+			putTagNumberLong (i `shiftR` 7)
+		else
+			putWord8 $ fromIntegral (i .&. 0x7f)
+
+{- | getIdentifier get an ASN1 encoded Identifier.
+ - if the first 5 bytes value is less than 1f then it's encoded on 1 byte, otherwise
+ - read bytes until the 8th bit is not set -}
+getIdentifier :: GetErr Identifier
+getIdentifier = do
+	w <- geteWord8
+	let cl =
+		case (w `shiftR` 7) .&. 3 of
+			0 -> Universal
+			1 -> Application 
+			2 -> Context
+			3 -> Private
+			_ -> Universal -- this cannot happens because of the .&. 3
+	let pc = (w .&. 0x20) > 0
+	let val = fromIntegral (w .&. 0x1f)
+	vencoded <- if val < 0x1f then return val else getTagNumberLong True
+	return $ (cl, pc, vencoded)
+
+{- | putIdentifier encode an ASN1 Identifier into a marshalled value -}
+putIdentifier :: Identifier -> Put
+putIdentifier (cl, pc, val) = do
+	let cli = case cl of
+		Universal   -> 0
+		Application -> 1
+		Context     -> 2
+		Private     -> 3
+	let pcval = if pc then 0x20 else 0x00
+	if val < 0x1f
+		then
+			putWord8 $ fromIntegral $ (cli `shiftL` 7) .|. pcval .|. (val)
+		else do
+			putWord8 $ fromIntegral $ (cli `shiftL` 7) .|. pcval .|. 0x1f
+			putTagNumberLong val
+
+{- | getLength get the ASN1 encoded length of an item.
+ - if less than 0x80 then it's encoded on 1 byte, otherwise
+ - the first byte is the number of bytes to read as the length.
+ - if the number of bytes is 0, then the length is indefinite,
+ - and the content length is bounded by an EOC -}
+getLength :: GetErr ValLength
+getLength = do
+	l1 <- geteWord8
+	if testBit l1 7
+		then do
+			case fromIntegral (clearBit l1 7) of
+				0   -> return LenIndefinite
+				len -> do
+					lw <- geteBytes len
+					return $ LenLong len (fromIntegral $ snd $ uintOfBytes lw)
+		else
+			return $ LenShort $ fromIntegral l1
+
+{- | putLength encode a length into a ASN1 length.
+ - see getLength for the encoding rules -}
+putLength :: ValLength -> Put
+putLength (LenShort i) = do
+	when (i < 0 || i > 0x7f) (error "putLength: short length is not between 0x0 and 0x80")
+	putWord8 (fromIntegral i)
+
+putLength (LenLong _ i) = do
+	when (i < 0) (error "putLength: long length is negative")
+	let lw = bytesOfUInt $ fromIntegral i
+	let lenbytes = fromIntegral (length lw .|. 0x80)
+	putWord8 lenbytes
+	mapM_ putWord8 lw
+	
+putLength (LenIndefinite) = putWord8 0x80
+
+{- helper to getValue to build a constructed list of values when length is known -}
+getValueConstructed :: CheckFn -> GetErr [Value]
+getValueConstructed check = do
+	remain <- liftGet $ remaining
+	if remain > 0
+		then do
+			o <- getValueCheck check
+			l <- getValueConstructed check
+			return (o : l)
+		else
+			return []
+
+{- helper to getValue to build a constructed list of values when length is unknown -}
+getValueConstructedUntilEOC :: CheckFn -> GetErr [Value]
+getValueConstructedUntilEOC check = do
+	o <- getValueCheck check
+	case o of
+		-- technically EOC should also match (Primitive B.empty) (LenShort 0)
+		Value Universal 0 _ -> return []
+		_                   -> do
+			l <- getValueConstructedUntilEOC check
+			return (o : l)
+
+getValueOfLength :: CheckFn -> Int -> Bool -> GetErr ValStruct
+getValueOfLength check len pc = do
+	b <- geteBytes len
+	case pc of
+		True  -> do
+			case runGetErr (getValueConstructed check) (L.fromChunks [b]) of
+				Right x  -> return $ Constructed x
+				Left err -> throwError err
+		False -> return $ Primitive b
+
+{- | getValueCheck decode an ASN1 value and check the values received through the check fn -}
+getValueCheck :: CheckFn -> GetErr Value
+getValueCheck check = do
+	(tc, pc, tn) <- getIdentifier
+	vallen <- getLength
+
+	{- policy checker, if it returns an error, raise it -}
+	case check (tc, pc, tn) vallen of
+		Just err -> throwError err
+		Nothing  -> return ()
+
+	struct <- case vallen of
+		LenIndefinite -> do
+			when (not pc) $ throwError $ ASN1Misc "lenght indefinite not allowed with primitive"
+			vs <- getValueConstructedUntilEOC check
+			return $ Constructed vs
+		(LenShort len)  -> getValueOfLength check len pc
+		(LenLong _ len) -> getValueOfLength check len pc
+	return $ Value tc tn struct
+
+getValue :: GetErr Value
+getValue = getValueCheck (\_ _ -> Nothing)
+
+putValStruct :: ValStruct -> Put
+putValStruct (Primitive x)   = putByteString x
+putValStruct (Constructed l) = mapM_ putValue l
+
+putValuePolicy :: (Value -> Int -> ValLength) -> Value -> Put
+putValuePolicy policy v@(Value tc tn struct) = do
+	let pc =
+		case struct of
+			Primitive _   -> False
+			Constructed _ -> True
+	putIdentifier (tc, pc, tn)
+	let content = runPut (putValStruct struct)
+	let len = fromIntegral $ L.length content 
+	let lenEncoded = policy v len
+	putLength lenEncoded
+	putLazyByteString content
+	case lenEncoded of
+		LenIndefinite -> putValue $ Value Universal 0x0 (Primitive B.empty)
+		_             -> return ()
+
+{- | putValue encode an ASN1 value using the shortest definite length -}
+putValue :: Value -> Put
+putValue = putValuePolicy (\_ len -> if len < 0x80 then LenShort len else LenLong 0 len)
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2010 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.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+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/README b/README
new file mode 100644
--- /dev/null
+++ b/README
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/TODO b/TODO
new file mode 100644
--- /dev/null
+++ b/TODO
@@ -0,0 +1,1 @@
+remove FIXMEs
diff --git a/Tests.hs b/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests.hs
@@ -0,0 +1,81 @@
+import Test.QuickCheck
+import qualified Test.HUnit as Unit
+import Test.HUnit ((~:), (~=?))
+import Text.Printf
+import Data.ASN1.Raw
+import Data.ASN1.DER (ASN1(..))
+import qualified Data.ASN1.DER as DER
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.Word
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
+
+mkSeq l = Value Universal 0x10 (Constructed l)
+mkBool t = Value Universal 0x1 (Primitive $ B.pack [ if t then 0x1 else 0x0 ])
+mkInt t = Value Universal 0x2 (Primitive $ B.pack [ t ])
+
+famous_oids :: [[Integer]]
+famous_oids =
+	[ [2,1,2]
+	, [1,38,53]
+	, [2,24,840,24042,530,530]
+	, [0,20,84,249,59342,53295392,325993252935]
+	, [1,2,840,113549,1,1,1]
+	]
+
+instance Arbitrary Value where
+	arbitrary = elements [
+		mkSeq [],
+		mkSeq [ mkBool True, mkInt 124 ],
+		mkSeq [ mkSeq [ mkBool True, mkInt 124 ], mkSeq [] ]
+		]
+	coarbitrary (Value _ tn _) = variant tn
+
+instance Arbitrary ASN1 where
+	arbitrary = elements (
+		   [Boolean False,Boolean True,Null,EOC]
+		++ map IntVal ([0..512] ++ [10241024..10242048])
+		++ map OID famous_oids
+		)
+
+
+getVal = either (const (Value Application (-1) (Constructed []))) id . runGetErr getValue
+putVal = runPut . putValue
+
+decodeASN1 = either (const EOC) id . DER.decodeASN1
+
+prop_getput_value_id :: Value -> Bool
+prop_getput_value_id v = (getVal . putVal) v == v
+
+prop_getput_asn1_id :: ASN1 -> Bool
+prop_getput_asn1_id v = (DER.decodeASN1 . DER.encodeASN1) v == Right v
+
+tests =
+	[ ("get.put value/id", test prop_getput_value_id)
+	, ("get.put DER.ASN1/id", test prop_getput_asn1_id)
+	]
+
+quickCheckMain = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
+
+
+value_units :: [ (String, Value, [Word8]) ]
+value_units = [
+	("empty Sequence", mkSeq [], [ 0x30, 0x0 ]),
+	("long Sequence", mkSeq (replicate 50 (mkBool True)), [ 0x30, 0x81, 150 ] ++ (concat $ replicate 50 [ 0x01, 0x1, 0x1 ]))
+	]
+
+asn1_units :: [ (String, ASN1) ]
+asn1_units =
+	map (\oid -> ("OID " ++ show oid, OID oid)) famous_oids ++
+	[]
+
+utests :: [Unit.Test]
+utests =
+	map (\ (s, v, r) -> ("put " ++ s) ~: s ~: r ~=? (L.unpack $ putVal v)) value_units ++
+	map (\ (s, v, r) -> ("get " ++ s) ~: s ~: v ~=? (getVal $ L.pack r)) value_units ++
+	map (\ (s, v) -> ("decode/encode=id") ~: s ~: v ~=? (decodeASN1 . DER.encodeASN1) v) asn1_units
+
+main = do
+	Unit.runTestTT (Unit.TestList utests)
+	quickCheckMain
diff --git a/asn1-data.cabal b/asn1-data.cabal
new file mode 100644
--- /dev/null
+++ b/asn1-data.cabal
@@ -0,0 +1,45 @@
+Name:                asn1-data
+Version:             0.1
+Description:
+    ASN1 data reader/writer in raw form with supports for high level forms of ASN1 (BER/CER/DER)
+License:             BSD3
+License-file:        LICENSE
+Copyright:           Vincent Hanquez <vincent@snarc.org>
+Author:              Vincent Hanquez <vincent@snarc.org>
+Maintainer:          Vincent Hanquez <vincent@snarc.org>
+Synopsis:            ASN1 data reader/writer in RAW/BER/DER/CER forms
+Build-Type:          Simple
+Category:            Data
+stability:           experimental
+Cabal-Version:       >=1.6
+data-files:          README, TODO
+
+
+Flag test
+  Description:       Build unit test
+  Default:           False
+
+Library
+  Build-Depends:     base >= 3 && < 5,
+                     binary >= 0.5,
+                     bytestring,
+                     mtl
+  Exposed-modules:   Data.ASN1.BER
+                     Data.ASN1.CER
+                     Data.ASN1.DER
+                     Data.ASN1.Raw
+  other-modules:     Data.ASN1.Prim
+                     Data.ASN1.Internal
+  ghc-options:       -Wall
+
+Executable           Tests
+  Main-Is:           Tests.hs
+  if flag(test)
+    Buildable:       True
+    Build-depends:   base >= 3 && < 5, HUnit, QuickCheck, bytestring
+  else
+    Buildable:       False
+
+source-repository head
+  type:     git
+  location: git://github.com/vincenthz/hs-asn1-data
