x509 1.6.5 → 1.7.7
raw patch · 14 files changed
Files
- ChangeLog.md +6/−0
- Data/X509.hs +1/−0
- Data/X509/AlgorithmIdentifier.hs +23/−5
- Data/X509/CRL.hs +39/−14
- Data/X509/Cert.hs +11/−3
- Data/X509/DistinguishedName.hs +14/−1
- Data/X509/EC.hs +126/−0
- Data/X509/Ext.hs +49/−12
- Data/X509/ExtensionRaw.hs +20/−15
- Data/X509/Internal.hs +1/−9
- Data/X509/PrivateKey.hs +266/−3
- Data/X509/PublicKey.hs +44/−2
- Tests/Tests.hs +60/−1
- x509.cabal +11/−7
+ 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