packages feed

x509 1.6.5 → 1.7.7

raw patch · 14 files changed

Files

+ ChangeLog.md view
@@ -0,0 +1,6 @@+# ChangeLog for x509++## 2022-05-31 v1.7.7++- Bump requirements to GHC 7.8 and transformers 0.4 series [#130](https://github.com/haskell-tls/hs-certificate/pull/130)+
Data/X509.hs view
@@ -19,6 +19,7 @@     , PubKeyEC(..)     , SerializedPoint(..)     , PrivKey(..)+    , PrivKeyEC(..)     , pubkeyToAlg     , privkeyToAlg     , module Data.X509.AlgorithmIdentifier
Data/X509/AlgorithmIdentifier.hs view
@@ -31,14 +31,20 @@     | PubKeyALG_RSAPSS      -- ^ RSA PSS Key algorithm (RFC 3447)     | PubKeyALG_DSA         -- ^ DSA Public Key algorithm     | PubKeyALG_EC          -- ^ ECDSA & ECDH Public Key algorithm+    | PubKeyALG_X25519      -- ^ ECDH 25519 key agreement+    | PubKeyALG_X448        -- ^ ECDH 448 key agreement+    | PubKeyALG_Ed25519     -- ^ EdDSA 25519 signature algorithm+    | PubKeyALG_Ed448       -- ^ EdDSA 448 signature algorithm     | PubKeyALG_DH          -- ^ Diffie Hellman Public Key algorithm     | PubKeyALG_Unknown OID -- ^ Unknown Public Key algorithm     deriving (Show,Eq) --- | Signature Algorithm often composed of--- a public key algorithm and a hash algorithm+-- | Signature Algorithm, often composed of a public key algorithm and a hash+-- algorithm.  For some signature algorithms the hash algorithm is intrinsic to+-- the public key algorithm and is not needed in the data type. data SignatureALG =       SignatureALG HashALG PubKeyALG+    | SignatureALG_IntrinsicHash PubKeyALG     | SignatureALG_Unknown OID     deriving (Show,Eq) @@ -47,6 +53,10 @@     getObjectID PubKeyALG_RSAPSS = [1,2,840,113549,1,1,10]     getObjectID PubKeyALG_DSA    = [1,2,840,10040,4,1]     getObjectID PubKeyALG_EC     = [1,2,840,10045,2,1]+    getObjectID PubKeyALG_X25519    = [1,3,101,110]+    getObjectID PubKeyALG_X448      = [1,3,101,111]+    getObjectID PubKeyALG_Ed25519   = [1,3,101,112]+    getObjectID PubKeyALG_Ed448     = [1,3,101,113]     getObjectID PubKeyALG_DH     = [1,2,840,10046,2,1]     getObjectID (PubKeyALG_Unknown oid) = oid @@ -71,6 +81,8 @@         , ([2,16,840,1,101,3,4,2,4],  SignatureALG HashSHA224 PubKeyALG_RSAPSS)         , ([2,16,840,1,101,3,4,3,1],  SignatureALG HashSHA224 PubKeyALG_DSA)         , ([2,16,840,1,101,3,4,3,2],  SignatureALG HashSHA256 PubKeyALG_DSA)+        , ([1,3,101,112], SignatureALG_IntrinsicHash PubKeyALG_Ed25519)+        , ([1,3,101,113], SignatureALG_IntrinsicHash PubKeyALG_Ed448)         ]  oidSig :: OID -> SignatureALG@@ -90,13 +102,19 @@  instance ASN1Object SignatureALG where     fromASN1 (Start Sequence:OID oid:Null:End Sequence:xs) =-        Right (oidSig oid, xs)+        case oidSig oid of+            SignatureALG_IntrinsicHash _ ->+                Left "fromASN1: X509.SignatureALG: EdDSA requires absent parameter"+            signatureAlg -> Right (signatureAlg, xs)     fromASN1 (Start Sequence:OID oid:End Sequence:xs) =         Right (oidSig oid, xs)     fromASN1 (Start Sequence:OID [1,2,840,113549,1,1,10]:Start Sequence:Start _:Start Sequence:OID hash1:End Sequence:End _:Start _:Start Sequence:OID [1,2,840,113549,1,1,8]:Start Sequence:OID _hash2:End Sequence:End Sequence:End _:Start _: IntVal _iv: End _: End Sequence : End Sequence:xs) =         Right (oidSig hash1, xs)+    fromASN1 (Start Sequence:OID [1,2,840,113549,1,1,10]:Start Sequence:Start _:Start Sequence:OID hash1:Null:End Sequence:End _:Start _:Start Sequence:OID [1,2,840,113549,1,1,8]:Start Sequence:OID _hash2:Null:End Sequence:End Sequence:End _:Start _: IntVal _iv: End _: End Sequence : End Sequence:xs) =+        Right (oidSig hash1, xs)     fromASN1 _ =         Left "fromASN1: X509.SignatureALG: unknown format"-    toASN1 signatureAlg@(SignatureALG hashAlg PubKeyALG_RSAPSS) = \xs -> Start Sequence:OID [1,2,840,113549,1,1,10]:Start Sequence:Start (Container Context 0):Start Sequence:OID (sigOID signatureAlg):End Sequence:End (Container Context 0):Start (Container Context 1): Start Sequence:OID [1,2,840,113549,1,1,8]:Start Sequence:OID (sigOID signatureAlg):End Sequence:End Sequence:End (Container Context 1):Start (Container Context 2):IntVal (saltLen hashAlg):End (Container Context 2):End Sequence:End Sequence:xs-    toASN1 signatureAlg@(SignatureALG _ _) = \xs -> Start Sequence:OID (sigOID signatureAlg):Null:End Sequence:xs     toASN1 (SignatureALG_Unknown oid) = \xs -> Start Sequence:OID oid:Null:End Sequence:xs+    toASN1 signatureAlg@(SignatureALG hashAlg PubKeyALG_RSAPSS) = \xs -> Start Sequence:OID [1,2,840,113549,1,1,10]:Start Sequence:Start (Container Context 0):Start Sequence:OID (sigOID signatureAlg):End Sequence:End (Container Context 0):Start (Container Context 1): Start Sequence:OID [1,2,840,113549,1,1,8]:Start Sequence:OID (sigOID signatureAlg):End Sequence:End Sequence:End (Container Context 1):Start (Container Context 2):IntVal (saltLen hashAlg):End (Container Context 2):End Sequence:End Sequence:xs+    toASN1 signatureAlg@(SignatureALG_IntrinsicHash _) = \xs -> Start Sequence:OID (sigOID signatureAlg):End Sequence:xs+    toASN1 signatureAlg = \xs -> Start Sequence:OID (sigOID signatureAlg):Null:End Sequence:xs
Data/X509/CRL.hs view
@@ -48,14 +48,30 @@     toASN1 crl = encodeCRL crl     fromASN1 = runParseASN1State parseCRL --- TODO support extension instance ASN1Object RevokedCertificate where-    fromASN1 (Start Sequence : IntVal serial : ASN1Time _ t _ : End Sequence : xs) =-        Right (RevokedCertificate serial t (Extensions Nothing), xs)-    fromASN1 l = Left ("fromASN1: X509.RevokedCertificate: unknown format:" ++ show l)-    toASN1 (RevokedCertificate serial time _) = \xs ->-        Start Sequence : IntVal serial : ASN1Time TimeGeneralized time (Just (TimezoneOffset 0)) : End Sequence : xs+    fromASN1 = runParseASN1State $+        onNextContainer Sequence $+        RevokedCertificate+        <$> parseSerialNumber+        <*> (getNext >>= toTime)+        <*> getObject+      where toTime (ASN1Time _ t _) = pure t+            toTime _                = throwParseError "bad revocation date"+    toASN1 (RevokedCertificate serial time crlEntryExtensions) = \xs ->+        [ Start Sequence ] +++        [ IntVal serial ] +++        [ ASN1Time TimeGeneralized time (Just (TimezoneOffset 0)) ] +++        toASN1 crlEntryExtensions [] +++        [ End Sequence ] +++        xs +parseSerialNumber :: ParseASN1 Integer+parseSerialNumber = do+    n <- getNext+    case n of+        IntVal v -> return v+        _        -> throwParseError ("missing serial" ++ show n)+ parseCRL :: ParseASN1 CRL parseCRL = do     CRL <$> (getNext >>= getVersion)@@ -63,8 +79,8 @@         <*> getObject         <*> (getNext >>= getThisUpdate)         <*> getNextUpdate-        <*> getRevokedCertificates-        <*> getObject+        <*> parseRevokedCertificates+        <*> parseCRLExtensions   where getVersion (IntVal v) = return $ fromIntegral v         getVersion _          = throwParseError "unexpected type for version" @@ -76,8 +92,16 @@         timeOrNothing (ASN1Time _ tnext _) = Just tnext         timeOrNothing _                    = Nothing -        getRevokedCertificates = onNextContainer Sequence $ getMany getObject+parseRevokedCertificates :: ParseASN1 [RevokedCertificate]+parseRevokedCertificates =+    fmap (maybe [] id) $ onNextContainerMaybe Sequence $ getMany getObject +parseCRLExtensions :: ParseASN1 Extensions+parseCRLExtensions =+    fmap adapt $ onNextContainerMaybe (Container Context 0) $ getObject+  where adapt (Just e) = e+        adapt Nothing = Extensions Nothing+ encodeCRL :: CRL -> ASN1S encodeCRL crl xs =     [IntVal $ crlVersion crl] ++@@ -85,10 +109,11 @@     toASN1 (crlIssuer crl) [] ++     [ASN1Time TimeGeneralized (crlThisUpdate crl) (Just (TimezoneOffset 0))] ++     (maybe [] (\t -> [ASN1Time TimeGeneralized t (Just (TimezoneOffset 0))]) (crlNextUpdate crl)) ++-    [Start Sequence] ++-    revoked ++-    [End Sequence] ++-    toASN1 (crlExtensions crl) [] +++    maybeRevoked (crlRevokedCertificates crl) +++    maybeCrlExts (crlExtensions crl) ++     xs   where-    revoked = concatMap (\e -> toASN1 e []) (crlRevokedCertificates crl)+    maybeRevoked [] = []+    maybeRevoked xs' = asn1Container Sequence $ concatMap (\e -> toASN1 e []) xs'+    maybeCrlExts (Extensions Nothing) = []+    maybeCrlExts exts = asn1Container (Container Context 0) $ toASN1 exts []
Data/X509/Cert.hs view
@@ -53,7 +53,7 @@  parseCertHeaderVersion :: ParseASN1 Int parseCertHeaderVersion =-    maybe 1 id <$> onNextContainerMaybe (Container Context 0) (getNext >>= getVer)+    maybe 0 id <$> onNextContainerMaybe (Container Context 0) (getNext >>= getVer)   where getVer (IntVal v) = return $ fromIntegral v         getVer _          = throwParseError "unexpected type for version" @@ -85,6 +85,12 @@         Subject Unique Identifier (Optional) (>= 2)         Extensions (Optional)   (>= v3) -}++parseExtensions :: ParseASN1 Extensions+parseExtensions = fmap adapt $ onNextContainerMaybe (Container Context 3) $ getObject+  where adapt (Just e) = e+        adapt Nothing = Extensions Nothing+ parseCertificate :: ParseASN1 Certificate parseCertificate =     Certificate <$> parseCertHeaderVersion@@ -94,7 +100,7 @@                 <*> parseCertHeaderValidity                 <*> getObject                 <*> getObject-                <*> getObject+                <*> parseExtensions  encodeCertificateHeader :: Certificate -> [ASN1] encodeCertificateHeader cert =@@ -108,7 +114,9 @@                                            ,ASN1Time (timeType t2) t2 (Just (TimezoneOffset 0))]         eSubject  = toASN1 (certSubjectDN cert) []         epkinfo   = toASN1 (certPubKey cert) []-        eexts     = toASN1 (certExtensions cert) []+        eexts     = case certExtensions cert of+                      Extensions Nothing -> []+                      exts -> asn1Container (Container Context 3) $ toASN1 exts []         timeType t =             if t >= timeConvert (Date 2050 January 1)             then TimeGeneralized
Data/X509/DistinguishedName.hs view
@@ -6,6 +6,8 @@ -- Portability : unknown -- -- X.509 Distinguished names types and functions++{-# LANGUAGE CPP #-} module Data.X509.DistinguishedName     ( DistinguishedName(..)     , DistinguishedNameInner(..)@@ -16,7 +18,11 @@     ) where  import Control.Applicative-import Data.Monoid+#if MIN_VERSION_base(4,9,0)+import           Data.Semigroup+#else+import           Data.Monoid+#endif import Data.ASN1.Types import Data.X509.Internal @@ -49,9 +55,16 @@ newtype DistinguishedNameInner = DistinguishedNameInner DistinguishedName     deriving (Show,Eq) +#if MIN_VERSION_base(4,9,0)+instance Semigroup DistinguishedName where+    DistinguishedName l1 <> DistinguishedName l2 = DistinguishedName (l1++l2)+#endif+ instance Monoid DistinguishedName where     mempty  = DistinguishedName []+#if !(MIN_VERSION_base(4,11,0))     mappend (DistinguishedName l1) (DistinguishedName l2) = DistinguishedName (l1++l2)+#endif  instance ASN1Object DistinguishedName where     toASN1 dn = \xs -> encodeDN dn ++ xs
+ Data/X509/EC.hs view
@@ -0,0 +1,126 @@+-- |+-- Module      : Data.X509.EC+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : unknown+--+-- Utilities related to Elliptic Curve certificates and keys.+--+module Data.X509.EC+    (+      unserializePoint+    , ecPubKeyCurve+    , ecPubKeyCurveName+    , ecPrivKeyCurve+    , ecPrivKeyCurveName+    , lookupCurveNameByOID+    ) where++import Data.ASN1.OID+import Data.List (find)++import Data.X509.OID+import Data.X509.PublicKey+import Data.X509.PrivateKey++import qualified Crypto.PubKey.ECC.Prim  as ECC+import qualified Crypto.PubKey.ECC.Types as ECC+import           Crypto.Number.Serialize (os2ip)++import qualified Data.ByteString as B++-- | Read an EC point from a serialized format and make sure the point is+-- valid for the specified curve.+unserializePoint :: ECC.Curve -> SerializedPoint -> Maybe ECC.Point+unserializePoint curve (SerializedPoint bs) =+    case B.uncons bs of+        Nothing                -> Nothing+        Just (ptFormat, input) ->+            case ptFormat of+                4 -> if B.length input /= 2 * bytes+                        then Nothing+                        else+                            let (x, y) = B.splitAt bytes input+                                p      = ECC.Point (os2ip x) (os2ip y)+                             in if ECC.isPointValid curve p+                                    then Just p+                                    else Nothing+                -- 2 and 3 for compressed format.+                _ -> Nothing+  where bits  = ECC.curveSizeBits curve+        bytes = (bits + 7) `div` 8++-- | Return the curve associated to an EC Public Key.  This does not check+-- if a curve in explicit format is valid: if the input is not trusted one+-- should consider 'ecPubKeyCurveName' instead.+ecPubKeyCurve :: PubKeyEC -> Maybe ECC.Curve+ecPubKeyCurve (PubKeyEC_Named name _) = Just $ ECC.getCurveByName name+ecPubKeyCurve pub@PubKeyEC_Prime{}    =+    fmap buildCurve $+        unserializePoint (buildCurve undefined) (pubkeyEC_generator pub)+  where+    prime = pubkeyEC_prime pub+    buildCurve g =+        let cc = ECC.CurveCommon+                     { ECC.ecc_a = pubkeyEC_a        pub+                     , ECC.ecc_b = pubkeyEC_b        pub+                     , ECC.ecc_g = g+                     , ECC.ecc_n = pubkeyEC_order    pub+                     , ECC.ecc_h = pubkeyEC_cofactor pub+                     }+         in ECC.CurveFP (ECC.CurvePrime prime cc)++-- | Return the name of a standard curve associated to an EC Public Key+ecPubKeyCurveName :: PubKeyEC -> Maybe ECC.CurveName+ecPubKeyCurveName (PubKeyEC_Named name _) = Just name+ecPubKeyCurveName pub@PubKeyEC_Prime{}    =+    find matchPrimeCurve $ enumFrom $ toEnum 0+  where+    matchPrimeCurve c =+        case ECC.getCurveByName c of+            ECC.CurveFP (ECC.CurvePrime p cc) ->+                ECC.ecc_a cc == pubkeyEC_a pub     &&+                ECC.ecc_b cc == pubkeyEC_b pub     &&+                ECC.ecc_n cc == pubkeyEC_order pub &&+                p            == pubkeyEC_prime pub+            _                                 -> False++-- | Return the EC curve associated to an EC Private Key.  This does not check+-- if a curve in explicit format is valid: if the input is not trusted one+-- should consider 'ecPrivKeyCurveName' instead.+ecPrivKeyCurve :: PrivKeyEC -> Maybe ECC.Curve+ecPrivKeyCurve (PrivKeyEC_Named name _) = Just $ ECC.getCurveByName name+ecPrivKeyCurve priv@PrivKeyEC_Prime{}   =+    fmap buildCurve $+        unserializePoint (buildCurve undefined) (privkeyEC_generator priv)+  where+    prime = privkeyEC_prime priv+    buildCurve g =+        let cc = ECC.CurveCommon+                     { ECC.ecc_a = privkeyEC_a        priv+                     , ECC.ecc_b = privkeyEC_b        priv+                     , ECC.ecc_g = g+                     , ECC.ecc_n = privkeyEC_order    priv+                     , ECC.ecc_h = privkeyEC_cofactor priv+                     }+         in ECC.CurveFP (ECC.CurvePrime prime cc)++-- | Return the name of a standard curve associated to an EC Private Key+ecPrivKeyCurveName :: PrivKeyEC -> Maybe ECC.CurveName+ecPrivKeyCurveName (PrivKeyEC_Named name _) = Just name+ecPrivKeyCurveName priv@PrivKeyEC_Prime{}   =+    find matchPrimeCurve $ enumFrom $ toEnum 0+  where+    matchPrimeCurve c =+        case ECC.getCurveByName c of+            ECC.CurveFP (ECC.CurvePrime p cc) ->+                ECC.ecc_a cc == privkeyEC_a priv     &&+                ECC.ecc_b cc == privkeyEC_b priv     &&+                ECC.ecc_n cc == privkeyEC_order priv &&+                p            == privkeyEC_prime priv+            _                                 -> False++-- | Return the curve name associated to an OID+lookupCurveNameByOID :: OID -> Maybe ECC.CurveName+lookupCurveNameByOID = lookupByOID curvesOIDTable
Data/X509/Ext.hs view
@@ -8,7 +8,7 @@ -- extension processing module. -- {-# LANGUAGE FlexibleContexts #-}-+{-# LANGUAGE ScopedTypeVariables #-} module Data.X509.Ext     ( Extension(..)     -- * Common extension usually found in x509v3@@ -21,6 +21,7 @@     , ExtSubjectAltName(..)     , ExtAuthorityKeyId(..)     , ExtCrlDistributionPoints(..)+    , ExtNetscapeComment(..)     , AltName(..)     , DistributionPoint(..)     , ReasonFlag(..)@@ -35,11 +36,15 @@ import qualified Data.ByteString.Char8 as BC import Data.ASN1.Types import Data.ASN1.Parse+import Data.ASN1.Encoding+import Data.ASN1.BinaryEncoding import Data.ASN1.BitArray+import Data.Proxy import Data.List (find) import Data.X509.ExtensionRaw import Data.X509.DistinguishedName import Control.Applicative+import Control.Monad  -- | key usage flag that is found in the key usage extension field. data ExtKeyUsageFlag =@@ -65,11 +70,24 @@ -- -- each extension have a unique OID associated, and a way -- to encode and decode an ASN1 stream.+--+-- Errata: turns out, the content is not necessarily ASN1,+-- it could be data that is only parsable by the extension+-- e.g. raw ascii string. Add method to parse and encode with+-- ByteString class Extension a where-    extOID    :: a -> OID-    extEncode :: a -> [ASN1]-    extDecode :: [ASN1] -> Either String a+    extOID           :: a -> OID+    extHasNestedASN1 :: Proxy a -> Bool+    extEncode        :: a -> [ASN1]+    extDecode        :: [ASN1] -> Either String a +    extDecodeBs :: B.ByteString -> Either String a+    extDecodeBs = (either (Left . show) Right . decodeASN1' BER) >=> extDecode++    extEncodeBs :: a -> B.ByteString+    extEncodeBs = encodeASN1' DER . extEncode++ -- | Get a specific extension from a lists of raw extensions extensionGet :: Extension a => Extensions -> Maybe a extensionGet (Extensions Nothing)  = Nothing@@ -94,16 +112,17 @@ -- * Nothing, the OID doesn't match -- * Just Left, the OID matched, but the extension couldn't be decoded -- * Just Right, the OID matched, and the extension has been succesfully decoded-extensionDecode :: Extension a => ExtensionRaw -> Maybe (Either String a)-extensionDecode = doDecode undefined-  where doDecode :: Extension a => a -> ExtensionRaw -> Maybe (Either String a)-        doDecode dummy (ExtensionRaw oid _ asn1)-            | extOID dummy == oid = Just (extDecode asn1)-            | otherwise           = Nothing+extensionDecode :: forall a . Extension a => ExtensionRaw -> Maybe (Either String a)+extensionDecode er@(ExtensionRaw oid _ content)+    | extOID (undefined :: a) /= oid      = Nothing+    | extHasNestedASN1 (Proxy :: Proxy a) = Just (tryExtRawASN1 er >>= extDecode)+    | otherwise                           = Just (extDecodeBs content)  -- | Encode an Extension to extensionRaw-extensionEncode :: Extension a => Bool -> a -> ExtensionRaw-extensionEncode critical ext = ExtensionRaw (extOID ext) critical (extEncode ext)+extensionEncode :: forall a . Extension a => Bool -> a -> ExtensionRaw+extensionEncode critical ext+    | extHasNestedASN1 (Proxy :: Proxy a) = ExtensionRaw (extOID ext) critical (encodeASN1' DER $ extEncode ext)+    | otherwise                           = ExtensionRaw (extOID ext) critical (extEncodeBs ext)  -- | Basic Constraints data ExtBasicConstraints = ExtBasicConstraints Bool (Maybe Integer)@@ -111,6 +130,7 @@  instance Extension ExtBasicConstraints where     extOID = const [2,5,29,19]+    extHasNestedASN1 = const True     extEncode (ExtBasicConstraints b Nothing)  = [Start Sequence,Boolean b,End Sequence]     extEncode (ExtBasicConstraints b (Just i)) = [Start Sequence,Boolean b,IntVal i,End Sequence] @@ -127,6 +147,7 @@  instance Extension ExtKeyUsage where     extOID = const [2,5,29,15]+    extHasNestedASN1 = const True     extEncode (ExtKeyUsage flags) = [BitString $ flagsToBits flags]     extDecode [BitString bits] = Right $ ExtKeyUsage $ bitsToFlags bits     extDecode _ = Left "unknown sequence"@@ -158,6 +179,7 @@  instance Extension ExtExtendedKeyUsage where     extOID = const [2,5,29,37]+    extHasNestedASN1 = const True     extEncode (ExtExtendedKeyUsage purposes) =         [Start Sequence] ++ map (OID . lookupRev) purposes ++ [End Sequence]       where lookupRev (KeyUsagePurpose_Unknown oid) = oid@@ -174,6 +196,7 @@  instance Extension ExtSubjectKeyId where     extOID = const [2,5,29,14]+    extHasNestedASN1 = const True     extEncode (ExtSubjectKeyId o) = [OctetString o]     extDecode [OctetString o] = Right $ ExtSubjectKeyId o     extDecode _ = Left "unknown sequence"@@ -203,6 +226,7 @@  instance Extension ExtSubjectAltName where     extOID = const [2,5,29,17]+    extHasNestedASN1 = const True     extEncode (ExtSubjectAltName names) = encodeGeneralNames names     extDecode l = runParseASN1 (ExtSubjectAltName <$> parseGeneralNames) l @@ -213,6 +237,7 @@  instance Extension ExtAuthorityKeyId where     extOID _ = [2,5,29,35]+    extHasNestedASN1 = const True     extEncode (ExtAuthorityKeyId keyid) =         [Start Sequence,Other Context 0 keyid,End Sequence]     extDecode [Start Sequence,Other Context 0 keyid,End Sequence] =@@ -244,6 +269,7 @@  instance Extension ExtCrlDistributionPoints where     extOID _ = [2,5,29,31]+    extHasNestedASN1 = const True     extEncode = error "extEncode ExtCrlDistributionPoints unimplemented"     extDecode = error "extDecode ExtCrlDistributionPoints unimplemented"     --extEncode (ExtCrlDistributionPoints )@@ -311,3 +337,14 @@ flagsToBits :: Enum a => [a] -> BitArray flagsToBits flags = foldl bitArraySetBit bitArrayEmpty $ map (fromIntegral . fromEnum) flags   where bitArrayEmpty = toBitArray (B.pack [0,0]) 7++data ExtNetscapeComment = ExtNetscapeComment B.ByteString+    deriving (Show,Eq)++instance Extension ExtNetscapeComment where+    extOID _ = [2,16,840,1,113730,1,13]+    extHasNestedASN1 = const False+    extEncode = error "Extension: Netscape Comment do not contain nested ASN1"+    extDecode = error "Extension: Netscape Comment do not contain nested ASN1"+    extEncodeBs (ExtNetscapeComment b) = b+    extDecodeBs = Right . ExtNetscapeComment
Data/X509/ExtensionRaw.hs view
@@ -9,6 +9,8 @@ -- module Data.X509.ExtensionRaw     ( ExtensionRaw(..)+    , tryExtRawASN1+    , extRawASN1     , Extensions(..)     ) where @@ -17,14 +19,25 @@ import Data.ASN1.Encoding import Data.ASN1.BinaryEncoding import Data.X509.Internal+import qualified Data.ByteString as B  -- | An undecoded extension data ExtensionRaw = ExtensionRaw     { extRawOID      :: OID    -- ^ OID of this extension     , extRawCritical :: Bool   -- ^ if this extension is critical-    , extRawASN1     :: [ASN1] -- ^ the associated ASN1+    , extRawContent  :: B.ByteString -- ^ undecoded content     } deriving (Show,Eq) +tryExtRawASN1 :: ExtensionRaw -> Either String [ASN1]+tryExtRawASN1 (ExtensionRaw oid _ content) =+    case decodeASN1' BER content of+        Left err -> Left $ "fromASN1: X509.ExtensionRaw: OID=" ++ show oid ++ ": cannot decode data: " ++ show err+        Right r  -> Right r++extRawASN1 :: ExtensionRaw -> [ASN1]+extRawASN1 extRaw = either error id $ tryExtRawASN1 extRaw+{-# DEPRECATED extRawASN1 "use tryExtRawASN1 instead" #-}+ -- | a Set of 'ExtensionRaw' newtype Extensions = Extensions (Maybe [ExtensionRaw])     deriving (Show,Eq)@@ -32,28 +45,20 @@ instance ASN1Object Extensions where     toASN1 (Extensions Nothing) = \xs -> xs     toASN1 (Extensions (Just exts)) = \xs ->-        asn1Container (Container Context 3) (asn1Container Sequence (concatMap encodeExt exts)) ++ xs+        asn1Container Sequence (concatMap encodeExt exts) ++ xs     fromASN1 s = runParseASN1State (Extensions <$> parseExtensions) s-      where parseExtensions = onNextContainerMaybe (Container Context 3) $-                              onNextContainer Sequence (getMany getObject)+      where parseExtensions = onNextContainerMaybe Sequence (getMany getObject)  instance ASN1Object ExtensionRaw where     toASN1 extraw = \xs -> encodeExt extraw ++ xs     fromASN1 (Start Sequence:OID oid:xs) =         case xs of-            Boolean b:OctetString obj:End Sequence:xs2 -> extractExt b obj xs2-            OctetString obj:End Sequence:xs2           -> extractExt False obj xs2+            Boolean b:OctetString obj:End Sequence:xs2 -> Right (ExtensionRaw oid b obj, xs2)+            OctetString obj:End Sequence:xs2           -> Right (ExtensionRaw oid False obj, xs2)             _                                          -> Left ("fromASN1: X509.ExtensionRaw: unknown format:" ++ show xs)-      where-        extractExt critical bs remainingStream =-            case decodeASN1' BER bs of-                Left err -> Left ("fromASN1: X509.ExtensionRaw: OID=" ++ show oid ++-                                  ": cannot decode data: " ++ show err)-                Right r  -> Right (ExtensionRaw oid critical r, remainingStream)     fromASN1 l                                      =         Left ("fromASN1: X509.ExtensionRaw: unknown format:" ++ show l)  encodeExt :: ExtensionRaw -> [ASN1]-encodeExt (ExtensionRaw oid critical asn1) =-    let bs = encodeASN1' DER asn1-     in asn1Container Sequence ([OID oid] ++ (if critical then [Boolean True] else []) ++ [OctetString bs])+encodeExt (ExtensionRaw oid critical content) =+    asn1Container Sequence ([OID oid] ++ (if critical then [Boolean True] else []) ++ [OctetString content])
Data/X509/Internal.hs view
@@ -5,7 +5,6 @@ -- Stability   : experimental -- Portability : unknown ---{-# LANGUAGE CPP #-} module Data.X509.Internal     ( module Data.ASN1.Parse     , asn1Container@@ -17,18 +16,11 @@  import Data.ASN1.Types import Data.ASN1.Parse+import Control.Monad.Trans.Except -#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except runErrT :: ExceptT e m a -> m (Either e a) runErrT = runExceptT type ErrT = ExceptT-#else-import Control.Monad.Error-runErrT :: ErrorT e m a -> m (Either e a)-runErrT = runErrorT-type ErrT = ErrorT-#endif  -- | create a container around the stream of ASN1 asn1Container :: ASN1ConstructionType -> [ASN1] -> [ASN1]
Data/X509/PrivateKey.hs view
@@ -5,25 +5,288 @@ -- Stability   : experimental -- Portability : unknown ----- Public key handling in X.509 infrastructure+-- Private key handling in X.509 infrastructure -- module Data.X509.PrivateKey     ( PrivKey(..)+    , PrivKeyEC(..)     , privkeyToAlg     ) where +import Control.Applicative ((<$>), pure)+import Data.Maybe (fromMaybe)+import Data.Word (Word)++import Data.ByteArray (ByteArrayAccess, convert)+import qualified Data.ByteString as B++import Data.ASN1.Types+import Data.ASN1.Encoding+import Data.ASN1.BinaryEncoding+import Data.ASN1.BitArray+import Data.ASN1.Stream (getConstructedEnd)+ import Data.X509.AlgorithmIdentifier+import Data.X509.PublicKey (SerializedPoint(..))+import Data.X509.OID (lookupByOID, lookupOID, curvesOIDTable)++import Crypto.Error (CryptoFailable(..))+import Crypto.Number.Serialize (i2osp, os2ip) import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448   as X448+import qualified Crypto.PubKey.Ed25519    as Ed25519+import qualified Crypto.PubKey.Ed448      as Ed448 +-- | Elliptic Curve Private Key+--+-- TODO: missing support for binary curve.+data PrivKeyEC =+      PrivKeyEC_Prime+        { privkeyEC_priv      :: Integer+        , privkeyEC_a         :: Integer+        , privkeyEC_b         :: Integer+        , privkeyEC_prime     :: Integer+        , privkeyEC_generator :: SerializedPoint+        , privkeyEC_order     :: Integer+        , privkeyEC_cofactor  :: Integer+        , privkeyEC_seed      :: Integer+        }+    | PrivKeyEC_Named+        { privkeyEC_name      :: ECC.CurveName+        , privkeyEC_priv      :: Integer+        }+    deriving (Show,Eq)+ -- | Private key types known and used in X.509 data PrivKey =       PrivKeyRSA RSA.PrivateKey -- ^ RSA private key     | PrivKeyDSA DSA.PrivateKey -- ^ DSA private key+    | PrivKeyEC  PrivKeyEC      -- ^ EC private key+    | PrivKeyX25519 X25519.SecretKey   -- ^ X25519 private key+    | PrivKeyX448 X448.SecretKey       -- ^ X448 private key+    | PrivKeyEd25519 Ed25519.SecretKey -- ^ Ed25519 private key+    | PrivKeyEd448 Ed448.SecretKey     -- ^ Ed448 private key     deriving (Show,Eq) --- | Convert a Public key to the Public Key Algorithm type+instance ASN1Object PrivKey where+    fromASN1 = privkeyFromASN1+    toASN1 = privkeyToASN1++privkeyFromASN1 :: [ASN1] -> Either String (PrivKey, [ASN1])+privkeyFromASN1 asn1 =+  (mapFst PrivKeyRSA <$> rsaFromASN1 asn1) <!>+  (mapFst PrivKeyDSA <$> dsaFromASN1 asn1) <!>+  (mapFst PrivKeyEC <$> ecdsaFromASN1 asn1) <!>+  newcurveFromASN1 asn1+  where+    mapFst f (a, b) = (f a, b)++    Left _ <!> b = b+    a      <!> _ = a++rsaFromASN1 :: [ASN1] -> Either String (RSA.PrivateKey, [ASN1])+rsaFromASN1 (Start Sequence : IntVal 0 : IntVal n : IntVal e : IntVal d+    : IntVal p : IntVal q : IntVal dP : IntVal dQ : IntVal qinv+    : End Sequence : as) = pure (key, as)+  where+    key = RSA.PrivateKey (RSA.PublicKey (go n 1) n e) d p q dP dQ qinv+    go m i+        | 2 ^ (i * 8) > m = i+        | otherwise = go m (i + 1)+rsaFromASN1 (Start Sequence : IntVal 0 : Start Sequence+    : OID [1, 2, 840, 113549, 1, 1, 1] : Null : End Sequence+    : OctetString bytes : End Sequence : as) = do+        asn1 <- mapLeft failure (decodeASN1' BER bytes)+        fmap (const as) <$> rsaFromASN1 asn1+  where+    failure = ("rsaFromASN1: " ++) . show+rsaFromASN1 _ = Left "rsaFromASN1: unexpected format"++dsaFromASN1 :: [ASN1] -> Either String (DSA.PrivateKey, [ASN1])+dsaFromASN1 (Start Sequence : IntVal 0 : IntVal p : IntVal q : IntVal g+    : IntVal _ : IntVal x : End Sequence : as) =+        pure (DSA.PrivateKey (DSA.Params p g q) x, as)+dsaFromASN1 (Start Sequence : IntVal 0 : Start Sequence+    : OID [1, 2, 840, 10040, 4, 1] : Start Sequence : IntVal p : IntVal q+    : IntVal g : End Sequence : End Sequence : OctetString bytes+    : End Sequence : as) = case decodeASN1' BER bytes of+        Right [IntVal x] -> pure (DSA.PrivateKey (DSA.Params p g q) x, as)+        Right _ -> Left "DSA.PrivateKey.fromASN1: unexpected format"+        Left e -> Left $ "DSA.PrivateKey.fromASN1: " ++ show e+dsaFromASN1 _ = Left "DSA.PrivateKey.fromASN1: unexpected format"++ecdsaFromASN1 :: [ASN1] -> Either String (PrivKeyEC, [ASN1])+ecdsaFromASN1 = go []+  where+    failing = ("ECDSA.PrivateKey.fromASN1: " ++)++    go acc (Start Sequence : IntVal 1 : OctetString bytes : rest) = do+        key <- subgo (oid ++ acc)+        case rest'' of+            End Sequence : rest''' -> pure (key, rest''')+            _ -> Left $ failing "unexpected EC format"+      where+        d = os2ip bytes+        (oid, rest') = spanTag 0 rest+        (_, rest'') = spanTag 1 rest'+        subgo (OID oid_ : _) = maybe failure success mcurve+          where+            failure = Left $ failing $ "unknown curve " ++ show oid_+            success = Right . flip PrivKeyEC_Named d+            mcurve = lookupByOID curvesOIDTable oid_+        subgo (Start Sequence : IntVal 1 : Start Sequence+            : OID [1, 2, 840, 10045, 1, 1] : IntVal p : End Sequence+            : Start Sequence : OctetString a : OctetString b : BitString s+            : End Sequence : OctetString g : IntVal o : IntVal c+            : End Sequence : _) =+                pure $ PrivKeyEC_Prime d a' b' p g' o c s'+          where+            a' = os2ip a+            b' = os2ip b+            g' = SerializedPoint g+            s' = os2ip $ bitArrayGetData s+        subgo (Null : rest_) = subgo rest_+        subgo [] = Left $ failing "curve is missing"+        subgo _ = Left $ failing "unexpected curve format"+    go acc (Start Sequence : IntVal 0 : Start Sequence+        : OID [1, 2, 840, 10045, 2, 1] : rest) = case rest' of+            (OctetString bytes : rest'') -> do+                asn1 <- mapLeft (failing . show) (decodeASN1' BER bytes)+                fmap (const rest'') <$> go (oid ++ acc) asn1+            _ -> Left $ failing "unexpected EC format"+      where+        (oid, rest') = spanEnd 0 rest+    go _ _ = Left $ failing "unexpected EC format"++    spanEnd :: Word -> [ASN1] -> ([ASN1], [ASN1])+    spanEnd = loop id+      where+        loop dlist n (a@(Start _) : as) = loop (dlist . (a :)) (n + 1) as+        loop dlist 0 (End _ : as) = (dlist [], as)+        loop dlist n (a@(End _) : as) = loop (dlist . (a :)) (n - 1) as+        loop dlist n (a : as) = loop (dlist . (a :)) n as+        loop dlist _ [] = (dlist [], [])++    spanTag :: Int -> [ASN1] -> ([ASN1], [ASN1])+    spanTag a (Start (Container _ b) : as) | a == b = spanEnd 0 as+    spanTag _ as = ([], as)++newcurveFromASN1 :: [ASN1] -> Either String (PrivKey, [ASN1])+newcurveFromASN1 ( Start Sequence+                  : IntVal v+                  : Start Sequence+                  : OID oid+                  : End Sequence+                  : OctetString bs+                  : xs)+    | isValidVersion v = do+        let (_, ys) = containerWithTag 0 xs+        case primitiveWithTag 1 ys of+            (_, End Sequence : zs) ->+                case getP oid of+                    Just (name, parse) -> do+                        let err s = Left (name ++ ".SecretKey.fromASN1: " ++ s)+                        case decodeASN1' BER bs of+                            Right [OctetString key] ->+                                case parse key of+                                    CryptoPassed s -> Right (s, zs)+                                    CryptoFailed e -> err ("invalid secret key: " ++ show e)+                            Right _ -> err "unexpected inner format"+                            Left  e -> err (show e)+                    Nothing -> Left ("newcurveFromASN1: unexpected OID " ++ show oid)+            _ -> Left "newcurveFromASN1: unexpected end format"+    | otherwise = Left ("newcurveFromASN1: unexpected version: " ++ show v)+  where+    getP [1,3,101,110] = Just ("X25519", fmap PrivKeyX25519 . X25519.secretKey)+    getP [1,3,101,111] = Just ("X448", fmap PrivKeyX448 . X448.secretKey)+    getP [1,3,101,112] = Just ("Ed25519", fmap PrivKeyEd25519 . Ed25519.secretKey)+    getP [1,3,101,113] = Just ("Ed448", fmap PrivKeyEd448 . Ed448.secretKey)+    getP _             = Nothing+    isValidVersion version = version >= 0 && version <= 1+newcurveFromASN1 _ =+    Left "newcurveFromASN1: unexpected format"++containerWithTag :: ASN1Tag -> [ASN1] -> ([ASN1], [ASN1])+containerWithTag etag (Start (Container _ atag) : xs)+    | etag == atag = getConstructedEnd 0 xs+containerWithTag _    xs = ([], xs)++primitiveWithTag :: ASN1Tag -> [ASN1] -> (Maybe B.ByteString, [ASN1])+primitiveWithTag etag (Other _ atag bs : xs)+    | etag == atag = (Just bs, xs)+primitiveWithTag _    xs = (Nothing, xs)++privkeyToASN1 :: PrivKey -> ASN1S+privkeyToASN1 (PrivKeyRSA rsa) = rsaToASN1 rsa+privkeyToASN1 (PrivKeyDSA dsa) = dsaToASN1 dsa+privkeyToASN1 (PrivKeyEC ecdsa) = ecdsaToASN1 ecdsa+privkeyToASN1 (PrivKeyX25519 k)  = newcurveToASN1 [1,3,101,110] k+privkeyToASN1 (PrivKeyX448 k)    = newcurveToASN1 [1,3,101,111] k+privkeyToASN1 (PrivKeyEd25519 k) = newcurveToASN1 [1,3,101,112] k+privkeyToASN1 (PrivKeyEd448 k)   = newcurveToASN1 [1,3,101,113] k++rsaToASN1 :: RSA.PrivateKey -> ASN1S+rsaToASN1 key = (++)+    [ Start Sequence, IntVal 0, IntVal n, IntVal e, IntVal d, IntVal p+    , IntVal q, IntVal dP, IntVal dQ, IntVal qinv, End Sequence+    ]+  where+    RSA.PrivateKey (RSA.PublicKey _ n e) d p q dP dQ qinv = key++dsaToASN1 :: DSA.PrivateKey -> ASN1S+dsaToASN1 (DSA.PrivateKey params@(DSA.Params p g q) y) = (++)+    [ Start Sequence, IntVal 0, IntVal p, IntVal q, IntVal g, IntVal x+    , IntVal y, End Sequence+    ]+  where+    x = DSA.calculatePublic params y++ecdsaToASN1 :: PrivKeyEC -> ASN1S+ecdsaToASN1 (PrivKeyEC_Named curveName d) = (++)+    [ Start Sequence, IntVal 1, OctetString (i2osp d)+    , Start (Container Context 0), OID oid, End (Container Context 0)+    , End Sequence+    ]+  where+    err = error . ("ECDSA.PrivateKey.toASN1: " ++)+    oid = fromMaybe (err $ "missing named curve " ++ show curveName)+                    (lookupOID curvesOIDTable curveName)+ecdsaToASN1 (PrivKeyEC_Prime d a b p g o c s) = (++)+    [ Start Sequence, IntVal 1, OctetString (i2osp d)+    , Start (Container Context 0), Start Sequence, IntVal 1+    , Start Sequence, OID [1, 2, 840, 10045, 1, 1], IntVal p, End Sequence+    , Start Sequence, OctetString a', OctetString b', BitString s'+    , End Sequence, OctetString g' , IntVal o, IntVal c, End Sequence+    , End (Container Context 0), End Sequence+    ]+  where+    a' = i2osp a+    b' = i2osp b+    SerializedPoint g' = g+    s' = BitArray (8 * fromIntegral (B.length bytes)) bytes+      where+        bytes = i2osp s++newcurveToASN1 :: ByteArrayAccess key => OID -> key -> ASN1S+newcurveToASN1 oid key = (++)+    [ Start Sequence, IntVal 0, Start Sequence, OID oid, End Sequence+    , OctetString (encodeASN1' DER [OctetString $ convert key])+    , End Sequence+    ]++mapLeft :: (a0 -> a1) -> Either a0 b -> Either a1 b+mapLeft f (Left x) = Left (f x)+mapLeft _ (Right x) = Right x++-- | Convert a Private key to the Public Key Algorithm type privkeyToAlg :: PrivKey -> PubKeyALG privkeyToAlg (PrivKeyRSA _)         = PubKeyALG_RSA privkeyToAlg (PrivKeyDSA _)         = PubKeyALG_DSA-+privkeyToAlg (PrivKeyEC _)          = PubKeyALG_EC+privkeyToAlg (PrivKeyX25519 _)      = PubKeyALG_X25519+privkeyToAlg (PrivKeyX448 _)        = PubKeyALG_X448+privkeyToAlg (PrivKeyEd25519 _)     = PubKeyALG_Ed25519+privkeyToAlg (PrivKeyEd448 _)       = PubKeyALG_Ed448
Data/X509/PublicKey.hs view
@@ -20,15 +20,22 @@ import Data.ASN1.BitArray  import Data.Bits+import Data.ByteArray (convert) import Data.ByteString (ByteString)  import Data.X509.Internal import Data.X509.OID import Data.X509.AlgorithmIdentifier +import Crypto.Error (CryptoFailable(..)) import qualified Crypto.PubKey.RSA.Types as RSA import qualified Crypto.PubKey.DSA       as DSA import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448   as X448+import qualified Crypto.PubKey.Ed25519    as Ed25519+import qualified Crypto.PubKey.Ed448      as Ed448+import           Crypto.Number.Basic (numBytes) import           Crypto.Number.Serialize (os2ip) import Data.Word @@ -65,6 +72,10 @@     | PubKeyDH (Integer,Integer,Integer,Maybe Integer,([Word8], Integer))                                 -- ^ DH format with (p,g,q,j,(seed,pgenCounter))     | PubKeyEC PubKeyEC       -- ^ EC public key+    | PubKeyX25519    X25519.PublicKey    -- ^ X25519 public key+    | PubKeyX448      X448.PublicKey      -- ^ X448 public key+    | PubKeyEd25519   Ed25519.PublicKey   -- ^ Ed25519 public key+    | PubKeyEd448     Ed448.PublicKey     -- ^ Ed448 public key     | PubKeyUnknown OID B.ByteString -- ^ unrecognized format     deriving (Show,Eq) @@ -132,6 +143,22 @@                         }, xs2)                 _ ->                     Left $ "fromASN1: X509.PubKey: unknown EC format: " ++ show xs+        | pkalg == getObjectID PubKeyALG_X25519    =+            case xs of+                End Sequence:BitString bits:End Sequence:xs2 -> decodeCF "X25519" PubKeyX25519 bits xs2 X25519.publicKey+                _ -> Left ("fromASN1: X509.PubKey: unknown X25519 format: " ++ show xs)+        | pkalg == getObjectID PubKeyALG_X448      =+            case xs of+                End Sequence:BitString bits:End Sequence:xs2 -> decodeCF "X448" PubKeyX448 bits xs2 X448.publicKey+                _ -> Left ("fromASN1: X509.PubKey: unknown X448 format: " ++ show xs)+        | pkalg == getObjectID PubKeyALG_Ed25519   =+            case xs of+                End Sequence:BitString bits:End Sequence:xs2 -> decodeCF "Ed25519" PubKeyEd25519 bits xs2 Ed25519.publicKey+                _ -> Left ("fromASN1: X509.PubKey: unknown Ed25519 format: " ++ show xs)+        | pkalg == getObjectID PubKeyALG_Ed448     =+            case xs of+                End Sequence:BitString bits:End Sequence:xs2 -> decodeCF "Ed448" PubKeyEd448 bits xs2 Ed448.publicKey+                _ -> Left ("fromASN1: X509.PubKey: unknown Ed448 format: " ++ show xs)         | otherwise = Left $ "fromASN1: unknown public key OID: " ++ show pkalg       where decodeASN1Err format bits xs2 f =                 case decodeASN1' BER (bitArrayGetData bits) of@@ -146,6 +173,10 @@             removeNull (Null:r) = r             removeNull l        = l +            decodeCF format c bits xs2 f = case f (bitArrayGetData bits) of+                CryptoPassed pk  -> Right (c pk, xs2)+                CryptoFailed err -> Left ("fromASN1: X509.PubKey " ++ format ++ " bitarray contains an invalid public key: " ++ show err)+     fromASN1 l = Left ("fromASN1: X509.PubKey: unknown format:" ++ show l)     toASN1 a = \xs -> encodePK a ++ xs @@ -155,6 +186,10 @@ pubkeyToAlg (PubKeyDSA _)         = PubKeyALG_DSA pubkeyToAlg (PubKeyDH _)          = PubKeyALG_DH pubkeyToAlg (PubKeyEC _)          = PubKeyALG_EC+pubkeyToAlg (PubKeyX25519 _)      = PubKeyALG_X25519+pubkeyToAlg (PubKeyX448 _)        = PubKeyALG_X448+pubkeyToAlg (PubKeyEd25519 _)     = PubKeyALG_Ed25519+pubkeyToAlg (PubKeyEd448 _)       = PubKeyALG_Ed448 pubkeyToAlg (PubKeyUnknown oid _) = PubKeyALG_Unknown oid  encodePK :: PubKey -> [ASN1]@@ -180,6 +215,14 @@                     _        -> error ("undefined curve OID: " ++ show curveName)     encodeInner (PubKeyEC (PubKeyEC_Prime {})) =         error "encodeInner: unimplemented public key EC_Prime"+    encodeInner (PubKeyX25519   pubkey)  =+        asn1Container Sequence [pkalg] ++ [BitString $ toBitArray (convert pubkey) 0]+    encodeInner (PubKeyX448     pubkey)  =+        asn1Container Sequence [pkalg] ++ [BitString $ toBitArray (convert pubkey) 0]+    encodeInner (PubKeyEd25519   pubkey) =+        asn1Container Sequence [pkalg] ++ [BitString $ toBitArray (convert pubkey) 0]+    encodeInner (PubKeyEd448     pubkey) =+        asn1Container Sequence [pkalg] ++ [BitString $ toBitArray (convert pubkey) 0]     encodeInner (PubKeyDH _) = error "encodeInner: unimplemented public key DH"     encodeInner (PubKeyUnknown _ l) =         asn1Container Sequence [pkalg,Null] ++ [BitString $ toBitArray l 0]@@ -192,11 +235,10 @@ rsaPubFromASN1 (Start Sequence:IntVal smodulus:IntVal pubexp:End Sequence:xs) =     Right (pub, xs)   where-    pub = RSA.PublicKey { RSA.public_size = calculate_modulus modulus 1+    pub = RSA.PublicKey { RSA.public_size = numBytes modulus                         , RSA.public_n    = modulus                         , RSA.public_e    = pubexp                         }-    calculate_modulus n i = if (2 ^ (i * 8)) > n then i else calculate_modulus n (i+1)     -- some bad implementation will not serialize ASN.1 integer properly, leading     -- to negative modulus. if that's the case, we correct it.     modulus = toPositive smodulus
Tests/Tests.hs view
@@ -12,6 +12,11 @@ import Data.List (nub, sort) import Data.ASN1.Types import Data.X509+import Crypto.Error (throwCryptoError)+import qualified Crypto.PubKey.Curve25519 as X25519+import qualified Crypto.PubKey.Curve448   as X448+import qualified Crypto.PubKey.Ed25519    as Ed25519+import qualified Crypto.PubKey.Ed448      as Ed448 import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA @@ -33,13 +38,64 @@ instance Arbitrary DSA.PublicKey where     arbitrary = DSA.PublicKey <$> arbitrary <*> arbitrary +instance Arbitrary X25519.PublicKey where+    arbitrary = X25519.toPublic <$> arbitrary++instance Arbitrary X448.PublicKey where+    arbitrary = X448.toPublic <$> arbitrary++instance Arbitrary Ed25519.PublicKey where+    arbitrary = Ed25519.toPublic <$> arbitrary++instance Arbitrary Ed448.PublicKey where+    arbitrary = Ed448.toPublic <$> arbitrary+ instance Arbitrary PubKey where     arbitrary = oneof         [ PubKeyRSA <$> arbitrary         , PubKeyDSA <$> arbitrary         --, PubKeyECDSA ECDSA_Hash_SHA384 <$> (B.pack <$> replicateM 384 arbitrary)+        , PubKeyX25519 <$> arbitrary+        , PubKeyX448 <$> arbitrary+        , PubKeyEd25519 <$> arbitrary+        , PubKeyEd448 <$> arbitrary         ] +instance Arbitrary RSA.PrivateKey where+    arbitrary = RSA.PrivateKey <$> arbitrary+                               <*> arbitrary+                               <*> arbitrary+                               <*> arbitrary+                               <*> arbitrary+                               <*> arbitrary+                               <*> arbitrary++instance Arbitrary DSA.PrivateKey where+    arbitrary = DSA.PrivateKey <$> arbitrary <*> arbitrary++instance Arbitrary X25519.SecretKey where+    arbitrary = throwCryptoError . X25519.secretKey <$> arbitraryBS 32 32++instance Arbitrary X448.SecretKey where+    arbitrary = throwCryptoError . X448.secretKey <$> arbitraryBS 56 56++instance Arbitrary Ed25519.SecretKey where+    arbitrary = throwCryptoError . Ed25519.secretKey <$> arbitraryBS 32 32++instance Arbitrary Ed448.SecretKey where+    arbitrary = throwCryptoError . Ed448.secretKey <$> arbitraryBS 57 57++instance Arbitrary PrivKey where+    arbitrary = oneof+        [ PrivKeyRSA <$> arbitrary+        , PrivKeyDSA <$> arbitrary+        --, PrivKeyECDSA ECDSA_Hash_SHA384 <$> (B.pack <$> replicateM 384 arbitrary)+        , PrivKeyX25519 <$> arbitrary+        , PrivKeyX448 <$> arbitrary+        , PrivKeyEd25519 <$> arbitrary+        , PrivKeyEd448 <$> arbitrary+        ]+ instance Arbitrary HashALG where     arbitrary = elements [HashMD2,HashMD5,HashSHA1,HashSHA224,HashSHA256,HashSHA384,HashSHA512] @@ -65,6 +121,8 @@         , SignatureALG HashSHA256 PubKeyALG_EC         , SignatureALG HashSHA384 PubKeyALG_EC         , SignatureALG HashSHA512 PubKeyALG_EC+        , SignatureALG_IntrinsicHash PubKeyALG_Ed25519+        , SignatureALG_IntrinsicHash PubKeyALG_Ed448         ]  arbitraryBS r1 r2 = choose (r1,r2) >>= \l -> (B.pack <$> replicateM l arbitrary)@@ -121,7 +179,7 @@ instance Arbitrary RevokedCertificate where     arbitrary = RevokedCertificate <$> arbitrary                                    <*> arbitrary-                                   <*> pure (Extensions Nothing)+                                   <*> arbitrary  instance Arbitrary CRL where     arbitrary = CRL <$> pure 1@@ -152,6 +210,7 @@ main = defaultMain $ testGroup "X509"     [ testGroup "marshall"         [ testProperty "pubkey" (property_unmarshall_marshall_id :: PubKey -> Bool)+        , testProperty "privkey" (property_unmarshall_marshall_id :: PrivKey -> Bool)         , testProperty "signature alg" (property_unmarshall_marshall_id :: SignatureALG -> Bool)         , testGroup "extension"             [ testProperty "key-usage" (property_extension_id :: ExtKeyUsage -> Bool)
x509.cabal view
@@ -1,6 +1,6 @@ Name:                x509-Version:             1.6.5-Description:         X509 reader and writer+version:             1.7.7+Description:         X509 reader and writer. please see README License:             BSD3 License-file:        LICENSE Copyright:           Vincent Hanquez <vincent@snarc.org>@@ -11,21 +11,24 @@ Category:            Data stability:           experimental Homepage:            http://github.com/vincenthz/hs-certificate-Cabal-Version:       >=1.8+Cabal-Version:       >= 1.10+Extra-Source-Files:  ChangeLog.md  Library-  Build-Depends:     base >= 3 && < 5+  Default-Language:  Haskell2010+  Build-Depends:     base >= 4.7 && < 5                    , bytestring                    , memory-                   , mtl+                   , transformers >= 0.4                    , containers                    , hourglass-                   , pem >= 0.1 && < 0.3+                   , pem >= 0.1                    , asn1-types >= 0.3.1 && < 0.4                    , asn1-encoding >= 0.9 && < 0.10                    , asn1-parse >= 0.9.3 && < 0.10-                   , cryptonite+                   , cryptonite >= 0.24   Exposed-modules:   Data.X509+                     Data.X509.EC   Other-modules:     Data.X509.Internal                      Data.X509.CertificateChain                      Data.X509.AlgorithmIdentifier@@ -41,6 +44,7 @@   ghc-options:       -Wall  Test-Suite test-x509+  Default-Language:  Haskell2010   type:              exitcode-stdio-1.0   hs-source-dirs:    Tests   Main-is:           Tests.hs