| """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() |
| |