packages feed

x509 1.7.4 → 1.7.5

raw patch · 5 files changed

+197/−42 lines, 5 filesdep ~cryptonite

Dependency ranges changed: cryptonite

Files

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@@ -99,6 +111,6 @@         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 = \xs -> Start Sequence:OID (sigOID signatureAlg):Null:End Sequence:xs
Data/X509/PrivateKey.hs view
@@ -14,23 +14,31 @@     ) 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, curvesOIDTable)+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 --@@ -57,6 +65,10 @@       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)  instance ASN1Object PrivKey where@@ -67,7 +79,8 @@ privkeyFromASN1 asn1 =   (mapFst PrivKeyRSA <$> rsaFromASN1 asn1) <!>   (mapFst PrivKeyDSA <$> dsaFromASN1 asn1) <!>-  (mapFst PrivKeyEC <$> ecdsaFromASN1 asn1)+  (mapFst PrivKeyEC <$> ecdsaFromASN1 asn1) <!>+  newcurveFromASN1 asn1   where     mapFst f (a, b) = (f a, b) @@ -161,10 +174,59 @@     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 = (++)@@ -189,40 +251,9 @@     , End Sequence     ]   where-    oid = case curveName of-        ECC.SEC_p112r1 -> [1, 3, 132, 0, 6]-        ECC.SEC_p112r2 -> [1, 3, 132, 0, 7]-        ECC.SEC_p128r1 -> [1, 3, 132, 0, 28]-        ECC.SEC_p128r2 -> [1, 3, 132, 0, 29]-        ECC.SEC_p160k1 -> [1, 3, 132, 0, 9]-        ECC.SEC_p160r1 -> [1, 3, 132, 0, 8]-        ECC.SEC_p160r2 -> [1, 3, 132, 0, 30]-        ECC.SEC_p192k1 -> [1, 3, 132, 0, 31]-        ECC.SEC_p192r1 -> [1, 2, 840, 10045, 3, 1, 1]-        ECC.SEC_p224k1 -> [1, 3, 132, 0, 32]-        ECC.SEC_p224r1 -> [1, 3, 132, 0, 33]-        ECC.SEC_p256k1 -> [1, 3, 132, 0, 10]-        ECC.SEC_p256r1 -> [1, 2, 840, 10045, 3, 1, 7]-        ECC.SEC_p384r1 -> [1, 3, 132, 0, 34]-        ECC.SEC_p521r1 -> [1, 3, 132, 0, 35]-        ECC.SEC_t113r1 -> [1, 3, 132, 0, 4]-        ECC.SEC_t113r2 -> [1, 3, 132, 0, 5]-        ECC.SEC_t131r1 -> [1, 3, 132, 0, 22]-        ECC.SEC_t131r2 -> [1, 3, 132, 0, 23]-        ECC.SEC_t163k1 -> [1, 3, 132, 0, 1]-        ECC.SEC_t163r1 -> [1, 3, 132, 0, 2]-        ECC.SEC_t163r2 -> [1, 3, 132, 0, 15]-        ECC.SEC_t193r1 -> [1, 3, 132, 0, 24]-        ECC.SEC_t193r2 -> [1, 3, 132, 0, 25]-        ECC.SEC_t233k1 -> [1, 3, 132, 0, 26]-        ECC.SEC_t233r1 -> [1, 3, 132, 0, 27]-        ECC.SEC_t239k1 -> [1, 3, 132, 0, 3]-        ECC.SEC_t283k1 -> [1, 3, 132, 0, 16]-        ECC.SEC_t283r1 -> [1, 3, 132, 0, 17]-        ECC.SEC_t409k1 -> [1, 3, 132, 0, 36]-        ECC.SEC_t409r1 -> [1, 3, 132, 0, 37]-        ECC.SEC_t571k1 -> [1, 3, 132, 0, 38]-        ECC.SEC_t571r1 -> [1, 3, 132, 0, 39]+    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@@ -239,6 +270,13 @@       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@@ -248,3 +286,7 @@ 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,21 @@ 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.Serialize (os2ip) import Data.Word @@ -65,6 +71,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 +142,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 +172,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 +185,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 +214,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]
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)@@ -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,5 +1,5 @@ Name:                x509-version:             1.7.4+version:             1.7.5 Description:         X509 reader and writer. please see README License:             BSD3 License-file:        LICENSE@@ -25,7 +25,7 @@                    , asn1-types >= 0.3.1 && < 0.4                    , asn1-encoding >= 0.9 && < 0.10                    , asn1-parse >= 0.9.3 && < 0.10-                   , cryptonite >= 0.8+                   , cryptonite >= 0.24   Exposed-modules:   Data.X509                      Data.X509.EC   Other-modules:     Data.X509.Internal