20 | %hide Network.TLS.Handshake.Message.Certificate
22 | ||| Takes a wildcard domain like "*.google.com" and creates
23 | ||| a function to check if a string matches the wildcard domain
29 | where
49 | ||| Type of identifier that will be used to check if a certificate's common name matches
50 | ||| the supplied hostname
55 | check_ipv4_address (IPv4Addr addr' :: xs) addr = if addr == addr' then True else check_ipv4_address xs addr
60 | check_ipv6_address (IPv6Addr addr' :: xs) addr = if addr == addr' then True else check_ipv6_address xs addr
65 | check_dns_name (DNSName predicate :: xs) name = if (domain_predicate' predicate) name then True else check_dns_name xs name
69 | ||| Check if the certificate has expired
71 | check_cert_timestamp cert = (\t => t > cert.valid_not_before && t < cert.valid_not_after) <$> time
73 | ||| Find a certificate with matching common name given an identifier
76 | where
78 | is_certificate (Left x) cert = guard (check_ipv4_address (certificate_subject_names cert) x) $> cert
79 | is_certificate (Right (Left x)) cert = guard (check_ipv6_address (certificate_subject_names cert) x) $> cert
80 | is_certificate (Right (Right x)) cert = guard (check_dns_name (certificate_subject_names cert) x) $> cert
82 | ||| Convert a string of hostname into an identifier, such as DNSName, IPv4 and IPv6 address
92 | ||| Check if the server's certificate is valid
103 | ||| Check if the server's certificate's ca and intermediate ca's are valid
104 | ||| depth refers to how deep you are in the certificate chain, needed since
105 | ||| some intermediate ca's specify a constraint on the maximum depth
119 | | False => throwE $ "certificate key usage does not allow signing certificates: " <+> show cert
121 | where
126 | ||| Given a certificate and its issuer's certificate, verify if the certificate's signature is correct
129 | verify_signature' subject.sig_parameter issuer.cert_public_key subject.tbs_raw_bytes subject.signature_value
131 | ||| True if there is intersecting elements in both list
137 | ||| Given an Authority Key Identifier extension, verifies if another certificate matches the constraint
138 | ||| This is needed since in the case that a certificate is cross signed by another CA, issuer and subject
139 | ||| distinguished names are not sufficient to find a subject's issuer.
143 | where
147 | -- Check if the SHA1 hash of public keys match
149 | go0 auth_key_id cert = def (map (\i => i == cert.cert_public_key_id) auth_key_id.key_identifier)
150 | -- Check if the serial number matches
153 | -- Check if the subject distinguished names match
169 | verify_certificate_chain : HasIO io => Nat -> List Certificate -> List Certificate -> Certificate -> EitherT String io ()
171 | -- Constructs a lazy list of ca's which have cross signed the current certificate
172 | -- Won't be used if the certificate has a valid issuer which is in the chain
178 | -- List of certificates which subject name matches the current certificate's issuser name
179 | let in_chain = filter (\(_,c) => c.subject == current.issuer) (map (False,) untrusted <+> map (True,) trusted)
185 | where
186 | -- Verify the issuer certificate and the subject's signature
193 | | Left err => throwE $ "certificate failed for subject: " <+> show current <+> ", issuer: " <+> show next <+> ", reason " <+> err
196 | -- replace the self signed certificate with the one in the trusted certificate list
197 | -- this should works since they are the same
198 | -- this prevents an attacker modifying the content of the certificate chain, changing the
199 | -- root ca's certificate with their own, with the same subject name but different public key
204 | | Left err => throwE $ "certificate failed for subject: " <+> show current <+> ", issuer: " <+> show next <+> ", reason " <+> err
207 | ||| Given a list of trusted certificate, hostname of the server, verify the certificate chain
208 | ||| and return a public key to be used to verify TLS handshake
209 | export
221 | ||| A CertificateCheck function that always trusts the certificate unless it's malformed
222 | export