UNPKG

ndn-js

Version:

A JavaScript client library for Named Data Networking

443 lines (380 loc) 19.1 kB
/** * Copyright (C) 2018-2019 Regents of the University of California. * @author: Jeff Thompson <jefft0@remap.ucla.edu> * From ndn-cxx unit tests: * https://github.com/named-data/ndn-cxx/blob/master/tests/unit-tests/security/v2/validator.t.cpp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version, with the additional exemption that * compiling, linking, and/or using OpenSSL is allowed. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * A copy of the GNU Lesser General Public License is in the file COPYING. */ var assert = require("assert"); var Name = require('../../..').Name; var Interest = require('../../..').Interest; var Data = require('../../..').Data; var NetworkNack = require('../../..').NetworkNack; var ContentType = require('../../..').ContentType; var RsaKeyParams = require('../../..').RsaKeyParams; var SigningInfo = require('../../..').SigningInfo; var ValidityPeriod = require('../../..').ValidityPeriod; var CertificateV2 = require('../../..').CertificateV2; var ValidationPolicySimpleHierarchy = require('../../..').ValidationPolicySimpleHierarchy; var HierarchicalValidatorFixture = require('./hierarchical-validator-fixture.js').HierarchicalValidatorFixture; describe ("TestValidator", function() { beforeEach(function() { this.fixture_ = new HierarchicalValidatorFixture (new ValidationPolicySimpleHierarchy()); /** * Call fixture_.validator_.validate and if it calls the failureCallback then * fail the test with the given message. * @param {Data} data The Data to validate. * @param {String} message The message to show if the test fails. */ this.validateExpectSuccess = function(data, message) { this.fixture_.validator_.validate (data, function(data) {}, function(data, error) { assert.fail('', '', message); }); }; /** * Call fixture_.validator_.validate and if it calls the successCallback then * fail the test with the given message. * @param {Data} data The Data to validate. * @param {String} message The message to show if the test succeeds. */ this.validateExpectFailure = function(data, message) { this.fixture_.validator_.validate (data, function(data) { assert.fail('', '', message); }, function(data, error) {}); }; /** * Make a certificate and put it in the fixture_.cache_. * @param {PibKey} key * @param {PibKey} signer */ this.makeCertificate = function(key, signer) { // Copy the default certificate. var request = new CertificateV2(key.getDefaultCertificate()); request.setName(new Name(key.getName()).append("looper").appendVersion(1)); // Set SigningInfo. var params = new SigningInfo(signer); // Validity period from 100 days before to 100 days after now. var now = new Date().getTime(); params.setValidityPeriod(new ValidityPeriod (now - 100 * 24 * 3600 * 1000.0, now + 100 * 24 * 3600 * 1000.0)); this.fixture_.keyChain_.sign(request, params); this.fixture_.keyChain_.addCertificate(key, request); this.fixture_.cache_.insert(request); }; }); it("ConstructorSetValidator", function() { var validator = this.fixture_.validator_; var middlePolicy = new ValidationPolicySimpleHierarchy(); var innerPolicy = new ValidationPolicySimpleHierarchy(); validator.getPolicy().setInnerPolicy(middlePolicy); validator.getPolicy().setInnerPolicy(innerPolicy); assert.ok(validator.getPolicy().validator_ != null); assert.ok(validator.getPolicy().getInnerPolicy().validator_ != null); assert.ok (validator.getPolicy().getInnerPolicy().getInnerPolicy().validator_ != null); }); it("Timeouts", function() { // Disable responses from the simulated Face. this.fixture_.face_.processInterest_ = null; var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(data, "Should fail to retrieve certificate"); // There should be multiple expressed interests due to retries. assert.ok(this.fixture_.face_.sentInterests_.length > 1); }); it("NackedInterests", function() { this.fixture_.face_.processInterest_ = function (interest, onData, onTimeout, onNetworkNack) { var networkNack = new NetworkNack(); networkNack.setReason(NetworkNack.Reason.NO_ROUTE); onNetworkNack(interest, networkNack); }; var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(data, "All interests should get NACKed"); // There should be multiple expressed interests due to retries. assert.ok(this.fixture_.face_.sentInterests_.length > 1); }); it("MalformedCertificate", function() { // Copy the default certificate. var malformedCertificate = new Data (this.fixture_.subIdentity_.getDefaultKey().getDefaultCertificate()); malformedCertificate.getMetaInfo().setType(ContentType.BLOB); this.fixture_.keyChain_.sign (malformedCertificate, new SigningInfo(this.fixture_.identity_)); // It has the wrong content type and a missing ValidityPeriod. try { new CertificateV2(malformedCertificate).wireEncode(); assert.fail('', '', "Did not throw the expected exception"); } catch (ex) { if (!(ex instanceof CertificateV2.Error)) assert.fail('', '', "Did not throw the expected exception"); } var originalProcessInterest = this.fixture_.face_.processInterest_; this.fixture_.face_.processInterest_ = function (interest, onData, onTimeout, onNetworkNack) { if (interest.getName().isPrefixOf(malformedCertificate.getName())) onData(interest, malformedCertificate); else originalProcessInterest.processInterest (interest, onData, onTimeout, onNetworkNack); }; var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(data, "Signed by a malformed certificate"); assert.equal(1, this.fixture_.face_.sentInterests_.length); }); it("ExpiredCertificate", function() { // Copy the default certificate. var expiredCertificate = new Data (this.fixture_.subIdentity_.getDefaultKey().getDefaultCertificate()); var info = new SigningInfo(this.fixture_.identity_); // Validity period from 2 hours ago do 1 hour ago. var now = new Date().getTime(); info.setValidityPeriod (new ValidityPeriod(now - 2 * 3600 * 1000, now - 3600 * 1000.0)); this.fixture_.keyChain_.sign(expiredCertificate, info); try { new CertificateV2(expiredCertificate).wireEncode(); } catch (ex) { assert.fail('', '', "Unexpected exception: " + ex); } var originalProcessInterest = this.fixture_.face_.processInterest_; this.fixture_.face_.processInterest_ = function (interest, onData, onTimeout, onNetworkNack) { if (interest.getName().isPrefixOf(expiredCertificate.getName())) onData(interest, expiredCertificate); else originalProcessInterest.processInterest (interest, onData, onTimeout, onNetworkNack); }; var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(data, "Signed by an expired certificate"); assert.equal(1, this.fixture_.face_.sentInterests_.length); }); it("ResetAnchors", function() { this.fixture_.validator_.resetAnchors(); var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(data, "Should fail, as no anchors are configured"); }); it("TrustedCertificateCaching", function() { var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectSuccess (data, "Should get accepted, as signed by the policy-compliant certificate"); assert.equal(1, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; // Disable responses from the simulated Face. this.fixture_.face_.processInterest_ = null; this.validateExpectSuccess (data, "Should get accepted, based on the cached trusted certificate"); assert.equal(0, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; // Make the trusted cache simulate a time 2 hours later, after expiration. this.fixture_.validator_.setCacheNowOffsetMilliseconds_(2 * 3600 * 1000.0); this.validateExpectFailure(data, "Should try and fail to retrieve certificates"); // There should be multiple expressed interests due to retries. assert.ok(this.fixture_.face_.sentInterests_.length > 1); this.fixture_.face_.sentInterests_ = []; }); it("InfiniteCertificateChain", function() { var thisTest = this; this.fixture_.face_.processInterest_ = function (interest, onData, onTimeout, onNetworkNack) { try { // Create another key for the same identity and sign it properly. var parentKey = thisTest.fixture_.keyChain_.createKey(thisTest.fixture_.subIdentity_); var requestedKey = thisTest.fixture_.subIdentity_.getKey(interest.getName()); // Copy the Name. var certificateName = new Name(requestedKey.getName()); certificateName.append("looper").appendVersion(1); var certificate = new CertificateV2(); certificate.setName(certificateName); // Set the MetaInfo. certificate.getMetaInfo().setType(ContentType.KEY); // Set the freshness period to one hour. certificate.getMetaInfo().setFreshnessPeriod(3600 * 1000.0); // Set the content. certificate.setContent(requestedKey.getPublicKey()); // Set SigningInfo. var params = new SigningInfo(parentKey); // Validity period from 10 days before to 10 days after now. var now = new Date().getTime(); params.setValidityPeriod(new ValidityPeriod (now - 10 * 24 * 3600 * 1000.0, now + 10 * 24 * 3600 * 1000.0)); thisTest.fixture_.keyChain_.sign(certificate, params); onData(interest, certificate); } catch (ex) { assert.fail("Error in InfiniteCertificateChain: " + ex); } }; var data = new Data(new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.fixture_.validator_.setMaxDepth(40); assert.equal(40, this.fixture_.validator_.getMaxDepth()); this.validateExpectFailure(data, "Should fail since the certificate should be looped"); assert.equal(40, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; // Make the trusted cache simulate a time 5 hours later, after expiration. this.fixture_.validator_.setCacheNowOffsetMilliseconds_(5 * 3600 * 1000.0); this.fixture_.validator_.setMaxDepth(30); assert.equal(30, this.fixture_.validator_.getMaxDepth()); this.validateExpectFailure(data, "Should fail since the certificate chain is infinite"); assert.equal(30, this.fixture_.face_.sentInterests_.length); }); it("LoopedCertificateChain", function() { var identity1 = this.fixture_.addIdentity(new Name("/loop")); var key1 = this.fixture_.keyChain_.createKey (identity1, new RsaKeyParams(new Name.Component("key1"))); var key2 = this.fixture_.keyChain_.createKey (identity1, new RsaKeyParams(new Name.Component("key2"))); var key3 = this.fixture_.keyChain_.createKey (identity1, new RsaKeyParams(new Name.Component("key3"))); this.makeCertificate(key1, key2); this.makeCertificate(key2, key3); this.makeCertificate(key3, key1); var data = new Data(new Name("/loop/Data")); this.fixture_.keyChain_.sign(data, new SigningInfo(key1)); this.validateExpectFailure(data, "Should fail since the certificate chain loops"); assert.equal(3, this.fixture_.face_.sentInterests_.length); }); }); var ValidationPolicySimpleHierarchyForInterestOnly = function ValidationPolicySimpleHierarchyForInterestOnly() { // Call the base constructor. ValidationPolicySimpleHierarchy.call(this); }; ValidationPolicySimpleHierarchyForInterestOnly.prototype = new ValidationPolicySimpleHierarchy(); ValidationPolicySimpleHierarchyForInterestOnly.prototype.name = "ValidationPolicySimpleHierarchyForInterestOnly"; ValidationPolicySimpleHierarchyForInterestOnly.prototype.checkPolicy = function (dataOrInterest, state, continueValidation) { if (dataOrInterest instanceof Data) continueValidation(null, state); else // Call the base method for the Interest. ValidationPolicySimpleHierarchy.prototype.checkPolicy.call (this, dataOrInterest, state, continueValidation); }; describe ("TestValidatorInterestOnly", function() { beforeEach(function() { this.fixture_ = new HierarchicalValidatorFixture (new ValidationPolicySimpleHierarchyForInterestOnly()); /** * Call fixture_.validator_.validate and if it calls the failureCallback then * fail the test with the given message. * @param {Data|Interest} dataOrInterest The Data or Interest to validate. * @param {String} message The message to show if the test fails. */ this.validateExpectSuccess = function(dataOrInterest, message) { this.fixture_.validator_.validate (dataOrInterest, function(dataOrInterest) {}, function(dataOrInterest, error) { assert.fail('', '', message); }); }; /** * Call fixture_.validator_.validate and if it calls the successCallback then * fail the test with the given message. * @param {Data|Interest} dataOrInterest The Data or Interest to validate. * @param {String} message The message to show if the test succeeds. */ this.validateExpectFailure = function(dataOrInterest, message) { this.fixture_.validator_.validate (dataOrInterest, function(dataOrInterest) { assert.fail('', '', message); }, function(dataOrInterest, error) {}); }; }); it("ValidateInterestsButBypassForData", function() { var interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); var data = new Data (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.validateExpectFailure(interest, "Unsigned"); this.validateExpectSuccess (data, "The policy requests to bypass validation for all data"); assert.equal(0, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.fixture_.keyChain_.sign (interest, new SigningInfo(SigningInfo.SignerType.SHA256)); this.fixture_.keyChain_.sign (data, new SigningInfo(SigningInfo.SignerType.SHA256)); this.validateExpectFailure(interest, "Required KeyLocator/Name is missing (not passed to the policy)"); this.validateExpectSuccess (data, "The policy requests to bypass validation for all data"); assert.equal(0, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.fixture_.keyChain_.sign(interest, new SigningInfo(this.fixture_.identity_)); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.identity_)); this.validateExpectSuccess(interest, "Should be successful since it is signed by the anchor"); this.validateExpectSuccess (data, "The policy requests to bypass validation for all data"); assert.equal(0, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.fixture_.keyChain_.sign(interest, new SigningInfo(this.fixture_.subIdentity_)); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subIdentity_)); this.validateExpectFailure(interest, "Should fail since the policy is not allowed to create new trust anchors"); this.validateExpectSuccess (data, "The policy requests to bypass validation for all data"); assert.equal(1, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.fixture_.keyChain_.sign(interest, new SigningInfo(this.fixture_.otherIdentity_)); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.otherIdentity_)); this.validateExpectFailure(interest, "Should fail since it is signed by a policy-violating certificate"); this.validateExpectSuccess (data, "The policy requests to bypass validation for all data"); // No network operations are expected since the certificate is not validated // by the policy. assert.equal(0, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; // Make the trusted cache simulate a time 2 hours later, after expiration. this.fixture_.validator_.setCacheNowOffsetMilliseconds_(2 * 3600 * 1000.0); interest = new Interest (new Name("/Security/V2/ValidatorFixture/Sub1/Sub2/Interest")); this.fixture_.keyChain_.sign(interest, new SigningInfo(this.fixture_.subSelfSignedIdentity_)); this.fixture_.keyChain_.sign(data, new SigningInfo(this.fixture_.subSelfSignedIdentity_)); this.validateExpectFailure(interest, "Should fail since the policy is not allowed to create new trust anchors"); this.validateExpectSuccess(data, "The policy requests to bypass validation for all data"); assert.equal(1, this.fixture_.face_.sentInterests_.length); this.fixture_.face_.sentInterests_ = []; }); });