diff options
Diffstat (limited to 'src/lib/tlslite/Checker.py')
-rwxr-xr-x | src/lib/tlslite/Checker.py | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/lib/tlslite/Checker.py b/src/lib/tlslite/Checker.py new file mode 100755 index 000000000..f97869762 --- /dev/null +++ b/src/lib/tlslite/Checker.py @@ -0,0 +1,146 @@ +"""Class for post-handshake certificate checking.""" + +from utils.cryptomath import hashAndBase64 +from X509 import X509 +from X509CertChain import X509CertChain +from errors import * + + +class Checker: + """This class is passed to a handshake function to check the other + party's certificate chain. + + If a handshake function completes successfully, but the Checker + judges the other party's certificate chain to be missing or + inadequate, a subclass of + L{tlslite.errors.TLSAuthenticationError} will be raised. + + Currently, the Checker can check either an X.509 or a cryptoID + chain (for the latter, cryptoIDlib must be installed). + """ + + def __init__(self, cryptoID=None, protocol=None, + x509Fingerprint=None, + x509TrustList=None, x509CommonName=None, + checkResumedSession=False): + """Create a new Checker instance. + + You must pass in one of these argument combinations: + - cryptoID[, protocol] (requires cryptoIDlib) + - x509Fingerprint + - x509TrustList[, x509CommonName] (requires cryptlib_py) + + @type cryptoID: str + @param cryptoID: A cryptoID which the other party's certificate + chain must match. The cryptoIDlib module must be installed. + Mutually exclusive with all of the 'x509...' arguments. + + @type protocol: str + @param protocol: A cryptoID protocol URI which the other + party's certificate chain must match. Requires the 'cryptoID' + argument. + + @type x509Fingerprint: str + @param x509Fingerprint: A hex-encoded X.509 end-entity + fingerprint which the other party's end-entity certificate must + match. Mutually exclusive with the 'cryptoID' and + 'x509TrustList' arguments. + + @type x509TrustList: list of L{tlslite.X509.X509} + @param x509TrustList: A list of trusted root certificates. The + other party must present a certificate chain which extends to + one of these root certificates. The cryptlib_py module must be + installed. Mutually exclusive with the 'cryptoID' and + 'x509Fingerprint' arguments. + + @type x509CommonName: str + @param x509CommonName: The end-entity certificate's 'CN' field + must match this value. For a web server, this is typically a + server name such as 'www.amazon.com'. Mutually exclusive with + the 'cryptoID' and 'x509Fingerprint' arguments. Requires the + 'x509TrustList' argument. + + @type checkResumedSession: bool + @param checkResumedSession: If resumed sessions should be + checked. This defaults to False, on the theory that if the + session was checked once, we don't need to bother + re-checking it. + """ + + if cryptoID and (x509Fingerprint or x509TrustList): + raise ValueError() + if x509Fingerprint and x509TrustList: + raise ValueError() + if x509CommonName and not x509TrustList: + raise ValueError() + if protocol and not cryptoID: + raise ValueError() + if cryptoID: + import cryptoIDlib #So we raise an error here + if x509TrustList: + import cryptlib_py #So we raise an error here + self.cryptoID = cryptoID + self.protocol = protocol + self.x509Fingerprint = x509Fingerprint + self.x509TrustList = x509TrustList + self.x509CommonName = x509CommonName + self.checkResumedSession = checkResumedSession + + def __call__(self, connection): + """Check a TLSConnection. + + When a Checker is passed to a handshake function, this will + be called at the end of the function. + + @type connection: L{tlslite.TLSConnection.TLSConnection} + @param connection: The TLSConnection to examine. + + @raise tlslite.errors.TLSAuthenticationError: If the other + party's certificate chain is missing or bad. + """ + if not self.checkResumedSession and connection.resumed: + return + + if self.cryptoID or self.x509Fingerprint or self.x509TrustList: + if connection._client: + chain = connection.session.serverCertChain + else: + chain = connection.session.clientCertChain + + if self.x509Fingerprint or self.x509TrustList: + if isinstance(chain, X509CertChain): + if self.x509Fingerprint: + if chain.getFingerprint() != self.x509Fingerprint: + raise TLSFingerprintError(\ + "X.509 fingerprint mismatch: %s, %s" % \ + (chain.getFingerprint(), self.x509Fingerprint)) + else: #self.x509TrustList + if not chain.validate(self.x509TrustList): + raise TLSValidationError("X.509 validation failure") + if self.x509CommonName and \ + (chain.getCommonName() != self.x509CommonName): + raise TLSAuthorizationError(\ + "X.509 Common Name mismatch: %s, %s" % \ + (chain.getCommonName(), self.x509CommonName)) + elif chain: + raise TLSAuthenticationTypeError() + else: + raise TLSNoAuthenticationError() + elif self.cryptoID: + import cryptoIDlib.CertChain + if isinstance(chain, cryptoIDlib.CertChain.CertChain): + if chain.cryptoID != self.cryptoID: + raise TLSFingerprintError(\ + "cryptoID mismatch: %s, %s" % \ + (chain.cryptoID, self.cryptoID)) + if self.protocol: + if not chain.checkProtocol(self.protocol): + raise TLSAuthorizationError(\ + "cryptoID protocol mismatch") + if not chain.validate(): + raise TLSValidationError("cryptoID validation failure") + elif chain: + raise TLSAuthenticationTypeError() + else: + raise TLSNoAuthenticationError() + |