|
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
|