def _checkTimestamp(self, state, keyName, timestamp): """ :param ValidationState state: On error, this calls state.fail and returns False. :param Name keyName: The key name. :param float timestamp: The timestamp as milliseconds since Jan 1, 1970 UTC. :return: On success, return True. On error, call state.fail and return False. :rtype: bool """ self._cleanUp() # _nowOffsetMilliseconds is only used for testing. now = Common.getNowMilliseconds() + self._nowOffsetMilliseconds if (timestamp < now - self._options._gracePeriod or timestamp > now + self._options._gracePeriod): state.fail(ValidationError(ValidationError.POLICY_ERROR, "Timestamp is outside the grace period for key " + keyName.toUri())) return False index = self._findByKeyName(keyName) if index >= 0: if timestamp <= self._container[index]._timestamp: state.fail(ValidationError(ValidationError.POLICY_ERROR, "Timestamp is reordered for key " + keyName.toUri())) return False def successCallback(interest): self._insertNewRecord(interest, keyName, timestamp) state.addSuccessCallback(successCallback) return True
def _parseCommandInterest(interest, state, keyLocatorName, timestamp): """ Get the keyLocatorName and timestamp from the command interest. :param Interest interest: The Interest to parse. :param ValidationState state: On error, this calls state.fail and returns False. :param Array<Name> keyLocatorName: Set keyLocatorName[0] to the KeyLocator name. :param Array<float> timestamp: Set timestamp[0] to the timestamp as milliseconds since Jan 1, 1970 UTC. :return: On success, return True. On error, call state.fail and return False. :rtype: bool """ keyLocatorName[0] = Name() timestamp[0] = 0 name = interest.getName() if name.size() < CommandInterestSigner.MINIMUM_SIZE: state.fail( ValidationError( ValidationError.POLICY_ERROR, "Command interest name `" + interest.getName().toUri() + "` is too short")) return False timestamp[0] = name.get(CommandInterestSigner.POS_TIMESTAMP).toNumber() keyLocatorName[0] = ValidationPolicy.getKeyLocatorName(interest, state) if state.isOutcomeFailed(): # Already failed. return False return True
def _verifyOriginalPacket(self, trustedCertificate): """ Verify the signature of the original packet. This is only called by the Validator class. :param CertificateV2 trustedCertificate: The certificate that signs the original packet. """ if VerificationHelpers.verifyInterestSignature(self._interest, trustedCertificate): logging.getLogger(__name__).info("OK signature for interest `" + self._interest.getName().toUri() + "`") for i in range(len(self._successCallbacks)): try: self._successCallbacks[i](self._interest) except: logging.exception("Error in successCallback") self.setOutcome(True) else: self.fail( ValidationError( ValidationError.INVALID_SIGNATURE, "Invalid signature of interest `" + self._interest.getName().toUri() + "`"))
def _validateCertificate(self, certificate, state): """ Recursively validate the certificates in the certification chain. :param CertificateV2 certificate: The certificate to check. :param ValidationState state: The current validation state. """ logging.getLogger(__name__).info("Start validating certificate " + certificate.getName().toUri()) if not certificate.isValid(): state.fail( ValidationError( ValidationError.EXPIRED_CERTIFICATE, "Retrieved certificate is not yet valid or expired `" + certificate.getName().toUri() + "`")) return def continueValidateCertificate(certificateRequest, state): if certificateRequest == None: state.fail( ValidationError( ValidationError.POLICY_ERROR, "Validation policy is not allowed to designate `" + certificate.getName().toUri() + "` as a trust anchor")) else: # We need to fetch the key and validate it. state.addCertificate(certificate) self._requestCertificate(certificateRequest, state) self._policy.checkCertificatePolicy(certificate, state, continueValidateCertificate)
def checkNames(self, packetName, keyLocatorName, state): """ :param Name packetName: :param Name keyLocatorName: :param ValidationState state: :rtype: bool """ if not self._packetNameRegex.match(packetName): state.fail( ValidationError( ValidationError.POLICY_ERROR, "The packet " + packetName.toUri() + " (KeyLocator=" + keyLocatorName.toUri() + ") does not match the hyper relation packet name regex " + self._packetNameRegex.getExpr())) return False if not self._keyNameRegex.match(keyLocatorName): state.fail( ValidationError( ValidationError.POLICY_ERROR, "The packet " + packetName.toUri() + " (KeyLocator=" + keyLocatorName.toUri() + ") does not match the hyper relation key name regex " + self._keyNameRegex.getExpr())) return False keyNameMatchExpansion = self._keyNameRegex.expand( self._keyNameExpansion) packetNameMatchExpansion = self._packetNameRegex.expand( self._packetNameExpansion) result = ConfigNameRelation.checkNameRelation( self._hyperRelation, keyNameMatchExpansion, packetNameMatchExpansion) if not result: state.fail( ValidationError( ValidationError.POLICY_ERROR, "KeyLocator check failed: hyper relation " + ConfigNameRelation.toString(self._hyperRelation) + " packet name match=" + packetNameMatchExpansion.toUri() + ", key name match=" + keyNameMatchExpansion.toUri() + " of packet " + packetName.toUri() + " (KeyLocator=" + keyLocatorName.toUri() + ") is invalid")) return result
def _checkPolicyHelper(self, keyName, state, continueValidation): """ :param Name keyName: :param ValidationState state: :param continueValidation: :type continueValidation: function object """ try: identity = self._pib.getIdentity( PibKey.extractIdentityFromKeyName(keyName)) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot get the PIB identity for key " + keyName.toUri() + ": " + repr(ex))) return try: key = identity.getKey(keyName) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot get the PIB key " + keyName.toUri() + ": " + repr(ex))) return try: certificate = key.getDefaultCertificate() except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot get the default certificate for key " + keyName.toUri() + ": " + repr(ex))) return # Add the certificate as the temporary trust anchor. self._validator.resetAnchors() self._validator.loadAnchor("", certificate) continueValidation(CertificateRequest(Interest(keyName)), state) # Clear the temporary trust anchor. self._validator.resetAnchors()
def onTimeout(interest): logging.getLogger(__name__).info( "Timeout while fetching certificate " + certificateRequest._interest.getName().toUri() + ", retrying") certificateRequest._nRetriesLeft -= 1 if certificateRequest._nRetriesLeft >= 0: try: self.fetch(certificateRequest, state, continueValidation) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in fetch: " + repr(ex))) else: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot fetch certificate after all retries `" + certificateRequest._interest.getName().toUri() + "`"))
def continueValidateCertificate(certificateRequest, state): if certificateRequest == None: state.fail( ValidationError( ValidationError.POLICY_ERROR, "Validation policy is not allowed to designate `" + certificate.getName().toUri() + "` as a trust anchor")) else: # We need to fetch the key and validate it. state.addCertificate(certificate) self._requestCertificate(certificateRequest, state)
def getKeyLocatorName(dataOrInterest, state): """ Extract the KeyLocator Name from a Data or signed Interest packet. The SignatureInfo in the packet must contain a KeyLocator of type KEYNAME. Otherwise, state.fail is invoked with INVALID_KEY_LOCATOR. :param dataOrInterest: The Data or Interest packet with the KeyLocator. :type dataOrInterest: Data or Interest :param ValidationState state: On error, this calls state.fail and returns an empty Name. :return: The KeyLocator name, or an empty Name for failure. :rtype: Name """ if isinstance(dataOrInterest, Data): data = dataOrInterest return ValidationPolicy._getKeyLocatorNameFromSignature( data.getSignature(), state) else: interest = dataOrInterest name = interest.getName() if name.size() < 2: state.fail( ValidationError(ValidationError.INVALID_KEY_LOCATOR, "Invalid signed Interest: name too short")) return Name() try: # TODO: Generalize the WireFormat. signatureInfo = WireFormat.getDefaultWireFormat( ).decodeSignatureInfoAndValue( interest.getName().get(-2).getValue().buf(), interest.getName().get(-1).getValue().buf()) except Exception as ex: state.fail( ValidationError(ValidationError.INVALID_KEY_LOCATOR, "Invalid signed Interest: " + repr(ex))) return Name() return ValidationPolicy._getKeyLocatorNameFromSignature( signatureInfo, state)
def _requestCertificate(self, certificateRequest, state): """ Request a certificate for further validation. :param CertificateRequest certificateRequest: The certificate request. :param ValidationState state: The current validation state. """ if state.getDepth() >= self._maxDepth: state.fail( ValidationError(ValidationError.EXCEEDED_DEPTH_LIMIT, "Exceeded validation depth limit")) return if state.hasSeenCertificateName( certificateRequest._interest.getName()): state.fail( ValidationError( ValidationError.LOOP_DETECTED, "Validation loop detected for certificate `" + certificateRequest._interest.getName().toUri() + "`")) return logging.getLogger(__name__).info( "Retrieving " + certificateRequest._interest.getName().toUri()) certificate = self.findTrustedCertificate(certificateRequest._interest) if certificate != None: logging.getLogger(__name__).info("Found trusted certificate " + certificate.getName().toUri()) certificate = state._verifyCertificateChain(certificate) if certificate != None: state._verifyOriginalPacket(certificate) for i in range(len(state._certificateChain)): self.cacheVerifiedCertificate(state._certificateChain[i]) return self._certificateFetcher.fetch(certificateRequest, state, self._validateCertificate)
def onNetworkNack(interest, networkNack): logging.getLogger( __name__).info("NACK (" + str(networkNack.getReason()) + ") while fetching certificate " + certificateRequest._interest.getName().toUri()) certificateRequest._nRetriesLeft -= 1 if certificateRequest._nRetriesLeft >= 0: try: self.fetch(certificateRequest, state, continueValidation) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in fetch: " + repr(ex))) else: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot fetch certificate after all retries `" + certificateRequest._interest.getName().toUri() + "`"))
def onData(interest, data): logging.getLogger(__name__).info( "Fetched certificate from network " + data.getName().toUri()) try: certificate = CertificateV2(data) except Exception as ex: state.fail( ValidationError( ValidationError.MALFORMED_CERTIFICATE, "Fetched a malformed certificate `" + data.getName().toUri() + "` (" + repr(ex) + ")")) return try: continueValidation(certificate, state) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in continueValidation: " + repr(ex)))
def _doFetch(self, certificateRequest, state, continueValidation): """ :type certificateRequest: CertificateRequest :type state: TValidationState :type continueValidation: function object """ state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot fetch certificate " + certificateRequest._interest.getName().toUri() + " in offline mode"))
def _getKeyLocatorNameFromSignature(signatureInfo, state): """ A helper method for getKeyLocatorName. :param Signature signatureInfo: :param ValidationState state: :rtype: Name """ if not KeyLocator.canGetFromSignature(signatureInfo): state.fail( ValidationError(ValidationError.INVALID_KEY_LOCATOR, "KeyLocator is missing")) return Name() keyLocator = KeyLocator.getFromSignature(signatureInfo) if keyLocator.getType() != KeyLocatorType.KEYNAME: state.fail( ValidationError(ValidationError.INVALID_KEY_LOCATOR, "KeyLocator type is not Name")) return Name() return keyLocator.getKeyName()
def checkNames(self, packetName, keyLocatorName, state): """ :param Name packetName: :param Name keyLocatorName: :param ValidationState state: :rtype: bool """ result = self._regex.match(keyLocatorName) if not result: state.fail( ValidationError( ValidationError.POLICY_ERROR, "KeyLocator check failed: regex " + self._regex.getExpr() + " for packet " + packetName.toUri() + " is invalid (KeyLocator=" + keyLocatorName.toUri() + ")")) return result
def _verifyCertificateChain(self, trustedCertificate): """ Verify signatures of certificates in the certificate chain. On return, the certificate chain contains a list of certificates successfully verified by trustedCertificate. When the certificate chain cannot be verified, this method will call fail() with the INVALID_SIGNATURE error code and the appropriate message. This is only called by the Validator class. :return: The certificate to validate the original data packet, either the last entry in the certificate chain or trustedCertificate if the certificate chain is empty. However, return None if the signature of at least one certificate in the chain is invalid, in which case all unverified certificates have been removed from the certificate chain. :rtype: CertificateV2 """ validatedCertificate = trustedCertificate for i in range(len(self._certificateChain)): certificateToValidate = self._certificateChain[i] if not VerificationHelpers.verifyDataSignature( certificateToValidate, validatedCertificate): self.fail( ValidationError( ValidationError.INVALID_SIGNATURE, "Invalid signature of certificate `" + certificateToValidate.getName().toUri() + "`")) # Remove this and remaining certificates in the chain. while len(self._certificateChain) > i: self._certificateChain.pop(i) return None else: logging.getLogger(__name__).info( "OK signature for certificate `" + certificateToValidate.getName().toUri() + "`") validatedCertificate = certificateToValidate return validatedCertificate
def checkPolicy(self, dataOrInterest, state, continueValidation): """ :param dataOrInterest: :type dataOrInterest: Data or Interest :param ValidationState state: :param continueValidation: :type continueValidation: function object """ keyLocatorName = ValidationPolicy.getKeyLocatorName( dataOrInterest, state) if state.isOutcomeFailed(): # Already called state.fail(). return if keyLocatorName.getPrefix(-2).isPrefixOf(dataOrInterest.getName()): continueValidation(CertificateRequest(Interest(keyLocatorName)), state) else: state.fail( ValidationError( ValidationError.INVALID_KEY_LOCATOR, "Signing policy violation for " + dataOrInterest.getName().toUri() + " by " + keyLocatorName.toUri()))
def checkNames(self, packetName, keyLocatorName, state): """ :param Name packetName: :param Name keyLocatorName: :param ValidationState state: :rtype: bool """ # packetName is not used in this check. identity = PibKey.extractIdentityFromKeyName(keyLocatorName) result = ConfigNameRelation.checkNameRelation(self._relation, self._name, identity) if not result: state.fail( ValidationError( ValidationError.POLICY_ERROR, "KeyLocator check failed: name relation " + self._name.toUri() + " " + ConfigNameRelation.toString(self._relation) + " for packet " + packetName.toUri() + " is invalid (KeyLocator=" + keyLocatorName.toUri() + ", identity=" + identity.toUri() + ")")) return result
def _doFetch(self, certificateRequest, state, continueValidation): """ Implement doFetch to use _face.expressInterest to fetch a certificate. :param CertificateRequest certificateRequest: The the request with the Interest for fetching the certificate. :param ValidationState state: The validation state. :param continueValidation: After fetching, this calls continueValidation(certificate, state) where certificate is the fetched certificate and state is the ValidationState. :type continueValidation: function object """ def onData(interest, data): logging.getLogger(__name__).info( "Fetched certificate from network " + data.getName().toUri()) try: certificate = CertificateV2(data) except Exception as ex: state.fail( ValidationError( ValidationError.MALFORMED_CERTIFICATE, "Fetched a malformed certificate `" + data.getName().toUri() + "` (" + repr(ex) + ")")) return try: continueValidation(certificate, state) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in continueValidation: " + repr(ex))) def onTimeout(interest): logging.getLogger(__name__).info( "Timeout while fetching certificate " + certificateRequest._interest.getName().toUri() + ", retrying") certificateRequest._nRetriesLeft -= 1 if certificateRequest._nRetriesLeft >= 0: try: self.fetch(certificateRequest, state, continueValidation) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in fetch: " + repr(ex))) else: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot fetch certificate after all retries `" + certificateRequest._interest.getName().toUri() + "`")) def onNetworkNack(interest, networkNack): logging.getLogger( __name__).info("NACK (" + str(networkNack.getReason()) + ") while fetching certificate " + certificateRequest._interest.getName().toUri()) certificateRequest._nRetriesLeft -= 1 if certificateRequest._nRetriesLeft >= 0: try: self.fetch(certificateRequest, state, continueValidation) except Exception as ex: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in fetch: " + repr(ex))) else: state.fail( ValidationError( ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Cannot fetch certificate after all retries `" + certificateRequest._interest.getName().toUri() + "`")) try: self._face.expressInterest(certificateRequest._interest, onData, onTimeout, onNetworkNack) except Exception as ex: state.fail( ValidationError(ValidationError.CANNOT_RETRIEVE_CERTIFICATE, "Error in expressInterest: " + repr(ex)))
def checkPolicy(self, dataOrInterest, state, continueValidation): """ :param dataOrInterest: :type dataOrInterest: Data or Interest :param ValidationState state: :param continueValidation: :type continueValidation: function object """ if self.hasInnerPolicy(): raise ValidatorConfigError( "ValidationPolicyConfig must be a terminal inner policy") if self._shouldBypass: continueValidation(None, state) return keyLocatorName = ValidationPolicy.getKeyLocatorName( dataOrInterest, state) if state.isOutcomeFailed(): # Already called state.fail() . return if isinstance(dataOrInterest, Data): data = dataOrInterest for i in range(len(self._dataRules)): rule = self._dataRules[i] if rule.match(False, data.getName()): if rule.check(False, data.getName(), keyLocatorName, state): continueValidation( CertificateRequest(Interest(keyLocatorName)), state) return else: # rule.check failed and already called state.fail() . return state.fail( ValidationError( ValidationError.POLICY_ERROR, "No rule matched for data `" + data.getName().toUri() + "`")) else: interest = dataOrInterest for i in range(len(self._interestRules)): rule = self._interestRules[i] if rule.match(True, interest.getName()): if rule.check(True, interest.getName(), keyLocatorName, state): continueValidation( CertificateRequest(Interest(keyLocatorName)), state) return else: # rule.check failed and already called state.fail() . return state.fail( ValidationError( ValidationError.POLICY_ERROR, "No rule matched for interest `" + interest.getName().toUri() + "`"))