1-- | 2-- Module : Data.X509.Validation.Signature 3-- License : BSD-style 4-- Maintainer : Vincent Hanquez <vincent@snarc.org> 5-- Stability : experimental 6-- Portability : unknown 7-- 8-- X.509 Certificate and CRL signature verification 9-- 10module Data.X509.Validation.Signature 11 ( verifySignedSignature 12 , verifySignature 13 , SignatureVerification(..) 14 , SignatureFailure(..) 15 ) where 16 17import Crypto.Error (CryptoFailable(..)) 18import qualified Crypto.PubKey.RSA.PKCS15 as RSA 19import qualified Crypto.PubKey.RSA.PSS as PSS 20import qualified Crypto.PubKey.DSA as DSA 21import qualified Crypto.PubKey.ECC.Types as ECC 22import qualified Crypto.PubKey.ECC.ECDSA as ECDSA 23import qualified Crypto.PubKey.Ed25519 as Ed25519 24import qualified Crypto.PubKey.Ed448 as Ed448 25import Crypto.Hash 26 27import Data.ByteString (ByteString) 28import Data.X509 29import Data.X509.EC 30import Data.ASN1.Types 31import Data.ASN1.Encoding 32import Data.ASN1.BinaryEncoding 33 34-- | A set of possible return from signature verification. 35-- 36-- When SignatureFailed is return, the signature shouldn't be 37-- accepted. 38-- 39-- Other values are only useful to differentiate the failure 40-- reason, but are all equivalent to failure. 41-- 42data SignatureVerification = 43 SignaturePass -- ^ verification succeeded 44 | SignatureFailed SignatureFailure -- ^ verification failed 45 deriving (Show,Eq) 46 47-- | Various failure possible during signature checking 48data SignatureFailure = 49 SignatureInvalid -- ^ signature doesn't verify 50 | SignaturePubkeyMismatch -- ^ algorithm and public key mismatch, cannot proceed 51 | SignatureUnimplemented -- ^ unimplemented signature algorithm 52 deriving (Show,Eq) 53 54-- | Verify a Signed object against a specified public key 55verifySignedSignature :: (Show a, Eq a, ASN1Object a) 56 => SignedExact a 57 -> PubKey 58 -> SignatureVerification 59verifySignedSignature signedObj pubKey = 60 verifySignature (signedAlg signed) 61 pubKey 62 (getSignedData signedObj) 63 (signedSignature signed) 64 where signed = getSigned signedObj 65 66-- | verify signature using parameter 67verifySignature :: SignatureALG -- ^ Signature algorithm used 68 -> PubKey -- ^ Public key to use for verify 69 -> ByteString -- ^ Certificate data that need to be verified 70 -> ByteString -- ^ Signature to verify 71 -> SignatureVerification 72verifySignature (SignatureALG_Unknown _) _ _ _ = SignatureFailed SignatureUnimplemented 73verifySignature (SignatureALG hashALG PubKeyALG_RSAPSS) pubkey cdata signature = case verifyF pubkey of 74 Nothing -> SignatureFailed SignatureUnimplemented 75 Just f -> if f cdata signature 76 then SignaturePass 77 else SignatureFailed SignatureInvalid 78 where 79 verifyF (PubKeyRSA key) 80 | hashALG == HashSHA256 = Just $ PSS.verify (PSS.defaultPSSParams SHA256) key 81 | hashALG == HashSHA384 = Just $ PSS.verify (PSS.defaultPSSParams SHA384) key 82 | hashALG == HashSHA512 = Just $ PSS.verify (PSS.defaultPSSParams SHA512) key 83 | hashALG == HashSHA224 = Just $ PSS.verify (PSS.defaultPSSParams SHA224) key 84 | otherwise = Nothing 85 verifyF _ = Nothing 86verifySignature (SignatureALG hashALG pubkeyALG) pubkey cdata signature 87 | pubkeyToAlg pubkey == pubkeyALG = case verifyF pubkey of 88 Nothing -> SignatureFailed SignatureUnimplemented 89 Just f -> if f cdata signature 90 then SignaturePass 91 else SignatureFailed SignatureInvalid 92 | otherwise = SignatureFailed SignaturePubkeyMismatch 93 where 94 verifyF (PubKeyRSA key) = Just $ rsaVerify hashALG key 95 verifyF (PubKeyDSA key) 96 | hashALG == HashSHA1 = Just $ dsaVerify SHA1 key 97 | hashALG == HashSHA224 = Just $ dsaVerify SHA224 key 98 | hashALG == HashSHA256 = Just $ dsaVerify SHA256 key 99 | otherwise = Nothing 100 verifyF (PubKeyEC key) = verifyECDSA hashALG key 101 verifyF _ = Nothing 102 103 dsaToSignature :: ByteString -> Maybe DSA.Signature 104 dsaToSignature b = 105 case decodeASN1' BER b of 106 Left _ -> Nothing 107 Right asn1 -> 108 case asn1 of 109 Start Sequence:IntVal r:IntVal s:End Sequence:_ -> 110 Just $ DSA.Signature { DSA.sign_r = r, DSA.sign_s = s } 111 _ -> 112 Nothing 113 114 dsaVerify hsh key b a = 115 case dsaToSignature a of 116 Nothing -> False 117 Just dsaSig -> DSA.verify hsh key dsaSig b 118 119 rsaVerify HashMD2 = RSA.verify (Just MD2) 120 rsaVerify HashMD5 = RSA.verify (Just MD5) 121 rsaVerify HashSHA1 = RSA.verify (Just SHA1) 122 rsaVerify HashSHA224 = RSA.verify (Just SHA224) 123 rsaVerify HashSHA256 = RSA.verify (Just SHA256) 124 rsaVerify HashSHA384 = RSA.verify (Just SHA384) 125 rsaVerify HashSHA512 = RSA.verify (Just SHA512) 126 127verifySignature (SignatureALG_IntrinsicHash pubkeyALG) pubkey cdata signature 128 | pubkeyToAlg pubkey == pubkeyALG = doVerify pubkey 129 | otherwise = SignatureFailed SignaturePubkeyMismatch 130 where 131 doVerify (PubKeyEd25519 key) = eddsa Ed25519.verify Ed25519.signature key 132 doVerify (PubKeyEd448 key) = eddsa Ed448.verify Ed448.signature key 133 doVerify _ = SignatureFailed SignatureUnimplemented 134 135 eddsa verify toSig key = 136 case toSig signature of 137 CryptoPassed sig 138 | verify key cdata sig -> SignaturePass 139 | otherwise -> SignatureFailed SignatureInvalid 140 CryptoFailed _ -> SignatureFailed SignatureInvalid 141 142verifyECDSA :: HashALG -> PubKeyEC -> Maybe (ByteString -> ByteString -> Bool) 143verifyECDSA hashALG key = 144 ecPubKeyCurveName key >>= verifyCurve (pubkeyEC_pub key) 145 where 146 verifyCurve pub curveName = Just $ \msg sigBS -> 147 case decodeASN1' BER sigBS of 148 Left _ -> False 149 Right [Start Sequence,IntVal r,IntVal s,End Sequence] -> 150 let curve = ECC.getCurveByName curveName 151 in case unserializePoint curve pub of 152 Nothing -> False 153 Just p -> let pubkey = ECDSA.PublicKey curve p 154 in (ecdsaVerify hashALG) pubkey (ECDSA.Signature r s) msg 155 Right _ -> False 156 157 ecdsaVerify HashMD2 = ECDSA.verify MD2 158 ecdsaVerify HashMD5 = ECDSA.verify MD5 159 ecdsaVerify HashSHA1 = ECDSA.verify SHA1 160 ecdsaVerify HashSHA224 = ECDSA.verify SHA224 161 ecdsaVerify HashSHA256 = ECDSA.verify SHA256 162 ecdsaVerify HashSHA384 = ECDSA.verify SHA384 163 ecdsaVerify HashSHA512 = ECDSA.verify SHA512 164