From 60831ac25b7cf9abe3da0a50d36ec3744ab9607b Mon Sep 17 00:00:00 2001 From: joe Date: Mon, 19 May 2014 02:32:28 -0400 Subject: TLSA: swapped arguments of 'match' and added documentation. --- TLSA.hs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 16 deletions(-) (limited to 'TLSA.hs') diff --git a/TLSA.hs b/TLSA.hs index 4c4a3d4..16cc568 100644 --- a/TLSA.hs +++ b/TLSA.hs @@ -30,11 +30,35 @@ fromWord8 = toEnum . fromEnum toWord8 :: Enum a => a -> Word8 toWord8 = toEnum . fromEnum +-- | The Certificate Usage Field as described in RFC 6698, section 2.1.1. data CertUsage + + -- | This usage limits which CA can be used to issue certificates for a + -- given service on a host. PKIX-validated TLS connections for the domain + -- should be considered invalid if the certification path does not include + -- at least one certificate that 'match'es the 'TLSA' record. = CAConstraint + + -- | This usage limits which end entity certificate can be used by a given + -- service on a host. The TLS connection for the domain should be + -- considered invalid if the end entity certificate does not 'match' the + -- 'TLSA' record. | ServiceCertificateConstraint + + -- | This usage allows a domain name administrator to specify a new trust + -- anchor. This is useful if the domain issues its own certificates under + -- its own CA that is not expected to be in the end users' collection of + -- trust anchors. When conducting PKIX validation for the domain, any + -- certificate matching the 'TLSA' record can be treated as a trust anchor + -- that does not require further validation. | TrustAnchorAssertion + + -- | This usage allows for a domain name administrator to issue + -- certificates for a domain without involving a third-party CA. The end + -- entity certificate MUST 'match' the 'TLSA' record. Unlike for a + -- 'ServiceCertificateConstraint', PKIX validation should not be performed. | DomainIssued + | CertUsage Word8 deriving (Eq, Ord, Show, Read) @@ -50,10 +74,11 @@ instance Enum CertUsage where toEnum 3 = DomainIssued toEnum n = CertUsage (toEnum n) +-- | Indicates what sort of object should be compared with 'associationData'. data Selector - = FullCertificate - | SubjectPublicKeyInfo - | Selector Word8 -- Selector 255 reserved for private use + = FullCertificate -- ^ x.509 certificate + | SubjectPublicKeyInfo -- ^ PKCS #8 formatted public key + | Selector Word8 -- ^ value 255 reserved for private use deriving (Eq,Ord,Show) instance Enum Selector where @@ -64,11 +89,13 @@ instance Enum Selector where toEnum 1 = SubjectPublicKeyInfo toEnum n = Selector (toEnum n) +-- | Is 'associationData' the an object of the form specified by 'Selector' or +-- is it only a hash of it? data MatchingType = Match_Exact | Match_SHA256 | Match_SHA512 - | Match Word8 -- Match 255 reserved for private use + | Match Word8 -- ^ value 255 is reserved for private use deriving (Eq,Ord,Show) instance Enum MatchingType where @@ -82,21 +109,28 @@ instance Enum MatchingType where toEnum n = Match (toEnum n) +-- | The parsed RDATA field of a TLSA DNS resource record (type 52) as +-- described in RFC 6698. +-- +-- Use the 'match' function uses 'selector', 'matchingType' and +-- 'associationData' to implement a predicate on certificates obtained via the +-- TLS protocol. The 'certUsage' field indicates what that predicate means. data TLSA = TLSA - { tlsaCertUsage :: CertUsage - , tlsaSelector :: Selector - , tlsaMatchingType :: MatchingType - , tlsaAssociationData :: BS.ByteString + { certUsage :: CertUsage + , selector :: Selector + , matchingType :: MatchingType + , associationData :: BS.ByteString } deriving (Eq, Ord, Show) +-- | Parse RDATA for a TLSA resource record. fromByteString :: BS.ByteString -> TLSA fromByteString bs = TLSA (fromWord8 cu) (fromWord8 sel) (fromWord8 mat) - associationData + dta where - (csm,associationData) = BS.splitAt 3 bs + (csm,dta) = BS.splitAt 3 bs (cu,sel,mat) = case BS.unpack csm of [cu,sel,mat] -> (cu,sel,mat) @@ -104,28 +138,32 @@ fromByteString bs = TLSA (fromWord8 cu) [cu] -> (cu,0,0) [] -> (0,0,0) +-- | Encode a valid RDATA field for a TLSA DNS record. toByteString :: TLSA -> BS.ByteString -toByteString (TLSA cu sel mat associationData) = csm <> associationData +toByteString (TLSA cu sel mat dta) = csm <> dta where csm = BS.pack [ toWord8 cu , toWord8 sel , toWord8 mat ] -match :: Certificate -> TLSA -> Bool -match cert tlsa = fromMaybe False $ - (== tlsaAssociationData tlsa) <$> (hash <*> material) +-- | Returns 'True' if the given certificate matches the given 'TLSA' object. +-- The algorithm for matching depends on the values of 'selector' and +-- 'matchingType' as described in RFC 6698. +match :: TLSA -> Certificate -> Bool +match tlsa cert = fromMaybe False $ + (== associationData tlsa) <$> (hash <*> material) where encode obj = encodeASN1 DER (toASN1 obj []) material = - case tlsaSelector tlsa of + case selector tlsa of FullCertificate -> Just $ encode cert SubjectPublicKeyInfo -> Just $ encode $ certPubKey cert _ -> Nothing hash = - case tlsaMatchingType tlsa of + case matchingType tlsa of Match_Exact -> Just L.toStrict Match_SHA256 -> Just SHA256.hashlazy Match_SHA512 -> Just SHA512.hashlazy -- cgit v1.2.3