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