Blame Tests/Certificate.hs

Packit 331900
{-# LANGUAGE GADTs #-}
Packit 331900
-- | Types and functions used to build test certificates.
Packit 331900
module Certificate
Packit 331900
    (
Packit 331900
    -- * Hash algorithms
Packit 331900
      hashMD2
Packit 331900
    , hashMD5
Packit 331900
    , hashSHA1
Packit 331900
    , hashSHA224
Packit 331900
    , hashSHA256
Packit 331900
    , hashSHA384
Packit 331900
    , hashSHA512
Packit 331900
    -- * Key and signature utilities
Packit 331900
    , Alg(..)
Packit 331900
    , Keys
Packit 331900
    , generateKeys
Packit 331900
    -- * Certificate utilities
Packit 331900
    , Pair(..)
Packit 331900
    , mkDn
Packit 331900
    , mkExtension
Packit 331900
    , leafStdExts
Packit 331900
    -- * Certificate creation functions
Packit 331900
    , Auth(..)
Packit 331900
    , mkCertificate
Packit 331900
    , mkCA
Packit 331900
    , mkLeaf
Packit 331900
    ) where
Packit 331900
Packit 331900
import Control.Applicative
Packit 331900
Packit 331900
import Crypto.Hash.Algorithms
Packit 331900
import Crypto.Number.Serialize
Packit 331900
Packit 331900
import qualified Crypto.PubKey.DSA        as DSA
Packit 331900
import qualified Crypto.PubKey.ECC.ECDSA  as ECDSA
Packit 331900
import qualified Crypto.PubKey.ECC.Generate as ECC
Packit 331900
import qualified Crypto.PubKey.ECC.Types  as ECC
Packit 331900
import qualified Crypto.PubKey.RSA        as RSA
Packit 331900
import qualified Crypto.PubKey.RSA.PKCS15 as RSA
Packit 331900
import qualified Crypto.PubKey.RSA.PSS    as PSS
Packit 331900
Packit 331900
import qualified Data.ByteString as B
Packit 331900
Packit 331900
import Data.ASN1.BinaryEncoding (DER(..))
Packit 331900
import Data.ASN1.Encoding
Packit 331900
import Data.ASN1.Types
Packit 331900
import Data.Maybe (catMaybes)
Packit 331900
import Data.String (fromString)
Packit 331900
import Data.X509
Packit 331900
Packit 331900
import Data.Hourglass
Packit 331900
Packit 331900
Packit 331900
-- Crypto utilities --
Packit 331900
Packit 331900
-- | Hash algorithms supported in certificates.
Packit 331900
--
Packit 331900
-- This relates the typed hash algorithm @hash@ to the 'HashALG' value.
Packit 331900
data GHash hash = GHash { getHashALG :: HashALG, getHashAlgorithm :: hash }
Packit 331900
Packit 331900
hashMD2    :: GHash MD2
Packit 331900
hashMD5    :: GHash MD5
Packit 331900
hashSHA1   :: GHash SHA1
Packit 331900
hashSHA224 :: GHash SHA224
Packit 331900
hashSHA256 :: GHash SHA256
Packit 331900
hashSHA384 :: GHash SHA384
Packit 331900
hashSHA512 :: GHash SHA512
Packit 331900
Packit 331900
hashMD2    = GHash HashMD2    MD2
Packit 331900
hashMD5    = GHash HashMD5    MD5
Packit 331900
hashSHA1   = GHash HashSHA1   SHA1
Packit 331900
hashSHA224 = GHash HashSHA224 SHA224
Packit 331900
hashSHA256 = GHash HashSHA256 SHA256
Packit 331900
hashSHA384 = GHash HashSHA384 SHA384
Packit 331900
hashSHA512 = GHash HashSHA512 SHA512
Packit 331900
Packit 331900
-- | Signature and hash algorithms instantiated with parameters.
Packit 331900
data Alg pub priv where
Packit 331900
    AlgRSA    :: (HashAlgorithm hash, RSA.HashAlgorithmASN1 hash)
Packit 331900
              => Int
Packit 331900
              -> GHash hash
Packit 331900
              -> Alg RSA.PublicKey RSA.PrivateKey
Packit 331900
Packit 331900
    AlgRSAPSS :: HashAlgorithm hash
Packit 331900
              => Int
Packit 331900
              -> PSS.PSSParams hash B.ByteString B.ByteString
Packit 331900
              -> GHash hash
Packit 331900
              -> Alg RSA.PublicKey RSA.PrivateKey
Packit 331900
Packit 331900
    AlgDSA    :: HashAlgorithm hash
Packit 331900
              => DSA.Params
Packit 331900
              -> GHash hash
Packit 331900
              -> Alg DSA.PublicKey DSA.PrivateKey
Packit 331900
Packit 331900
    AlgEC     :: HashAlgorithm hash
Packit 331900
              => ECC.CurveName
Packit 331900
              -> GHash hash
Packit 331900
              -> Alg ECDSA.PublicKey ECDSA.PrivateKey
Packit 331900
Packit 331900
-- | Types of public and private keys used by a signature algorithm.
Packit 331900
type Keys pub priv = (Alg pub priv, pub, priv)
Packit 331900
Packit 331900
-- | Generates random keys for a signature algorithm.
Packit 331900
generateKeys :: Alg pub priv -> IO (Keys pub priv)
Packit 331900
generateKeys alg@(AlgRSA bits      _) = generateRSAKeys alg bits
Packit 331900
generateKeys alg@(AlgRSAPSS bits _ _) = generateRSAKeys alg bits
Packit 331900
generateKeys alg@(AlgDSA params    _) = do
Packit 331900
    x <- DSA.generatePrivate params
Packit 331900
    let y = DSA.calculatePublic params x
Packit 331900
    return (alg, DSA.PublicKey params y, DSA.PrivateKey params x)
Packit 331900
generateKeys alg@(AlgEC name       _) = do
Packit 331900
    let curve = ECC.getCurveByName name
Packit 331900
    (pub, priv) <- ECC.generate curve
Packit 331900
    return (alg, pub, priv)
Packit 331900
Packit 331900
generateRSAKeys :: Alg RSA.PublicKey RSA.PrivateKey
Packit 331900
                -> Int
Packit 331900
                -> IO (Alg RSA.PublicKey RSA.PrivateKey, RSA.PublicKey, RSA.PrivateKey)
Packit 331900
generateRSAKeys alg bits = addAlg <$> RSA.generate size e
Packit 331900
  where
Packit 331900
    addAlg (pub, priv) = (alg, pub, priv)
Packit 331900
    size = bits `div` 8
Packit 331900
    e    = 3
Packit 331900
Packit 331900
getPubKey :: Alg pub priv -> pub -> PubKey
Packit 331900
getPubKey (AlgRSA    _    _) key = PubKeyRSA key
Packit 331900
getPubKey (AlgRSAPSS _ _  _) key = PubKeyRSA key
Packit 331900
getPubKey (AlgDSA    _    _) key = PubKeyDSA key
Packit 331900
getPubKey (AlgEC     name _) key = PubKeyEC (PubKeyEC_Named name pub)
Packit 331900
  where
Packit 331900
    ECC.Point x y = ECDSA.public_q key
Packit 331900
    pub   = SerializedPoint bs
Packit 331900
    bs    = B.cons 4 (i2ospOf_ bytes x `B.append` i2ospOf_ bytes y)
Packit 331900
    bits  = ECC.curveSizeBits (ECC.getCurveByName name)
Packit 331900
    bytes = (bits + 7) `div` 8
Packit 331900
Packit 331900
getSignatureALG :: Alg pub priv -> SignatureALG
Packit 331900
getSignatureALG (AlgRSA    _   hash) = SignatureALG (getHashALG hash) PubKeyALG_RSA
Packit 331900
getSignatureALG (AlgRSAPSS _ _ hash) = SignatureALG (getHashALG hash) PubKeyALG_RSAPSS
Packit 331900
getSignatureALG (AlgDSA    _   hash) = SignatureALG (getHashALG hash) PubKeyALG_DSA
Packit 331900
getSignatureALG (AlgEC     _   hash) = SignatureALG (getHashALG hash) PubKeyALG_EC
Packit 331900
Packit 331900
doSign :: Alg pub priv -> priv -> B.ByteString -> IO B.ByteString
Packit 331900
doSign (AlgRSA _ hash)        key msg = do
Packit 331900
    result <- RSA.signSafer (Just $ getHashAlgorithm hash) key msg
Packit 331900
    case result of
Packit 331900
        Left err      -> error ("doSign(AlgRSA): " ++ show err)
Packit 331900
        Right sigBits -> return sigBits
Packit 331900
doSign (AlgRSAPSS _ params _) key msg = do
Packit 331900
    result <- PSS.signSafer params key msg
Packit 331900
    case result of
Packit 331900
        Left err      -> error ("doSign(AlgRSAPSS): " ++ show err)
Packit 331900
        Right sigBits -> return sigBits
Packit 331900
doSign (AlgDSA _ hash)        key msg = do
Packit 331900
    sig <- DSA.sign key (getHashAlgorithm hash) msg
Packit 331900
    return $ encodeASN1' DER
Packit 331900
                 [ Start Sequence
Packit 331900
                 , IntVal (DSA.sign_r sig)
Packit 331900
                 , IntVal (DSA.sign_s sig)
Packit 331900
                 , End Sequence
Packit 331900
                 ]
Packit 331900
doSign (AlgEC _ hash)         key msg = do
Packit 331900
    sig <- ECDSA.sign key (getHashAlgorithm hash) msg
Packit 331900
    return $ encodeASN1' DER
Packit 331900
                 [ Start Sequence
Packit 331900
                 , IntVal (ECDSA.sign_r sig)
Packit 331900
                 , IntVal (ECDSA.sign_s sig)
Packit 331900
                 , End Sequence
Packit 331900
                 ]
Packit 331900
Packit 331900
Packit 331900
-- Certificate utilities --
Packit 331900
Packit 331900
-- | Holds together a certificate and its private key for convenience.
Packit 331900
--
Packit 331900
-- Contains also the crypto algorithm that both are issued from.  This is
Packit 331900
-- useful when signing another certificate.
Packit 331900
data Pair pub priv = Pair
Packit 331900
    { pairAlg        :: Alg pub priv
Packit 331900
    , pairSignedCert :: SignedCertificate
Packit 331900
    , pairKey        :: priv
Packit 331900
    }
Packit 331900
Packit 331900
-- | Builds a DN with a single component.
Packit 331900
mkDn :: String -> DistinguishedName
Packit 331900
mkDn cn = DistinguishedName [(getObjectID DnCommonName, fromString cn)]
Packit 331900
Packit 331900
-- | Used to build a certificate extension.
Packit 331900
mkExtension :: Extension a => Bool -> a -> ExtensionRaw
Packit 331900
mkExtension crit ext = ExtensionRaw (extOID ext) crit (extEncodeBs ext)
Packit 331900
Packit 331900
-- | Default extensions in leaf certificates.
Packit 331900
leafStdExts :: [ExtensionRaw]
Packit 331900
leafStdExts = [ku, eku]
Packit 331900
  where
Packit 331900
    ku  = mkExtension False $ ExtKeyUsage
Packit 331900
               [ KeyUsage_digitalSignature , KeyUsage_keyEncipherment ]
Packit 331900
    eku = mkExtension False $ ExtExtendedKeyUsage
Packit 331900
               [ KeyUsagePurpose_ServerAuth , KeyUsagePurpose_ClientAuth ]
Packit 331900
Packit 331900
Packit 331900
-- Authority signing a certificate --
Packit 331900
--
Packit 331900
-- When the certificate is self-signed, issuer and subject are the same.  So
Packit 331900
-- they have identical signature algorithms.  The purpose of the GADT is to
Packit 331900
-- hold this constraint only in the self-signed case.
Packit 331900
Packit 331900
-- | Authority signing a certificate, itself or another certificate.
Packit 331900
data Auth pubI privI pubS privS where
Packit 331900
    Self :: (pubI ~ pubS, privI ~ privS) => Auth pubI privI pubS privS
Packit 331900
    CA   ::              Pair pubI privI -> Auth pubI privI pubS privS
Packit 331900
Packit 331900
foldAuth :: a
Packit 331900
         -> (Pair pubI privI -> a)
Packit 331900
         -> Auth pubI privI pubS privS
Packit 331900
         -> a
Packit 331900
foldAuth x _ Self   = x          -- no constraint used
Packit 331900
foldAuth _ f (CA p) = f p
Packit 331900
Packit 331900
foldAuthPriv :: privS
Packit 331900
             -> (Pair pubI privI -> privI)
Packit 331900
             -> Auth pubI privI pubS privS
Packit 331900
             -> privI
Packit 331900
foldAuthPriv x _ Self   = x      -- uses constraint privI ~ privS
Packit 331900
foldAuthPriv _ f (CA p) = f p
Packit 331900
Packit 331900
foldAuthPubPriv :: k pubS privS
Packit 331900
                -> (Pair pubI privI -> k pubI privI)
Packit 331900
                -> Auth pubI privI pubS privS
Packit 331900
                -> k pubI privI
Packit 331900
foldAuthPubPriv x _ Self   = x   -- uses both constraints
Packit 331900
foldAuthPubPriv _ f (CA p) = f p
Packit 331900
Packit 331900
Packit 331900
-- Certificate creation functions --
Packit 331900
Packit 331900
-- | Builds a certificate using the supplied keys and signs it with an
Packit 331900
-- authority (itself or another certificate).
Packit 331900
mkCertificate :: Int                        -- ^ Certificate version
Packit 331900
              -> Integer                    -- ^ Serial number
Packit 331900
              -> DistinguishedName          -- ^ Subject DN
Packit 331900
              -> (DateTime, DateTime)       -- ^ Certificate validity period
Packit 331900
              -> [ExtensionRaw]             -- ^ Extensions to include
Packit 331900
              -> Auth pubI privI pubS privS -- ^ Authority signing the new certificate
Packit 331900
              -> Keys pubS privS            -- ^ Keys for the new certificate
Packit 331900
              -> IO (Pair pubS privS)       -- ^ The new certificate/key pair
Packit 331900
mkCertificate version serial dn validity exts auth (algS, pubKey, privKey) = do
Packit 331900
    signedCert <- objectToSignedExactF signatureFunction cert
Packit 331900
    return Pair { pairAlg        = algS
Packit 331900
                , pairSignedCert = signedCert
Packit 331900
                , pairKey        = privKey
Packit 331900
                }
Packit 331900
Packit 331900
  where
Packit 331900
    pairCert = signedObject . getSigned . pairSignedCert
Packit 331900
Packit 331900
    cert = Certificate
Packit 331900
        { certVersion      = version
Packit 331900
        , certSerial       = serial
Packit 331900
        , certSignatureAlg = signAlgI
Packit 331900
        , certIssuerDN     = issuerDN
Packit 331900
        , certValidity     = validity
Packit 331900
        , certSubjectDN    = dn
Packit 331900
        , certPubKey       = getPubKey algS pubKey
Packit 331900
        , certExtensions   = extensions
Packit 331900
        }
Packit 331900
Packit 331900
    signingKey = foldAuthPriv     privKey pairKey auth
Packit 331900
    algI       = foldAuthPubPriv  algS    pairAlg auth
Packit 331900
Packit 331900
    signAlgI   = getSignatureALG algI
Packit 331900
    issuerDN   = foldAuth dn (certSubjectDN . pairCert) auth
Packit 331900
    extensions = Extensions (if null exts then Nothing else Just exts)
Packit 331900
Packit 331900
    signatureFunction objRaw = do
Packit 331900
        sigBits <- doSign algI signingKey objRaw
Packit 331900
        return (sigBits, signAlgI)
Packit 331900
Packit 331900
-- | Builds a CA certificate using the supplied keys and signs it with an
Packit 331900
-- authority (itself or another certificate).
Packit 331900
mkCA :: Integer                    -- ^ Serial number
Packit 331900
     -> String                     -- ^ Common name
Packit 331900
     -> (DateTime, DateTime)       -- ^ CA validity period
Packit 331900
     -> Maybe ExtBasicConstraints  -- ^ CA basic constraints
Packit 331900
     -> Maybe ExtKeyUsage          -- ^ CA key usage
Packit 331900
     -> Auth pubI privI pubS privS -- ^ Authority signing the new certificate
Packit 331900
     -> Keys pubS privS            -- ^ Keys for the new certificate
Packit 331900
     -> IO (Pair pubS privS)       -- ^ The new CA certificate/key pair
Packit 331900
mkCA serial cn validity bc ku =
Packit 331900
    let exts = catMaybes [ mkExtension True <$> bc, mkExtension False <$> ku ]
Packit 331900
    in mkCertificate 2 serial (mkDn cn) validity exts
Packit 331900
Packit 331900
-- | Builds a leaf certificate using the supplied keys and signs it with an
Packit 331900
-- authority (itself or another certificate).
Packit 331900
mkLeaf :: String                     -- ^ Common name
Packit 331900
       -> (DateTime, DateTime)       -- ^ Certificate validity period
Packit 331900
       -> Auth pubI privI pubS privS -- ^ Authority signing the new certificate
Packit 331900
       -> Keys pubS privS            -- ^ Keys for the new certificate
Packit 331900
       -> IO (Pair pubS privS)       -- ^ The new leaf certificate/key pair
Packit 331900
mkLeaf cn validity = mkCertificate 2 100 (mkDn cn) validity leafStdExts