Source: security/key-chain.js

/**
 * Copyright (C) 2014-2018 Regents of the University of California.
 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
 * From ndn-cxx security by Yingdi Yu <yingdi@cs.ucla.edu>.
 *
 * 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.
 *
 * 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.
 */

/** @ignore */
var path = require('path'); /** @ignore */
var fs = require('fs'); /** @ignore */
var Crypto = require('../crypto.js'); /** @ignore */
var LOG = require('../log.js').Log.LOG; /** @ignore */
var Name = require('../name.js').Name; /** @ignore */
var Interest = require('../interest.js').Interest; /** @ignore */
var Data = require('../data.js').Data; /** @ignore */
var ContentType = require('../meta-info.js').ContentType; /** @ignore */
var Blob = require('../util/blob.js').Blob; /** @ignore */
var ConfigFile = require('../util/config-file.js').ConfigFile; /** @ignore */
var WireFormat = require('../encoding/wire-format.js').WireFormat; /** @ignore */
var SecurityException = require('./security-exception.js').SecurityException; /** @ignore */
var RsaKeyParams = require('./key-params.js').RsaKeyParams; /** @ignore */
var BasicIdentityStorage = require('./identity/basic-identity-storage.js').BasicIdentityStorage; /** @ignore */
var IdentityCertificate = require('./certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
var Tpm = require('./tpm/tpm.js').Tpm; /** @ignore */
var TpmBackEndFile = require('./tpm/tpm-back-end-file.js').TpmBackEndFile; /** @ignore */
var TpmBackEndMemory = require('./tpm/tpm-back-end-memory.js').TpmBackEndMemory; /** @ignore */
var SyncPromise = require('../util/sync-promise.js').SyncPromise; /** @ignore */
var NdnCommon = require('../util/ndn-common.js').NdnCommon; /** @ignore */
var IdentityManager = require('./identity/identity-manager.js').IdentityManager; /** @ignore */
var CertificateV2 = require('./v2/certificate-v2.js').CertificateV2; /** @ignore */
var SigningInfo = require('./signing-info.js').SigningInfo; /** @ignore */
var Sha256WithRsaSignature = require('../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
var Sha256WithEcdsaSignature = require('../sha256-with-ecdsa-signature.js').Sha256WithEcdsaSignature; /** @ignore */
var DigestSha256Signature = require('../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
var HmacWithSha256Signature = require('../hmac-with-sha256-signature.js').HmacWithSha256Signature; /** @ignore */
var KeyLocator = require('../key-locator.js').KeyLocator; /** @ignore */
var KeyLocatorType = require('../key-locator.js').KeyLocatorType; /** @ignore */
var DigestAlgorithm = require('./security-types.js').DigestAlgorithm; /** @ignore */
var KeyType = require('./security-types.js').KeyType; /** @ignore */
var ValidityPeriod = require('./validity-period.js').ValidityPeriod; /** @ignore */
var VerificationHelpers = require('./verification-helpers.js').VerificationHelpers; /** @ignore */
var PublicKey = require('./certificate/public-key.js').PublicKey; /** @ignore */
var NoVerifyPolicyManager = require('./policy/no-verify-policy-manager.js').NoVerifyPolicyManager;

/**
 * A KeyChain provides a set of interfaces to the security library such as
 * identity management, policy configuration and packet signing and verification.
 * Note: This class is an experimental feature. See the API docs for more detail at
 * http://named-data.net/doc/ndn-ccl-api/key-chain.html .
 *
 * There are four forms to create a KeyChain:
 * KeyChain(pibLocator, tpmLocator, allowReset = false) - Create a KeyChain to
 * use the PIB and TPM defined by the given locators, which creates a security
 * v2 KeyChain that uses CertificateV2, Pib, Tpm and Validator (instead of v1
 * Certificate, IdentityStorage, PrivateKeyStorage and PolicyManager).
 * KeyChain(identityManager = null, policyManager = null) - Create a security v1
 * KeyChain to use the optional identityManager and policyManager.
 * KeyChain(pibImpl, tpmBackEnd, policyManager) - Create a KeyChain using this
 * temporary constructor for the transition to security v2, which creates a
 * security v2 KeyChain but still uses the v1 PolicyManager.
 * Finally, the default constructor KeyChain() creates a KeyChain with the
 * default PIB and TPM, which are platform-dependent and can be overridden
 * system-wide or individually by the user. The default constructor creates a
 * security v2 KeyChain that uses CertificateV2, Pib, Tpm and Validator.
 * However, if the default security v1 database file still exists, and the
 * default security v2 database file does not yet exists, then assume that the
 * system is running an older NFD and create a security v1 KeyChain with the
 * default IdentityManager and a NoVerifyPolicyManager.
 * @param {string} pibLocator The PIB locator, e.g., "pib-sqlite3:/example/dir".
 * @param {string} tpmLocator The TPM locator, e.g., "tpm-memory:".
 * @param {boolean} allowReset (optional) If True, the PIB will be reset when
 * the supplied tpmLocator mismatches the one in the PIB. If omitted, don't
 * allow reset.
 * @param {IdentityManager} identityManager (optional) The identity manager as a
 * subclass of IdentityManager. If omitted, use the default IdentityManager
 * constructor.
 * @param {PolicyManager} policyManager: (optional) The policy manager as a
 * subclass of PolicyManager. If omitted, use NoVerifyPolicyManager.
 * @param {PibImpl} pibImpl The PibImpl when using the constructor form
 * KeyChain(pibImpl, tpmBackEnd, policyManager).
 * @param {TpmBackEnd} tpmBackEnd: The TpmBackEnd when using the constructor
 * form KeyChain(pibImpl, tpmBackEnd, policyManager).
 * @throws SecurityException if this is not in Node.js and this uses the default
 * IdentityManager constructor. (See IdentityManager for details.)
 * @constructor
 */
var KeyChain = function KeyChain(arg1, arg2, arg3)
{
  this.identityManager_ = null;  // for security v1
  this.policyManager_ = null;    // for security v1
  this.face_ = null;             // for security v1

  this.pib_ = null;
  this.tpm_ = null;

  if (arg1 == undefined) {
    // The default constructor.
    if (!ConfigFile)
      // Assume we are in the browser.
      throw new SecurityException(new Error
        ("KeyChain: The default KeyChain constructor is not supported in the browser"));

    if (fs.existsSync(BasicIdentityStorage.getDefaultDatabaseFilePath()) &&
       !fs.existsSync(PibSqlite3.getDefaultDatabaseFilePath())) {
      // The security v1 SQLite file still exists and the security v2
      //   does not yet.
      arg1 = new IdentityManager();
      arg2 = new NoVerifyPolicyManager();
    }
    else {
      // Set the security v2 locators to default empty strings.
      arg1 = "";
      arg2 = "";
    }
  }

  if (typeof arg1 === 'string') {
    var pibLocator = arg1;
    var tpmLocator = arg2;
    var allowReset = arg3;
    if (allowReset == undefined)
      allowReset = false;

    this.isSecurityV1_ = false;

    // PIB locator.
    var pibScheme = [null];
    var pibLocation = [null];
    KeyChain.parseAndCheckPibLocator_(pibLocator, pibScheme, pibLocation);
    var canonicalPibLocator = pibScheme[0] + ":" + pibLocation[0];

    // Create the PIB and TPM, where Pib.initializePromise_ will complete the
    // initialization the first time it is called in an asynchronous context. We
    // can't do it here because this constructor cannot perform async operations.
    this.pib_ = KeyChain.createPib_(canonicalPibLocator);
    this.tpm_ = new Tpm("", "", null);
    this.pib_.initializeTpm_ = this.tpm_;
    this.pib_.initializePibLocator_ = pibLocator;
    this.pib_.initializeTpmLocator_ = tpmLocator;
    this.pib_.initializeAllowReset_ = allowReset;
    this.tpm_.initializePib_ = this.pib_;
  }
  else if (arg1 instanceof PibImpl) {
    var pibImpl = arg1;
    var tpmBackEnd = arg2;
    var policyManager = arg3;

    this.isSecurityV1_ = false;
    this.policyManager_ = policyManager;

    this.pib_ = new Pib("", "", pibImpl);
    this.tpm_ = new Tpm("", "", tpmBackEnd);
  }
  else {
    var identityManager = arg1;
    var policyManager = arg2;

    this.isSecurityV1_ = true;
    if (identityManager == undefined)
      identityManager = new IdentityManager();
    if (policyManager == undefined)
      policyManager = new NoVerifyPolicyManager();

    this.identityManager_ = identityManager;
    this.policyManager_ = policyManager;
  }
};

exports.KeyChain = KeyChain;

/**
 * Create a KeyChain.Error which represents an error in KeyChain processing.
 * Call with: throw new KeyChain.Error(new Error("message")).
 * @constructor
 * @param {Error} error The exception created with new Error.
 */
KeyChain.Error = function KeyChainError(error)
{
  if (error) {
    error.__proto__ = KeyChain.Error.prototype;
    return error;
  }
};

KeyChain.Error.prototype = new Error();
KeyChain.Error.prototype.name = "KeyChainError";

/**
 * @return {Pib}
 */
KeyChain.prototype.getPib = function()
{
  if (this.isSecurityV1_)
    throw new SecurityException(new Error
      ("getPib is not supported for security v1"));

  return this.pib_;
};

/**
 * @return {Tpm}
 */
KeyChain.prototype.getTpm = function()
{
  if (this.isSecurityV1_)
    throw new SecurityException(new Error
      ("getTpm is not supported for security v1"));

  return this.tpm_;
};

/**
 * Get the flag set by the constructor if this is a security v1 or v2 KeyChain.
 * @return (boolean} True if this is a security v1 KeyChain, false if this is a
 * security v2 KeyChain.
 */
KeyChain.prototype.getIsSecurityV1 = function() { return this.isSecurityV1_; };

// Identity management

/**
 * Create a security V2 identity for identityName. This method will check if the
 * identity exists in PIB and whether the identity has a default key and default
 * certificate. If the identity does not exist, this method will create the
 * identity in PIB. If the identity's default key does not exist, this method
 * will create a key pair and set it as the identity's default key. If the key's
 * default certificate is missing, this method will create a self-signed
 * certificate for the key. If identityName did not exist and no default
 * identity was selected before, the created identity will be set as the default
 * identity.
 * @param {Name} identityName The name of the identity.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use getDefaultKeyParams().
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that returns the created PibIdentity
 * instance.
 */
KeyChain.prototype.createIdentityV2Promise = function
  (identityName, params, useSync)
{
  useSync = (typeof params === "boolean") ? params : useSync;
  params = (typeof params === "boolean" || !params) ? undefined : params;

  if (params == undefined)
    params = KeyChain.getDefaultKeyParams();

  var thisKeyChain = this;
  var id;

  return this.pib_.addIdentityPromise_(identityName, useSync)
  .then(function(localId) {
    id = localId;

    return id.getDefaultKeyPromise(useSync)
    .catch(function(err) {
      if (err instanceof Pib.Error)
        return thisKeyChain.createKeyPromise(id, params, useSync);
      else
        return SyncPromise.reject(err);
    });
  })
  .then(function(key) {
    return key.getDefaultCertificatePromise(useSync)
    .catch(function(err) {
      if (err instanceof Pib.Error) {
        if (LOG > 2)
          console.log("No default cert for " + key.getName() +
            ", requesting self-signing")
        return thisKeyChain.selfSignPromise(key, useSync);
      }
      else
        return SyncPromise.reject(err);
    });
  })
  .then(function() {
    return SyncPromise.resolve(id);
  });
};

/**
 * Create a security V2 identity for identityName. This method will check if the
 * identity exists in PIB and whether the identity has a default key and default
 * certificate. If the identity does not exist, this method will create the
 * identity in PIB. If the identity's default key does not exist, this method
 * will create a key pair and set it as the identity's default key. If the key's
 * default certificate is missing, this method will create a self-signed
 * certificate for the key. If identityName did not exist and no default
 * identity was selected before, the created identity will be set as the default
 * identity.
 * @param {Name} identityName The name of the identity.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use getDefaultKeyParams().
 * @param {function} onComplete (optional) This calls
 * onComplete(identity) with the created PibIdentity instance. If omitted, the
 * return value is described below. (Some database libraries only use a callback,
 * so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {PibIdentity} If onComplete is omitted, return the created
 * PibIdentity instance. Otherwise, if onComplete is supplied then return
 * undefined and use onComplete as described above.
 */
KeyChain.prototype.createIdentityV2 = function
  (identityName, params, onComplete, onError)
{
  onError = (typeof params === "function") ? onComplete : onError;
  onComplete = (typeof params === "function") ? params : onComplete;
  params = (typeof params === "function" || !params) ? undefined : params;

  return SyncPromise.complete(onComplete, onError,
    this.createIdentityV2Promise(identityName, params, !onComplete));
};

/**
 * This method has two forms:
 * deleteIdentity(identity, useSync) - Delete the PibIdentity identity. After this
 * operation, the identity is invalid.
 * deleteIdentity(identityName, useSync) - Delete the identity from the public and
 * private key storage. If the identity to be deleted is the current default s
 * system default, the method will not delete the identity and will return
 * immediately.
 * @param {PibIdentity} identity The identity to delete.
 * @param {Name} identityName The name of the identity to delete.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete.
 */
KeyChain.prototype.deleteIdentityPromise = function(identity, useSync)
{
  var thisKeyChain = this;

  if (identity instanceof Name) {
    if (!this.isSecurityV1_) {
      return this.pib_.getIdentityPromise(identity, useSync)
      .then(function(pibIdentity) {
        return thisKeyChain.deleteIdentityPromise(pibIdentity, useSync);
      })
      .catch(function(err) {
        // Ignore errors.
        return SyncPromise.resolve();
      });

      return;
    }
    else
      return SyncPromise.reject(new KeyChain.Error(new Error
        ("deleteIdentityPromise is not supported for security v1. Use deleteIdentity.")));
  }

  var identityName = identity.getName();
  var keyNames = identity.getKeys_().getKeyNames();

  // Make a recursive function to do the loop.
  function deleteKeys(i) {
    if (i >= keyNames.length)
      // Done.
      return SyncPromise.resolve();

    return thisKeyChain.tpm_.deleteKeyPromise_(keyNames[i], useSync)
    .then(function() {
      // Recurse to the next iteration.
      return deleteKeys(i + 1);
    });
  }

  return deleteKeys(0)
  .then(function() {
    return thisKeyChain.pib_.removeIdentityPromise_(identityName, useSync);
    // TODO: Mark identity as invalid.
  });
};

/**
 * This method has two forms:
 * deleteIdentity(identity, onComplete, onError) - Delete the PibIdentity
 * identity (optionally using onComplete and onError callbacks). After this
 * operation, the identity is invalid.
 * deleteIdentity(identityName, onComplete, onError) - Delete the identity from
 * the public and private key storage (optionally using onComplete and onError
 * callbacks). If the identity to be deleted is the current default system
 * default, the method will not delete the identity and will return immediately.
 * @param {PibIdentity} identity The identity to delete.
 * @param {Name} identityName The name of the identity to delete.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.deleteIdentity = function(identity, onComplete, onError)
{
  if (identity instanceof Name && this.isSecurityV1_) {
    this.identityManager_.deleteIdentity(identity, onComplete, onError);
    return;
  }

  return SyncPromise.complete(onComplete, onError,
    this.deleteIdentityPromise(identity, !onComplete));
};

/**
 * Set the identity as the default identity.
 * @param {PibIdentity} identity The identity to make the default.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete.
 */
KeyChain.prototype.setDefaultIdentityPromise = function(identity, useSync)
{
  return this.pib_.setDefaultIdentityPromise_(identity.getName(), useSync);
};

/**
 * Set the identity as the default identity.
 * @param {PibIdentity} identity The identity to make the default.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.setDefaultIdentity = function(identity, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.setDefaultIdentityPromise(identity, !onComplete));
};

// Key management

/**
 * Create a key for the identity according to params. If the identity had no
 * default key selected, the created key will be set as the default for this
 * identity. This method will also create a self-signed certificate for the
 * created key.
 * @param {PibIdentity} identity A valid PibIdentity object.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use getDefaultKeyParams().
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that returns the new PibKey.
 */
KeyChain.prototype.createKeyPromise = function(identity, params, useSync)
{
  useSync = (typeof params === "boolean") ? params : useSync;
  params = (typeof params === "boolean" || !params) ? undefined : params;

  if (params == undefined)
    params = KeyChain.getDefaultKeyParams();

  var thisKeyChain = this;
  var key, keyName;

  // Create the key in the TPM.
  return this.tpm_.createKeyPromise_(identity.getName(), params, useSync)
  .then(function(localKeyName) {
    keyName = localKeyName;

    // Set up the key info in the PIB.
    return thisKeyChain.tpm_.getPublicKeyPromise(keyName, useSync);
  })
  .then(function(publicKey) {
    return identity.addKeyPromise_(publicKey.buf(), keyName, useSync);
  })
  .then(function(localKey) {
    key = localKey;

    if (LOG > 2)
      console.log
        ("Requesting self-signing for newly created key " + key.getName().toUri());
    return thisKeyChain.selfSignPromise(key, useSync);
  })
  .then(function() {
    return SyncPromise.resolve(key);
  });
};

/**
 * Create a key for the identity according to params. If the identity had no
 * default key selected, the created key will be set as the default for this
 * identity. This method will also create a self-signed certificate for the
 * created key.
 * @param {PibIdentity} identity A valid PibIdentity object.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use getDefaultKeyParams().
 * @param {function} onComplete (optional) This calls onComplete(key) with the
 * new PibKey. If omitted, the return value is described below. (Some database
 * libraries only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {PibKey} If onComplete is omitted, return the new PibKey. Otherwise,
 * if onComplete is supplied then return undefined and use onComplete as
 * described above.
 */
KeyChain.prototype.createKey = function(identity, params, onComplete, onError)
{
  onError = (typeof params === "function") ? onComplete : onError;
  onComplete = (typeof params === "function") ? params : onComplete;
  params = (typeof params === "function" || !params) ? undefined : params;

  return SyncPromise.complete(onComplete, onError,
    this.createKeyPromise(identity, params, !onComplete));
};

/**
 * Delete the given key of the given identity. The key becomes invalid.
 * @param {PibIdentity} identity A valid PibIdentity object.
 * @param {PibKey} key The key to delete.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete, or a promise rejected with Error if the key does not belong to the
 * identity.
 */
KeyChain.prototype.deleteKeyPromise = function(identity, key, useSync)
{
  var keyName = key.getName();
  if (!identity.getName().equals(key.getIdentityName()))
    return SyncPromise.reject(new Error
      ("Identity `" + identity.getName().toUri() + "` does not match key `" +
       keyName.toUri() + "`"));

  var thisKeyChain = this;

  return identity.removeKeyPromise_(keyName, useSync)
  .then(function() {
    return thisKeyChain.tpm_.deleteKeyPromise_(keyName, useSync);
  });
};

/**
 * Delete the given key of the given identity. The key becomes invalid.
 * @param {PibIdentity} identity A valid PibIdentity object.
 * @param {PibKey} key The key to delete.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @throws Error if the key does not belong to the identity. However, if
 * onComplete and onError are defined, then if there is an exception return
 * undefined and call onError(exception).
 */
KeyChain.prototype.deleteKey = function(identity, key, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.deleteKeyPromise(identity, key, !onComplete));
};

/**
 * Set the key as the default key of identity.
 * @param {type} identity A valid PibIdentity object.
 * @param {type} key The key to become the default.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete, or a promise rejected with Error if the key does not belong to the
 * identity.
 */
KeyChain.prototype.setDefaultKeyPromise = function(identity, key, useSync)
{
  if (!identity.getName().equals(key.getIdentityName()))
    return SyncPromise.reject(new Error
      ("Identity `" + identity.getName().toUri() + "` does not match key `" +
       key.getName().toUri() + "`"));

  return identity.setDefaultKeyPromise_(key.getName(), useSync);
};

/**
 * Set the key as the default key of identity.
 * @param {type} identity A valid PibIdentity object.
 * @param {type} key The key to become the default.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @throws Error if the key does not belong to the identity. However, if
 * onComplete and onError are defined, then if there is an exception return
 * undefined and call onError(exception).
 */
KeyChain.prototype.setDefaultKey = function(identity, key, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.setDefaultKeyPromise(identity, key, !onComplete));
};

// Certificate management

/**
 * Add a certificate for the key. If the key had no default certificate
 * selected, the added certificate will be set as the default certificate for
 * this key.
 * @param {PibKey} key A valid PibKey object.
 * @param {CertificateV2} certificate The certificate to add. This copies the
 * object.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete, or a promise rejected with Error if the key does not match the
 * certificate.
 */
KeyChain.prototype.addCertificatePromise = function(key, certificate, useSync)
{
  if (!key.getName().equals(certificate.getKeyName()) ||
      !certificate.getContent().equals(key.getPublicKey()))
    return SyncPromise.reject(new Error
      ("Key `" + key.getName().toUri() + "` does not match certificate `" +
       certificate.getKeyName().toUri() + "`"));

  return key.addCertificatePromise_(certificate, useSync);
};

/**
 * Add a certificate for the key. If the key had no default certificate
 * selected, the added certificate will be set as the default certificate for
 * this key.
 * @param {PibKey} key A valid PibKey object.
 * @param {CertificateV2} certificate The certificate to add. This copies the
 * object.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @throws Error if the key does not match the certificate. However, if
 * onComplete and onError are defined, then if there is an exception return
 * undefined and call onError(exception).
 */
KeyChain.prototype.addCertificate = function
  (key, certificate, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.addCertificatePromise(key, certificate, !onComplete));
};

/**
 * Delete the certificate with the given name from the given key. If the
 * certificate does not exist, this does nothing.
 * @param {PibKey} key A valid PibKey object.
 * @param {Name} certificateName The name of the certificate to delete.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete, or a promise rejected with Error if certificateName does not follow
 * certificate naming conventions.
 */
KeyChain.prototype.deleteCertificatePromise = function
  (key, certificateName, useSync)
{
  if (!CertificateV2.isValidName(certificateName))
    return SyncPromise.reject(new Error
      ("Wrong certificate name `" + certificateName.toUri() + "`"));

  return key.removeCertificatePromise_(certificateName, useSync);
};

/**
 * Delete the certificate with the given name from the given key. If the
 * certificate does not exist, this does nothing.
 * @param {PibKey} key A valid PibKey object.
 * @param {Name} certificateName The name of the certificate to delete.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @throws Error if certificateName does not follow certificate naming
 * conventions. However, if onComplete and onError are defined, then if there is
 * an exception return undefined and call onError(exception).
 */
KeyChain.prototype.deleteCertificate = function
  (key, certificateName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.deleteCertificatePromise(key, certificateName, !onComplete));
};

/**
 * Set the certificate as the default certificate of the key. The certificate
 * will be added to the key, potentially overriding an existing certificate if
 * it has the same name (without considering implicit digest).
 * @param {PibKey} key A valid PibKey object.
 * @param {CertificateV2} certificate The certificate to become the default.
 * This copies the object.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that fulfills when the operation is
 * complete.
 */
KeyChain.prototype.setDefaultCertificatePromise = function
  (key, certificate, useSync)
{
  // This replaces the certificate it it exists.
  return this.addCertificatePromise(key, certificate, useSync)
  .then(function() {
    return key.setDefaultCertificatePromise_(certificate.getName(), useSync);
  });
};

/**
 * Set the certificate as the default certificate of the key. The certificate
 * will be added to the key, potentially overriding an existing certificate if
 * it has the same name (without considering implicit digest).
 * @param {PibKey} key A valid PibKey object.
 * @param {CertificateV2} certificate The certificate to become the default.
 * This copies the object.
 * @param {function} onComplete (optional) This calls onComplete() when the
 * operation is complete. If omitted, do not use it. (Some database libraries
 * only use a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.setDefaultCertificate = function
  (key, certificate, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.setDefaultCertificatePromise(key, certificate, !onComplete));
};

// Signing

/**
 * Sign the target. If it is a Data or Interest object, set its signature. If it
 * is a Buffer, produce a Signature object.
 * @param {Data|Interest|Buffer} target If this is a Data object, wire encode
 * for signing, replace its Signature object based on the type of key and other
 * info in the SigningInfo params or default identity, and update the
 * wireEncoding. If this is an Interest object, wire encode for signing, append
 * a SignatureInfo to the Interest name, sign the name components and append a
 * final name component with the signature bits. If it is a buffer, sign it and
 * return a Signature object.
 * @param {SigningInfo|Name} paramsOrCertificateName (optional) If a SigningInfo,
 * it is the signing parameters. If a Name, it is the certificate name of the
 * key to use for signing. If omitted and this is a security v1 KeyChain then
 * use the IdentityManager to get the default identity. Otherwise, use the PIB
 * to get the default key of the default identity.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that returns the target (if target is
 * Data or Interest), or returns the generated Signature object (if target is a
 * Buffer).
 */
KeyChain.prototype.signPromise = function
  (target, paramsOrCertificateName, wireFormat, useSync)
{
  var arg2 = paramsOrCertificateName;
  var arg3 = wireFormat;
  var arg4 = useSync;
  // arg2,                    arg3,       arg4
  // paramsOrCertificateName, wireFormat, useSync
  // paramsOrCertificateName, wireFormat, null
  // paramsOrCertificateName, useSync,    null
  // paramsOrCertificateName, null,       null
  // wireFormat,              useSync,    null
  // wireFormat,              null,       null
  // useSync,                 null,       null
  // null,                    null,       null
  if (arg2 instanceof SigningInfo || arg2 instanceof Name)
    paramsOrCertificateName = arg2;
  else
    paramsOrCertificateName = undefined;

  if (arg2 instanceof WireFormat)
    wireFormat = arg2;
  else if (arg3 instanceof WireFormat)
    wireFormat = arg3;
  else
    wireFormat = undefined;

  if (typeof arg2 === "boolean")
    useSync = arg2;
  else if (typeof arg3 === "boolean")
    useSync = arg3;
  else if (typeof arg4 === "boolean")
    useSync = arg4;
  else
    useSync = false;

  if (wireFormat == undefined)
    wireFormat = WireFormat.getDefaultWireFormat();

  var thisKeyChain = this;

  return SyncPromise.resolve()
  .then(function() {
    if (paramsOrCertificateName == undefined) {
      // Convert sign(target) into sign(target, paramsOrCertificateName)
      if (thisKeyChain.isSecurityV1_) {
        return thisKeyChain.prepareDefaultCertificateNamePromise_(useSync)
        .then(function(name) {
          paramsOrCertificateName = name;
          return SyncPromise.resolve();
        });
      }
      else {
        paramsOrCertificateName = KeyChain.defaultSigningInfo_;
        return SyncPromise.resolve();
      }
    }
    else
      return SyncPromise.resolve();
  })
  .then(function() {
    if (paramsOrCertificateName instanceof Name) {
      var certificateName = paramsOrCertificateName;

      if (!thisKeyChain.isSecurityV1_) {
        // Make and use a SigningInfo for backwards compatibility.
        if (!((target instanceof Interest) || (target instanceof Data)))
          return SyncPromise.reject(new SecurityException(new Error
("sign(buffer, certificateName) is not supported for security v2. Use sign with SigningInfo.")));

        var signingInfo = new SigningInfo();
        signingInfo.setSigningCertificateName(certificateName);
        return thisKeyChain.signPromise(target, signingInfo, wireFormat, useSync)
        .catch(function(err) {
          return SyncPromise.reject(new SecurityException(new Error
            ("Error in sign: " + err)));
        });
      }
      else {
        if (target instanceof Interest)
          return thisKeyChain.identityManager_.signInterestByCertificatePromise
            (target, certificateName, wireFormat, useSync);
        else if (target instanceof Data)
          return thisKeyChain.identityManager_.signByCertificatePromise
            (target, certificateName, wireFormat, useSync);
        else
          return thisKeyChain.identityManager_.signByCertificatePromise
            (target, certificateName, useSync);
      }
    }

    var params = paramsOrCertificateName;

    if (target instanceof Data) {
      var data = target;

      var keyName = [null];
      return thisKeyChain.prepareSignatureInfoPromise_(params, keyName, useSync)
      .then(function(signatureInfo) {
        data.setSignature(signatureInfo);

        // Encode once to get the signed portion.
        var encoding = data.wireEncode(wireFormat);

        return thisKeyChain.signBufferPromise_
          (encoding.signedBuf(), keyName[0], params.getDigestAlgorithm(), useSync);
      })
      .then(function(signatureBytes) {
        data.getSignature().setSignature(signatureBytes);

        // Encode again to include the signature.
        data.wireEncode(wireFormat);
        return SyncPromise.resolve(data);
      });
    }
    else if (target instanceof Interest) {
      var interest = target;
      var signatureInfo;

      var keyName = [null];
      return thisKeyChain.prepareSignatureInfoPromise_(params, keyName, useSync)
      .then(function(localSignatureInfo) {
        signatureInfo = localSignatureInfo;

        // Append the encoded SignatureInfo.
        interest.getName().append(wireFormat.encodeSignatureInfo(signatureInfo));

        // Append an empty signature so that the "signedPortion" is correct.
        interest.getName().append(new Name.Component());
        // Encode once to get the signed portion, and sign.
        var encoding = interest.wireEncode(wireFormat);
        return thisKeyChain.signBufferPromise_
          (encoding.signedBuf(), keyName[0], params.getDigestAlgorithm(), useSync);
      })
      .then(function(signatureBytes) {
        signatureInfo.setSignature(signatureBytes);

        // Remove the empty signature and append the real one.
        interest.setName(interest.getName().getPrefix(-1).append
          (wireFormat.encodeSignatureValue(signatureInfo)));
        return SyncPromise.resolve(interest);
      });
    }
    else {
      var buffer = target;

      var keyName = [null];
      return thisKeyChain.prepareSignatureInfoPromise_(params, keyName, useSync)
      .then(function(signatureInfo) {
        return thisKeyChain.signBufferPromise_
          (buffer, keyName[0], params.getDigestAlgorithm(), useSync);
      });
    }
  });
};

/**
 * Sign the target. If it is a Data or Interest object, set its signature. If it
 * is a Buffer, produce a Signature object.
 * @param {Data|Interest|Buffer} target If this is a Data object, wire encode
 * for signing, replace its Signature object based on the type of key and other
 * info in the SigningInfo params or default identity, and update the
 * wireEncoding. If this is an Interest object, wire encode for signing, append
 * a SignatureInfo to the Interest name, sign the name components and append a
 * final name component with the signature bits. If it is a buffer, sign it and
 * return a Signature object.
 * @param {SigningInfo|Name} paramsOrCertificateName (optional) If a SigningInfo,
 * it is the signing parameters. If a Name, it is the certificate name of the
 * key to use for signing. If omitted and this is a security v1 KeyChain then
 * use the IdentityManager to get the default identity. Otherwise, use the PIB
 * to get the default key of the default identity.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @param {function} onComplete (optional) If target is a Data object, this calls
 * onComplete(data) with the supplied Data object which has been modified to set
 * its signature. If target is an Interest object, this calls
 * onComplete(interest) with the supplied Interest object which has been
 * modified to set its signature. If target is a Buffer, this calls
 * onComplete(signature) where signature is the produced Signature object. If
 * omitted, the return value is described below. (Some crypto libraries only use
 * a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {Signature} If onComplete is omitted, return the generated Signature
 * object (if target is a Buffer) or the target (if target is Data or Interest).
 * Otherwise, if onComplete is supplied then return undefined and use onComplete as
 * described above.
 */
KeyChain.prototype.sign = function
  (target, paramsOrCertificateName, wireFormat, onComplete, onError)
{
  var arg2 = paramsOrCertificateName;
  var arg3 = wireFormat;
  var arg4 = onComplete;
  var arg5 = onError;
  // arg2,                    arg3,       arg4,       arg5
  // paramsOrCertificateName, wireFormat, onComplete, onError
  // paramsOrCertificateName, wireFormat, null,       null
  // paramsOrCertificateName, onComplete, onError,    null
  // paramsOrCertificateName, null,       null,       null
  // wireFormat,              onComplete, onError,    null
  // wireFormat,              null,       null,       null
  // onComplete,              onError,    null,       null
  // null,                    null,       null,       null
  if (arg2 instanceof SigningInfo || arg2 instanceof Name)
    paramsOrCertificateName = arg2;
  else
    paramsOrCertificateName = null;

  if (arg2 instanceof WireFormat)
    wireFormat = arg2;
  else if (arg3 instanceof WireFormat)
    wireFormat = arg3;
  else
    wireFormat = null;

  if (typeof arg2 === "function") {
    onComplete = arg2;
    onError = arg3;
  }
  else if (typeof arg3 === "function") {
    onComplete = arg3;
    onError = arg4;
  }
  else if (typeof arg4 === "function") {
    onComplete = arg4;
    onError = arg5;
  }
  else {
    onComplete = null;
    onError = null;
  }

  return SyncPromise.complete(onComplete, onError,
    this.signPromise(target, paramsOrCertificateName, wireFormat, !onComplete));
};

/**
 * Generate a self-signed certificate for the public key and add it to the PIB.
 * This creates the certificate name from the key name by appending "self" and a
 * version based on the current time. If no default certificate for the key has
 * been set, then set the certificate as the default for the key.
 * @param {PibKey} key The PibKey with the key name and public key.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the certificate. If omitted, use WireFormat getDefaultWireFormat().
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that returns the new CertificateV2.
 */
KeyChain.prototype.selfSignPromise = function(key, wireFormat, useSync)
{
  var arg2 = wireFormat;
  var arg3 = useSync;
  // arg2,       arg3
  // wireFormat, useSync
  // wireFormat, null
  // useSync,    null

  if (arg2 instanceof WireFormat)
    wireFormat = arg2;
  else
    wireFormat = undefined;

  if (typeof arg2 === "boolean")
    useSync = arg2;
  else if (typeof arg3 === "boolean")
    useSync = arg3;
  else
    useSync = false;

  if (wireFormat == undefined)
    wireFormat = WireFormat.getDefaultWireFormat();

  var certificate = new CertificateV2();

  // Set the name.
  var now = new Date().getTime();
  var certificateName = new Name(key.getName());
  certificateName.append("self").appendVersion(now);
  certificate.setName(certificateName);

  // Set the MetaInfo.
  certificate.getMetaInfo().setType(ContentType.KEY);
  // Set a one-hour freshness period.
  certificate.getMetaInfo().setFreshnessPeriod(3600 * 1000.0);

  // Set the content.
  certificate.setContent(key.getPublicKey());

  // Set the signature-info.
  signingInfo = new SigningInfo(key);
  // Set a 20-year validity period.
  signingInfo.setValidityPeriod
    (new ValidityPeriod(now, now + 20 * 365 * 24 * 3600 * 1000.0));

  return this.signPromise(certificate, signingInfo, wireFormat, useSync)
  .then(function() {
    return key.addCertificatePromise_(certificate, useSync)
    .catch(function(ex) {
      // We don't expect this since we just created the certificate.
      return SyncPromise.reject(new KeyChain.Error(new Error
        ("Error encoding certificate: " + ex)));
    });
  })
  .then(function() {
    return SyncPromise.resolve(certificate);
  });
};

/**
 * Generate a self-signed certificate for the public key and add it to the PIB.
 * This creates the certificate name from the key name by appending "self" and a
 * version based on the current time. If no default certificate for the key has
 * been set, then set the certificate as the default for the key.
 * @param {PibKey} key The PibKey with the key name and public key.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the certificate. If omitted, use WireFormat getDefaultWireFormat().
 * @param {function} onComplete (optional) This calls
 * onComplete(certificate) with the new CertificateV2. If omitted, the return
 * value is described below. (Some crypto libraries only use a callback, so
 * onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {CertificateV2} If onComplete is omitted, return the new certificate.
 * Otherwise, if onComplete is supplied then return undefined and use onComplete
 * as described above.
 */
KeyChain.prototype.selfSign = function(key, wireFormat, onComplete, onError)
{
  if (typeof wireFormat === 'function') {
    // wireFormat is omitted, so shift.
    onError = onComplete;
    onComplete = wireFormat;
    wireFormat = undefined;
  }

  if (wireFormat == undefined)
    wireFormat = WireFormat.getDefaultWireFormat();

  return SyncPromise.complete(onComplete, onError,
    this.selfSignPromise(key, wireFormat, !onComplete));
};

// Import and export

/**
 * Import a certificate and its corresponding private key encapsulated in a
 * SafeBag. If the certificate and key are imported properly, the default
 * setting will be updated as if a new key and certificate is added into this
 * KeyChain.
 * @param {SafeBag} safeBag The SafeBag containing the certificate and private
 * key. This copies the values from the SafeBag.
 * @param {Buffer} password (optional) The password for decrypting the private
 * key. If the password is supplied, use it to decrypt the PKCS #8
 * EncryptedPrivateKeyInfo. If the password is omitted or null, import an
 * unencrypted PKCS #8 PrivateKeyInfo.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise which fulfills when finished.
 */
KeyChain.prototype.importSafeBagPromise = function(safeBag, password, useSync)
{
  if (typeof password === 'boolean') {
    // password is omitted, so shift.
    useSync = password;
    password = undefined;
  }

  var certificate;
  try {
    certificate = new CertificateV2(safeBag.getCertificate());
  } catch (ex) {
    return SyncPromise.reject(new Error("Error reading CertificateV2: " + ex));
  }
  var identity = certificate.getIdentity();
  var keyName = certificate.getKeyName();
  var publicKeyBits = certificate.getPublicKey();
  var content = new Blob([0x01, 0x02, 0x03, 0x04]);
  var signatureBits;
  var thisKeyChain = this;

  return SyncPromise.resolve()
  .then(function() {
    return thisKeyChain.tpm_.hasKeyPromise(keyName, useSync);
  })
  .then(function(hasKey) {
    if (hasKey)
      return SyncPromise.reject(new KeyChain.Error(new Error
        ("Private key `" + keyName.toUri() + "` already exists")));

    return thisKeyChain.pib_.getIdentityPromise(identity, useSync)
    .then(function(existingId) {
      return existingId.getKeyPromise(keyName, useSync);
    })
    .then(function() {
      return SyncPromise.reject(new KeyChain.Error(new Error
        ("Public key `" + keyName.toUri() + "` already exists")));
    }, function(err) {
      // Either the identity or the key doesn't exist, so OK to import.
      return SyncPromise.resolve();
    });
  })
  .then(function() {
    return thisKeyChain.tpm_.importPrivateKeyPromise_
      (keyName, safeBag.getPrivateKeyBag().buf(), password, useSync)
    .catch(function(err) {
      return SyncPromise.reject(new KeyChain.Error(new Error
        ("Failed to import private key `" + keyName.toUri() + "`: " + err)));
    });
  })
  .then(function() {
    // Check the consistency of the private key and certificate.
    return thisKeyChain.tpm_.signPromise
      (content.buf(), keyName, DigestAlgorithm.SHA256, useSync)
    .then(function(localSignatureBits) {
      signatureBits = localSignatureBits;
      return SyncPromise.resolve();
    }, function(err) {
      return thisKeyChain.tpm_.deleteKeyPromise_(keyName, useSync)
      .then(function() {
        return SyncPromise.reject(new KeyChain.Error(new Error
          ("Invalid private key `" + keyName.toUri() + "`")));
      });
    });
  })
  .then(function() {
    var publicKey;
    try {
      publicKey = new PublicKey(publicKeyBits);
    } catch (ex) {
      // Promote to KeyChain.Error.
      return thisKeyChain.tpm_.deleteKeyPromise_(keyName, useSync)
      .then(function() {
        return SyncPromise.reject(new KeyChain.Error(new Error
          ("Error decoding public key " + ex)));
      });
    }

    return VerificationHelpers.verifySignaturePromise
      (content, signatureBits, publicKey, useSync);
  })
  .then(function(isVerified) {
    if (!isVerified) {
      return thisKeyChain.tpm_.deleteKeyPromise_(keyName, useSync)
      .then(function() {
        return SyncPromise.reject(new KeyChain.Error(new Error
          ("Certificate `" + certificate.getName().toUri() +
           "` and private key `" + keyName.toUri() + "` do not match")));
      });
    }

    // The consistency is verified. Add to the PIB.
    return thisKeyChain.pib_.addIdentityPromise_(identity, useSync);
  })
  .then(function(id) {
    return id.addKeyPromise_(certificate.getPublicKey().buf(), keyName, useSync);
  })
  .then(function(key) {
    return key.addCertificatePromise_(certificate, useSync);
  });
};

/**
 * Import a certificate and its corresponding private key encapsulated in a
 * SafeBag. If the certificate and key are imported properly, the default
 * setting will be updated as if a new key and certificate is added into this
 * KeyChain.
 * @param {SafeBag} safeBag The SafeBag containing the certificate and private
 * key. This copies the values from the SafeBag.
 * @param {Buffer} password (optional) The password for decrypting the private
 * key. If the password is supplied, use it to decrypt the PKCS #8
 * EncryptedPrivateKeyInfo. If the password is omitted or null, import an
 * unencrypted PKCS #8 PrivateKeyInfo.
 * @param {function} onComplete (optional) This calls onComplete() when finished.
 * If omitted, just return when finished. (Some crypto libraries only use a
 * callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some crypto libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.importSafeBag = function
  (safeBag, password, onComplete, onError)
{
  onError = (typeof password === "function") ? onComplete : onError;
  onComplete = (typeof password === "function") ? password : onComplete;
  password = (typeof password === "function") ? null : password;

  return SyncPromise.complete(onComplete, onError,
    this.importSafeBagPromise(safeBag, password, !onComplete));
};

// PIB & TPM backend registry

/**
 * Add to the PIB factories map where scheme is the key and makePibImpl is the
 * value. If your application has its own PIB implementations, this must be
 * called before creating a KeyChain instance which uses your PIB scheme.
 * @param {string} scheme The PIB scheme.
 * @param {function} makePibImpl A callback which takes the PIB location and
 * returns a new PibImpl instance.
 */
KeyChain.registerPibBackend = function(scheme, makePibImpl)
{
  KeyChain.getPibFactories_()[scheme] = makePibImpl;
};

/**
 * Add to the TPM factories map where scheme is the key and makeTpmBackEnd is
 * the value. If your application has its own TPM implementations, this must be
 * called before creating a KeyChain instance which uses your TPM scheme.
 * @param {string} scheme The TPM scheme.
 * @param {function} makeTpmBackEnd A callback which takes the TPM location and
 * returns a new TpmBackEnd instance.
 */
KeyChain.registerTpmBackend = function(scheme, makeTpmBackEnd)
{
  KeyChain.getTpmFactories_()[scheme] = makeTpmBackEnd;
};

// Security v1 methods

/*****************************************
 *          Identity Management          *
 *****************************************/

/**
 * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
 * identity and a self-signed certificate of the KSK. If a key pair or
 * certificate for the identity already exists, use it.
 * @param {Name} identityName The name of the identity.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use KeyChain.getDefaultKeyParams().
 * @param {function} onComplete (optional) This calls onComplete(certificateName)
 * with name of the default certificate of the identity. If omitted, the return
 * value is described below. (Some crypto libraries only use a callback, so
 * onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {Name} If onComplete is omitted, return the name of the default
 * certificate of the identity. Otherwise, if onComplete is supplied then return
 * undefined and use onComplete as described above.
 */
KeyChain.prototype.createIdentityAndCertificate = function
  (identityName, params, onComplete, onError)
{
  onError = (typeof params === "function") ? onComplete : onError;
  onComplete = (typeof params === "function") ? params : onComplete;
  params = (typeof params === "function" || !params) ?
    KeyChain.getDefaultKeyParams() : params;

  return this.identityManager_.createIdentityAndCertificate
    (identityName, params, onComplete, onError);
};

/**
 * Create an identity by creating a pair of Key-Signing-Key (KSK) for this
 * identity and a self-signed certificate of the KSK. If a key pair or
 * certificate for the identity already exists, use it.
 * @deprecated Use createIdentityAndCertificate which returns the
 * certificate name instead of the key name. You can use
 * IdentityCertificate.certificateNameToPublicKeyName to convert the
 * certificate name to the key name.
 * @param {Name} identityName The name of the identity.
 * @param {KeyParams} params (optional) The key parameters if a key needs to be
 * generated for the identity. If omitted, use KeyChain.getDefaultKeyParams().
 * @return {Name} The key name of the auto-generated KSK of the identity.
 */
KeyChain.prototype.createIdentity = function(identityName, params)
{
  return IdentityCertificate.certificateNameToPublicKeyName
    (this.createIdentityAndCertificate(identityName, params));
};

/**
 * Get the default identity.
 * @param {function} onComplete (optional) This calls onComplete(identityName)
 * with name of the default identity. If omitted, the return value is described
 * below. (Some crypto libraries only use a callback, so onComplete is required
 * to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * @return {Name} If onComplete is omitted, return the name of the default
 * identity. Otherwise, if onComplete is supplied then return undefined and use
 * onComplete as described above.
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @throws SecurityException if the default identity is not set. However, if
 * onComplete and onError are defined, then if there is an exception return
 * undefined and call onError(exception).
 */
KeyChain.prototype.getDefaultIdentity = function(onComplete, onError)
{
  if (!this.isSecurityV1_) {
    return SyncPromise.complete(onComplete, onError,
      this.pib_.getDefaultIdentityPromise(!onComplete)
      .then(function(pibIdentity) {
        return SyncPromise.resolve(pibIdentity.getName());
      }));
  }

  return this.identityManager_.getDefaultIdentity(onComplete, onError);
};

/**
 * Get the default certificate name of the default identity, which will be used
 * when signing is based on identity and the identity is not specified.
 * @param {function} onComplete (optional) This calls onComplete(certificateName)
 * with name of the default certificate. If omitted, the return value is described
 * below. (Some crypto libraries only use a callback, so onComplete is required
 * to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {Name} If onComplete is omitted, return the default certificate name.
 * Otherwise, if onComplete is supplied then return undefined and use onComplete
 * as described above.
 * @throws SecurityException if the default identity is not set or the default
 * key name for the identity is not set or the default certificate name for
 * the key name is not set. However, if onComplete and onError are defined, then
 * if there is an exception return undefined and call onError(exception).
 */
KeyChain.prototype.getDefaultCertificateName = function(onComplete, onError)
{
  if (!this.isSecurityV1_) {
    return SyncPromise.complete(onComplete, onError,
      this.pib_.getDefaultIdentityPromise(!onComplete)
      .then(function(identity) {
        return identity.getDefaultKeyPromise(!onComplete);
      })
      .then(function(key) {
        return key.getDefaultCertificatePromise(!onComplete);
      })
      .then(function(certificate) {
        return SyncPromise.resolve(certificate.getName());
      }));
  }

  return this.identityManager_.getDefaultCertificateName(onComplete, onError);
};

/**
 * Generate a pair of RSA keys for the specified identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk (optional) true for generating a Key-Signing-Key (KSK),
 * false for a Data-Signing-Key (DSK). If omitted, generate a Data-Signing-Key.
 * @param {number} keySize (optional) The size of the key. If omitted, use a
 * default secure key size.
 * @return {Name} The generated key name.
 */
KeyChain.prototype.generateRSAKeyPair = function(identityName, isKsk, keySize)
{
  if (!this.isSecurityV1_)
    throw new SecurityException(new Error
      ("generateRSAKeyPair is not supported for security v2. Use createIdentityV2."));

  keySize = (typeof isKsk === "boolean") ? isKsk : keySize;
  isKsk = (typeof isKsk === "boolean") ? isKsk : false;

  if (!keySize)
    keySize = 2048;

  return this.identityManager_.generateRSAKeyPair(identityName, isKsk, keySize);
};

/**
 * Set a key as the default key of an identity. The identity name is inferred
 * from keyName.
 * @param {Name} keyName The name of the key.
 * @param {Name} identityNameCheck (optional) The identity name to check that the
 * keyName contains the same identity name. If an empty name, it is ignored.
 * @param {function} onComplete (optional) This calls onComplete() when complete.
 * (Some database libraries only use a callback, so onComplete is required to
 * use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.setDefaultKeyForIdentity = function
  (keyName, identityNameCheck, onComplete, onError)
{
  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("setDefaultKeyForIdentity is not supported for security v2. Use getPib() methods."))));

  return this.identityManager_.setDefaultKeyForIdentity
    (keyName, identityNameCheck, onComplete, onError);
};

/**
 * Generate a pair of RSA keys for the specified identity and set it as the
 * default key for the identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk (optional) true for generating a Key-Signing-Key (KSK),
 * false for a Data-Signing-Key (DSK). If omitted, generate a Data-Signing-Key.
 * @param {number} keySize (optional) The size of the key. If omitted, use a
 * default secure key size.
 * @return {Name} The generated key name.
 */
KeyChain.prototype.generateRSAKeyPairAsDefault = function
  (identityName, isKsk, keySize)
{
  if (!this.isSecurityV1_)
    throw new SecurityException(new Error
      ("generateRSAKeyPairAsDefault is not supported for security v2. Use createIdentityV2."));

  return this.identityManager_.generateRSAKeyPairAsDefault
    (identityName, isKsk, keySize);
};

/**
 * Create a public key signing request.
 * @param {Name} keyName The name of the key.
 * @return {Blob} The signing request data.
 */
KeyChain.prototype.createSigningRequest = function(keyName)
{
  if (!this.isSecurityV1_) {
    var useSync = true;
    return SyncPromise.complete(null, null,
      this.pib_.getIdentityPromise
        (PibKey.extractIdentityFromKeyName(keyName, useSync))
      .then(function(identity) {
        return identity.getKeyPromise(keyName, useSync);
      })
      .then(function(key) {
        return SyncPromise.resolve(key.getPublicKey());
      }));
  }

  return this.identityManager_.getPublicKey(keyName).getKeyDer();
};

/**
 * Install an identity certificate into the public key identity storage.
 * @param {IdentityCertificate} certificate The certificate to to added.
 * @param {function} onComplete (optional) This calls onComplete() when complete.
 * (Some database libraries only use a callback, so onComplete is required to
 * use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.installIdentityCertificate = function
  (certificate, onComplete, onError)
{
  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("installIdentityCertificate is not supported for security v2. Use getPib() methods."))));

  this.identityManager_.addCertificate(certificate, onComplete, onError);
};

/**
 * Set the certificate as the default for its corresponding key.
 * @param {IdentityCertificate} certificate The certificate.
 * @param {function} onComplete (optional) This calls onComplete() when complete.
 * (Some database libraries only use a callback, so onComplete is required to
 * use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.setDefaultCertificateForKey = function
  (certificate, onComplete, onError)
{
  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("setDefaultCertificateForKey is not supported for security v2. Use getPib() methods."))));

  this.identityManager_.setDefaultCertificateForKey
    (certificate, onComplete, onError);
};

/**
 * Get a certificate which is still valid with the specified name.
 * @param {Name} certificateName The name of the requested certificate.
 * @param {function} onComplete (optional) This calls onComplete(certificate)
 * with the requested IdentityCertificate. If omitted, the return value is
 * described below. (Some crypto libraries only use a callback, so onComplete is
 * required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {IdentityCertificate} If onComplete is omitted, return the requested
 * certificate. Otherwise, if onComplete is supplied then return undefined and
 * use onComplete as described above.
 */
KeyChain.prototype.getCertificate = function
  (certificateName, onComplete, onError)
{
  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("getCertificate is not supported for security v2. Use getPib() methods."))));

  return this.identityManager_.getCertificate
    (certificateName, onComplete, onError);
};

/**
 * @deprecated Use getCertificate.
 */
KeyChain.prototype.getIdentityCertificate = function
  (certificateName, onComplete, onError)
{
  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("getIdentityCertificate is not supported for security v2. Use getPib() methods."))));

  return this.identityManager_.getCertificate
    (certificateName, onComplete, onError);
};

/**
 * Revoke a key.
 * @param {Name} keyName The name of the key that will be revoked.
 */
KeyChain.prototype.revokeKey = function(keyName)
{
  //TODO: Implement
};

/**
 * Revoke a certificate.
 * @param {Name} certificateName The name of the certificate that will be
 * revoked.
 */
KeyChain.prototype.revokeCertificate = function(certificateName)
{
  //TODO: Implement
};

/**
 * Get the identity manager given to or created by the constructor.
 * @return {IdentityManager} The identity manager.
 */
KeyChain.prototype.getIdentityManager = function()
{
  if (!this.isSecurityV1_)
    throw new SecurityException(new Error
      ("getIdentityManager is not supported for security v2"));

  return this.identityManager_;
};

/*****************************************
 *           Policy Management           *
 *****************************************/

/**
 * Get the policy manager given to or created by the constructor.
 * @return {PolicyManager} The policy manager.
 */
KeyChain.prototype.getPolicyManager = function()
{
  return this.policyManager_;
};

/*****************************************
 *              Sign/Verify              *
 *****************************************/

/**
 * Sign the target. If it is a Data object, set its signature. If it is an
 * array, produce a signature object.
 * @param {Data|Buffer} target If this is a Data object, wire encode for
 * signing, update its signature and key locator field and wireEncoding. If it
 * is an array, sign it and return a Signature object.
 * @param {Name} identityName (optional) The identity name for the key to use for
 * signing.  If omitted, infer the signing identity from the data packet name.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @param {function} onComplete (optional) If target is a Data object, this calls
 * onComplete(data) with the supplied Data object which has been modified to set
 * its signature. If target is a Buffer, this calls
 * onComplete(signature) where signature is the produced Signature object. If
 * omitted, the return value is described below. (Some crypto libraries only use
 * a callback, so onComplete is required to use these.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onError (optional) If defined, then onComplete must be
 * defined and if there is an exception, then this calls onError(exception)
 * with the exception. If onComplete is defined but onError is undefined, then
 * this will log any thrown exception. (Some database libraries only use a
 * callback, so onError is required to be notified of an exception.)
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @return {Signature} If onComplete is omitted, return the generated Signature
 * object (if target is a Buffer) or undefined (if target is Data).
 * Otherwise, if onComplete is supplied then return undefined and use onComplete
 * as described above.
 */
KeyChain.prototype.signByIdentity = function
  (target, identityName, wireFormat, onComplete, onError)
{
  onError = (typeof wireFormat === "function") ? onComplete : onError;
  onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
  wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;

  if (!this.isSecurityV1_)
    return SyncPromise.complete(onComplete, onError,
      SyncPromise.reject(new SecurityException(new Error
        ("signByIdentity(buffer, identityName) is not supported for security v2. Use sign with SigningInfo."))));

  var useSync = !onComplete;
  var thisKeyChain = this;

  if (identityName == null)
    identityName = new Name();

  if (target instanceof Data) {
    var data = target;

    var mainPromise = SyncPromise.resolve()
    .then(function() {
      if (identityName.size() == 0) {
        var inferredIdentity = thisKeyChain.policyManager_.inferSigningIdentity
          (data.getName());
        if (inferredIdentity.size() == 0)
          return thisKeyChain.identityManager_.getDefaultCertificateNamePromise
            (useSync);
        else
          return thisKeyChain.identityManager_.getDefaultCertificateNameForIdentityPromise
              (inferredIdentity, useSync);
      }
      else
        return thisKeyChain.identityManager_.getDefaultCertificateNameForIdentityPromise
          (identityName, useSync);
    })
    .then(function(signingCertificateName) {
      if (signingCertificateName.size() == 0)
        throw new SecurityException(new Error
          ("No qualified certificate name found!"));

      if (!thisKeyChain.policyManager_.checkSigningPolicy
           (data.getName(), signingCertificateName))
        throw new SecurityException(new Error
          ("Signing Cert name does not comply with signing policy"));

      return thisKeyChain.identityManager_.signByCertificatePromise
        (data, signingCertificateName, wireFormat, useSync);
    });

    return SyncPromise.complete(onComplete, onError, mainPromise);
  }
  else {
    var array = target;

    return SyncPromise.complete(onComplete, onError,
      this.identityManager_.getDefaultCertificateNameForIdentityPromise
        (identityName, useSync)
      .then(function(signingCertificateName) {
        if (signingCertificateName.size() == 0)
          throw new SecurityException(new Error
            ("No qualified certificate name found!"));

        return thisKeyChain.identityManager_.signByCertificatePromise
          (array, signingCertificateName, wireFormat, useSync);
      }));
  }
};

/**
 * Sign the target using DigestSha256.
 * @param {Data|Interest} target If this is a Data object, wire encode for
 * signing, digest it and set its SignatureInfo to a DigestSha256, updating its
 * signature and wireEncoding. If this is an Interest object, wire encode for
 * signing, append a SignatureInfo for DigestSha256 to the Interest name, digest
 * the name components and append a final name component with the signature bits.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 */
KeyChain.prototype.signWithSha256 = function(target, wireFormat)
{
  if (!this.isSecurityV1_) {
    var signingInfo = SigningInfo();
    signingInfo.setSha256Signing();
    this.sign(target, signingInfo, wireFormat);

    return;
  }

  if (target instanceof Interest)
    this.identityManager_.signInterestWithSha256(target, wireFormat);
  else
    this.identityManager_.signWithSha256(target, wireFormat);
};

/**
 * Check the signature on the Data object and call either onVerify or
 * onValidationFailed. We use callback functions because verify may fetch
 * information to check the signature.
 * @param {Data} data The Data object with the signature to check.
 * @param {function} onVerified If the signature is verified, this calls
 * onVerified(data).
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onValidationFailed If the signature check fails, this calls
 * onValidationFailed(data, reason) with the Data object and reason string.
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {number} stepCount
 */
KeyChain.prototype.verifyData = function
  (data, onVerified, onValidationFailed, stepCount)
{
  if (stepCount == null)
    stepCount = 0;

  if (this.policyManager_.requireVerify(data)) {
    var nextStep = this.policyManager_.checkVerificationPolicy
      (data, stepCount, onVerified, onValidationFailed);
    if (nextStep != null) {
      var thisKeyChain = this;
      this.face_.expressInterest
        (nextStep.interest,
         function(callbackInterest, callbackData) {
           thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
         },
         function(callbackInterest) {
           thisKeyChain.onCertificateInterestTimeout
             (callbackInterest, nextStep.retry, onValidationFailed, data, nextStep);
         });
    }
  }
  else if (this.policyManager_.skipVerifyAndTrust(data)) {
    try {
      onVerified(data);
    } catch (ex) {
      console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
    }
  }
  else {
    try {
      onValidationFailed
        (data, "The packet has no verify rule but skipVerifyAndTrust is false");
    } catch (ex) {
      console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
    }
  }
};

/**
 * Check the signature on the signed interest and call either onVerify or
 * onValidationFailed. We use callback functions because verify may fetch
 * information to check the signature.
 * @param {Interest} interest The interest with the signature to check.
 * @param {function} onVerified If the signature is verified, this calls
 * onVerified(interest).
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 * @param {function} onValidationFailed If the signature check fails, this calls
 * onValidationFailed(interest, reason) with the Interest object and reason
 * string.
 * NOTE: The library will log any exceptions thrown by this callback, but for
 * better error handling the callback should catch and properly handle any
 * exceptions.
 */
KeyChain.prototype.verifyInterest = function
  (interest, onVerified, onValidationFailed, stepCount, wireFormat)
{
  if (stepCount == null)
    stepCount = 0;
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

  if (this.policyManager_.requireVerify(interest)) {
    var nextStep = this.policyManager_.checkVerificationPolicy
      (interest, stepCount, onVerified, onValidationFailed, wireFormat);
    if (nextStep != null) {
      var thisKeyChain = this;
      this.face_.expressInterest
        (nextStep.interest,
         function(callbackInterest, callbackData) {
           thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
         },
         function(callbackInterest) {
           thisKeyChain.onCertificateInterestTimeout
             (callbackInterest, nextStep.retry, onValidationFailed, interest,
              nextStep);
         });
    }
  }
  else if (this.policyManager_.skipVerifyAndTrust(interest)) {
    try {
      onVerified(interest);
    } catch (ex) {
      console.log("Error in onVerified: " + NdnCommon.getErrorWithStackTrace(ex));
    }
  }
  else {
    try {
      onValidationFailed
        (interest,
         "The packet has no verify rule but skipVerifyAndTrust is false");
    } catch (ex) {
      console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
    }
  }
};

/**
 * Set the Face which will be used to fetch required certificates.
 * @param {Face} face A pointer to the Face object.
 */
KeyChain.prototype.setFace = function(face)
{
  this.face_ = face;
};

/**
 * Wire encode the target, compute an HmacWithSha256 and update the object.
 * Note: This method is an experimental feature. The API may change.
 * @param {Data|Interest} target If the target is a Data object (which should
 * already have an HmacWithSha256Signature with a KeyLocator for the key name),
 * then update its signature and wire encoding. If the target is an Interest,
 * then append a SignatureInfo to the Interest name, compute an HmacWithSha256
 * signature for the name components and append a final name component with the
 * signature bits.
 * @param {Blob} key The key for the HmacWithSha256.
 * param {Name} keyName (needed if target is an Interest) The name of the key
 * for the KeyLocator in the SignatureInfo which is added to the Interest name.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the target. If omitted, use WireFormat getDefaultWireFormat().
 */
KeyChain.signWithHmacWithSha256 = function(target, key, keyName, wireFormat)
{
  if (keyName instanceof WireFormat) {
    // The keyName is omitted, so shift arguments.
    wireFormat = keyName;
    keyName = undefined;
  }

  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

  if (target instanceof Data) {
    var data = target;
    // Encode once to get the signed portion.
    var encoding = data.wireEncode(wireFormat);

    var signer = Crypto.createHmac('sha256', key.buf());
    signer.update(encoding.signedBuf());
    data.getSignature().setSignature(
      new Blob(signer.digest(), false));
  }
  else if (target instanceof Interest) {
    var interest = target;

    if (keyName == null)
      throw new SecurityException(new Error
        ("signWithHmacWithSha256: keyName is required to sign an Interest"));

    var signature = new HmacWithSha256Signature();
    signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
    signature.getKeyLocator().setKeyName(keyName);

    // Append the encoded SignatureInfo.
    interest.getName().append(wireFormat.encodeSignatureInfo(signature));
    // Append an empty signature so that the "signedPortion" is correct.
    interest.getName().append(new Name.Component());

    // Encode once to get the signed portion.
    var encoding = interest.wireEncode(wireFormat);

    var signer = Crypto.createHmac('sha256', key.buf());
    signer.update(encoding.signedBuf());
    signature.setSignature(new Blob(signer.digest(), false));

    // Remove the empty signature and append the real one.
    interest.setName(interest.getName().getPrefix(-1).append
      (wireFormat.encodeSignatureValue(signature)));
  }
  else
    throw new SecurityException(new Error
      ("signWithHmacWithSha256: Unrecognized target type"));
};

/**
 * Compute a new HmacWithSha256 for the target and verify it against the
 * signature value.
 * Note: This method is an experimental feature. The API may change.
 * @param {Data} data The Data object to verify.
 * @param {Blob} key The key for the HmacWithSha256.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @return {boolean} True if the signature verifies, otherwise false.
 */
KeyChain.verifyDataWithHmacWithSha256 = function(data, key, wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

  // wireEncode returns the cached encoding if available.
  var encoding = data.wireEncode(wireFormat);

  var signer = Crypto.createHmac('sha256', key.buf());
  signer.update(encoding.signedBuf());
  var newSignatureBits = new Blob(signer.digest(), false);

  // Use the flexible Blob.equals operator.
  return newSignatureBits.equals(data.getSignature().getSignature());
};

/**
 * Compute a new HmacWithSha256 for all but the final name component and verify
 * it against the signature value in the final name component.
 * Note: This method is an experimental feature. The API may change.
 * @param {Interest} interest The Interest object to verify.
 * @param {Blob} key The key for the HmacWithSha256.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @return {boolean} True if the signature verifies, otherwise false.
 */
KeyChain.verifyInterestWithHmacWithSha256 = function(interest, key, wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

  // Decode the last two name components of the signed interest.
  var signature = wireFormat.decodeSignatureInfoAndValue
    (interest.getName().get(-2).getValue().buf(),
     interest.getName().get(-1).getValue().buf());

  // wireEncode returns the cached encoding if available.
  var encoding = interest.wireEncode(wireFormat);

  var signer = Crypto.createHmac('sha256', key.buf());
  signer.update(encoding.signedBuf());
  var newSignatureBits = new Blob(signer.digest(), false);

  // Use the flexible Blob.equals operator.
  return newSignatureBits.equals(signature.getSignature());
};

KeyChain.getDefaultKeyParams = function() { return KeyChain.defaultKeyParams_; };

/**
 * @deprecated Use getDefaultKeyParams().
 */
KeyChain.DEFAULT_KEY_PARAMS = new RsaKeyParams();

// Private security v2 methods

/**
 * Get the PIB factories map. On the first call, this initializes the map with
 * factories for standard PibImpl implementations.
 * @return {object} A map where the key is the scheme string and the value is a
 * function makePibImpl(location) which takes a string location and returns a
 * new PibImpl object.
 */
KeyChain.getPibFactories_ = function()
{
  if (KeyChain.pibFactories_ == null) {
    KeyChain.pibFactories_ = {};

    // Add the standard factories.
    if (PibSqlite3)
      // PibSqlite3 is defined for Node.js .
      KeyChain.pibFactories_[PibSqlite3.getScheme()] =
        function(location) { return new PibSqlite3(location); };
    KeyChain.pibFactories_[PibMemory.getScheme()] =
      function(location) { return new PibMemory(); };
  }

  return KeyChain.pibFactories_;
};

/**
 * Get the TPM factories map. On the first call, this initializes the map with
 * factories for standard TpmBackEnd implementations.
 * @return {object} A map where the key is the scheme string and the value is a
 * function makeTpmBackEnd(location) which takes a string location and returns a
 * new TpmBackEnd object.
 */
KeyChain.getTpmFactories_ = function()
{
  if (KeyChain.tpmFactories_ == null) {
    KeyChain.tpmFactories_ = {};

    // Add the standard factories.
    if (TpmBackEndFile)
      // TpmBackEndFile is defined for Node.js .
      KeyChain.tpmFactories_[TpmBackEndFile.getScheme()] =
        function(location) { return new TpmBackEndFile(location); };
    KeyChain.tpmFactories_[TpmBackEndMemory.getScheme()] =
      function(location) { return new TpmBackEndMemory(); };
  }

  return KeyChain.tpmFactories_;
};

/**
 * Parse the uri and set the scheme and location.
 * @param {string} uri The URI to parse.
 * @param {Array<string>} scheme Set scheme[0] to the scheme.
 * @param {Array<string>} location Set location[0] to the location.
 */
KeyChain.parseLocatorUri_ = function(uri, scheme, location)
{
  iColon = uri.indexOf(':');
  if (iColon >= 0) {
    scheme[0] = uri.substring(0, iColon);
    location[0] = uri.substring(iColon + 1);
  }
  else {
    scheme[0] = uri;
    location[0] = "";
  }
};

/**
 * Parse the pibLocator and set the pibScheme and pibLocation.
 * @param {string} pibLocator The PIB locator to parse.
 * @param {Array<string>} pibScheme Set pibScheme[0] to the PIB scheme.
 * @param {Array<string>} pibLocation Set pibLocation[0] to the PIB location.
 */
KeyChain.parseAndCheckPibLocator_ = function(pibLocator, pibScheme, pibLocation)
{
  KeyChain.parseLocatorUri_(pibLocator, pibScheme, pibLocation);

  if (pibScheme[0] == "")
    pibScheme[0] = KeyChain.getDefaultPibScheme_();

  if (KeyChain.getPibFactories_()[pibScheme[0]] == undefined)
    throw new KeyChain.Error(new Error
      ("PIB scheme `" + pibScheme[0] + "` is not supported"));
};

/**
 * Parse the tpmLocator and set the tpmScheme and tpmLocation.
 * @param {string} tpmLocator The TPM locator to parse.
 * @param {Array<string>} tpmScheme Set tpmScheme[0] to the TPM scheme.
 * @param {Array<string>} tpmLocation Set tpmLocation[0] to the TPM location.
 */
KeyChain.parseAndCheckTpmLocator_ = function(tpmLocator, tpmScheme, tpmLocation)
{
  KeyChain.parseLocatorUri_(tpmLocator, tpmScheme, tpmLocation);

  if (tpmScheme[0] == "")
    tpmScheme[0] = KeyChain.getDefaultTpmScheme_();

  if (KeyChain.getTpmFactories_()[tpmScheme[0]] == undefined)
    throw new KeyChain.Error(new Error
      ("TPM scheme `" + tpmScheme[0] + "` is not supported"));
};

/**
 * @return {string}
 */
KeyChain.getDefaultPibScheme_ = function() { return PibSqlite3.getScheme(); };

/**
 * @return {string}
 */
KeyChain.getDefaultTpmScheme_ = function()
{
  // Assume we are in Node.js, so check the system.
  if (process.platform === "darwin")
    throw new KeyChain.Error(new Error
      ("TpmBackEndOsx is not implemented. You must use tpm-file."));

  return TpmBackEndFile.getScheme();
};

/**
 * Create a Pib according to the pibLocator.
 * @param {string} pibLocator The PIB locator, e.g., "pib-sqlite3:/example/dir".
 * @return {Pib} A new Pib object.
 */
KeyChain.createPib_ = function(pibLocator)
{
  var pibScheme = [null];
  var pibLocation = [null];
  KeyChain.parseAndCheckPibLocator_(pibLocator, pibScheme, pibLocation);
  var pibFactory = KeyChain.getPibFactories_()[pibScheme[0]];
  return new Pib(pibScheme[0], pibLocation[0], pibFactory(pibLocation[0]));
};

/**
 * Set up tpm according to the tpmLocator. This is called by
 * Pib.initializePromise_ after determining the correct tpmLocator.
 * @param {Tpm} tpm The Tpm to set up.
 * @param {string} tpmLocator The TPM locator, e.g., "tpm-memory:".
 * @return {Tpm} A new Tpm object.
 */
KeyChain.setUpTpm_ = function(tpm, tpmLocator)
{
  var tpmScheme = [null];
  var tpmLocation = [null];
  KeyChain.parseAndCheckTpmLocator_(tpmLocator, tpmScheme, tpmLocation);
  var tpmFactory = KeyChain.getTpmFactories_()[tpmScheme[0]];
  tpm.scheme_ = tpmScheme[0];
  tpm.location_ = tpmLocation[0];
  tpm.backEnd_ = tpmFactory(tpmLocation[0]);
};

/**
 * @param {ConfigFile} config
 * @return {string}
 */
KeyChain.getDefaultPibLocator_ = function(config)
{
  if (KeyChain.defaultPibLocator_ != null)
    return KeyChain.defaultPibLocator_;

  var clientPib = process.env.NDN_CLIENT_PIB;
  if (clientPib != undefined && clientPib != "")
    KeyChain.defaultPibLocator_ = clientPib;
  else
    KeyChain.defaultPibLocator_ = config.get
      ("pib", KeyChain.getDefaultPibScheme_() + ":");

  return KeyChain.defaultPibLocator_;
};

/**
 * @param {ConfigFile} config
 * @return {string}
 */
KeyChain.getDefaultTpmLocator_ = function(config)
{
  if (KeyChain.defaultTpmLocator_ != null)
    return KeyChain.defaultTpmLocator_;

  var clientTpm = process.env.NDN_CLIENT_TPM;
  if (clientTpm != undefined && clientTpm != "")
    KeyChain.defaultTpmLocator_ = clientTpm;
  else
    KeyChain.defaultTpmLocator_ = config.get
      ("tpm", KeyChain.getDefaultTpmScheme_() + ":");

  return KeyChain.defaultTpmLocator_;
};

/**
 * Prepare a Signature object according to signingInfo and get the signing key
 * name.
 * @param {SigningInfo} params The signing parameters.
 * @param {Array<Name>} keyName Set keyName[0] to the signing key name.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise which returns a new Signature object
 * with the SignatureInfo, or a promise rejected with InvalidSigningInfoError
 * when the requested signing method cannot be satisfied.
 */
KeyChain.prototype.prepareSignatureInfoPromise_ = function
  (params, keyName, useSync)
{
  var identity = null;
  var key = null;
  var thisKeyChain = this;

  return SyncPromise.resolve()
  .then(function() {
    if (params.getSignerType() == SigningInfo.SignerType.NULL) {
      return thisKeyChain.pib_.getDefaultIdentityPromise(useSync)
      .then(function(localIdentity) {
        identity = localIdentity;
        return SyncPromise.resolve(null);
      }, function(err) {
        // There is no default identity, so use sha256 for signing.
        keyName[0] = SigningInfo.getDigestSha256Identity();
        return SyncPromise.resolve(new DigestSha256Signature());
      });
    }
    else if (params.getSignerType() == SigningInfo.SignerType.ID) {
      identity = params.getPibIdentity();
      if (identity == null) {
        return thisKeyChain.pib_.getIdentityPromise(params.getSignerName(), useSync)
        .then(function(localIdentity) {
          identity = localIdentity;
          return SyncPromise.resolve(null);
        }, function(err) {
          return SyncPromise.reject(new InvalidSigningInfoError(new Error
            ("Signing identity `" + params.getSignerName().toUri() +
             "` does not exist")));
        });
      }
      else
        return SyncPromise.resolve(null);
    }
    else if (params.getSignerType() == SigningInfo.SignerType.KEY) {
      key = params.getPibKey();
      if (key == null) {
        identityName = PibKey.extractIdentityFromKeyName(
          params.getSignerName());

        return thisKeyChain.pib_.getIdentityPromise(identityName, useSync)
        .then(function(localIdentity) {
          return localIdentity.getKeyPromise(params.getSignerName(), useSync)
          .then(function(localKey) {
            key = localKey;
            // We will use the PIB key instance, so reset the identity.
            identity = null;
            return SyncPromise.resolve(null);
          });
        }, function(err) {
          return SyncPromise.reject(new InvalidSigningInfoError(new Error
            ("Signing key `" + params.getSignerName().toUri() +
             "` does not exist")));
        });
      }
      else
        return SyncPromise.resolve(null);
    }
    else if (params.getSignerType() == SigningInfo.SignerType.CERT) {
      var identityName = CertificateV2.extractIdentityFromCertName
        (params.getSignerName());

      return thisKeyChain.pib_.getIdentityPromise(identityName, useSync)
      .then(function(localIdentity) {
        identity = localIdentity;
        return identity.getKeyPromise
          (CertificateV2.extractKeyNameFromCertName(params.getSignerName()), useSync)
        .then(function(localKey) {
          key = localKey;
          return SyncPromise.resolve(null);
        });
      }, function(err) {
        return SyncPromise.reject(new InvalidSigningInfoError(new Error
          ("Signing certificate `" + params.getSignerName().toUri() +
           "` does not exist")));
      });
    }
    else if (params.getSignerType() == SigningInfo.SignerType.SHA256) {
      keyName[0] = SigningInfo.getDigestSha256Identity();
      return SyncPromise.resolve(new DigestSha256Signature());
    }
    else
      // We don't expect this to happen.
      return SyncPromise.reject(new InvalidSigningInfoError(new Error
        ("Unrecognized signer type")));
  })
  .then(function(signingInfo) {
    if (signingInfo != null)
      // We already have the result (a DigestSha256Signature).
      return SyncPromise.resolve(signingInfo);
    else {
      if (identity == null && key == null)
        return SyncPromise.reject(new InvalidSigningInfoError(new Error
          ("Cannot determine signing parameters")));

      return SyncPromise.resolve()
      .then(function() {
        if (identity != null && key == null) {
          return identity.getDefaultKeyPromise(useSync)
          .then(function(localKey) {
            key = localKey;
            return SyncPromise.resolve(null);
          }, function(err) {
            return SyncPromise.reject(new InvalidSigningInfoError(new Error
              ("Signing identity `" + identity.getName().toUri() +
               "` does not have default certificate")));
          });
        }
        else
          return SyncPromise.resolve();
      })
      .then(function() {
        if (key.getKeyType() == KeyType.RSA &&
            params.getDigestAlgorithm() == DigestAlgorithm.SHA256)
          signatureInfo = new Sha256WithRsaSignature();
        else if (key.getKeyType() == KeyType.EC &&
                 params.getDigestAlgorithm() == DigestAlgorithm.SHA256)
          signatureInfo = new Sha256WithEcdsaSignature()
        else
          return SyncPromise.reject(new KeyChain.Error(new Error
            ("Unsupported key type")));

        if (params.getValidityPeriod().hasPeriod() &&
            ValidityPeriod.canGetFromSignature(signatureInfo))
          // Set the ValidityPeriod from the SigningInfo params.
          ValidityPeriod.getFromSignature(signatureInfo).setPeriod
            (params.getValidityPeriod().getNotBefore(),
             params.getValidityPeriod().getNotAfter());

        var keyLocator = KeyLocator.getFromSignature(signatureInfo);
        keyLocator.setType(KeyLocatorType.KEYNAME);
        keyLocator.setKeyName(key.getName());

        keyName[0] = key.getName();
        return SyncPromise.resolve(signatureInfo);
      });
    }
  });
};

/**
 * Sign the byte buffer using the key with name keyName.
 * @param {Buffer} buffer The input byte buffer.
 * @param {Name} keyName The name of the key.
 * @param {number} digestAlgorithm The digest algorithm as an int from the
 * DigestAlgorithm enum.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise which returns the signature Blob (or
 * an isNull Blob if the key does not exist), or a promise rejected
 * with TpmBackEnd.Error for an error in signing.
 */
KeyChain.prototype.signBufferPromise_ = function
  (buffer, keyName, digestAlgorithm, useSync)
{
  if (keyName.equals(SigningInfo.getDigestSha256Identity())) {
    var hash = Crypto.createHash('sha256');
    hash.update(buffer);
    return SyncPromise.resolve(new Blob(hash.digest(), false));
  }

  return this.tpm_.signPromise(buffer, keyName, digestAlgorithm, useSync);
};

// Private security v1 methods

KeyChain.prototype.onCertificateData = function(interest, data, nextStep)
{
  // Try to verify the certificate (data) according to the parameters in nextStep.
  this.verifyData
    (data, nextStep.onVerified, nextStep.onValidationFailed, nextStep.stepCount);
};

KeyChain.prototype.onCertificateInterestTimeout = function
  (interest, retry, onValidationFailed, originalDataOrInterest, nextStep)
{
  if (retry > 0) {
    // Issue the same expressInterest as in verifyData except decrement retry.
    var thisKeyChain = this;
    this.face_.expressInterest
      (interest,
       function(callbackInterest, callbackData) {
         thisKeyChain.onCertificateData(callbackInterest, callbackData, nextStep);
       },
       function(callbackInterest) {
         thisKeyChain.onCertificateInterestTimeout
           (callbackInterest, retry - 1, onValidationFailed,
            originalDataOrInterest, nextStep);
       });
  }
  else {
    try {
      onValidationFailed
        (originalDataOrInterest, "The retry count is zero after timeout for fetching " +
          interest.getName().toUri());
    } catch (ex) {
      console.log("Error in onValidationFailed: " + NdnCommon.getErrorWithStackTrace(ex));
    }
  }
};

/**
 * Get the default certificate from the identity storage and return its name.
 * If there is no default identity or default certificate, then create one.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that returns the default certificate
 * name.
 */
KeyChain.prototype.prepareDefaultCertificateNamePromise_ = function(useSync)
{
  var signingCertificate;
  var thisKeyChain = this;
  return this.identityManager_.getDefaultCertificatePromise(useSync)
  .then(function(localCertificate) {
    signingCertificate = localCertificate;
    if (signingCertificate != null)
      return SyncPromise.resolve();

    // Set the default certificate and get the certificate again.
    return thisKeyChain.setDefaultCertificatePromise_(useSync)
    .then(function() {
      return thisKeyChain.identityManager_.getDefaultCertificatePromise(useSync);
    })
    .then(function(localCertificate) {
      signingCertificate = localCertificate;
      return SyncPromise.resolve();
    });
  })
  .then(function() {
    return SyncPromise.resolve(signingCertificate.getName());
  });
}

/**
 * Create the default certificate if it is not initialized. If there is no
 * default identity yet, creating a new tmp-identity.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If omitted or false, this may return a SyncPromise or
 * an async Promise.
 * @return {Promise|SyncPromise} A promise that resolves when the default
 * certificate is set.
 */
KeyChain.prototype.setDefaultCertificatePromise_ = function(useSync)
{
  var thisKeyChain = this;

  return this.identityManager_.getDefaultCertificatePromise(useSync)
  .then(function(certificate) {
    if (certificate != null)
      // We already have a default certificate.
      return SyncPromise.resolve();

    var defaultIdentity;
    return thisKeyChain.identityManager_.getDefaultIdentityPromise(useSync)
    .then(function(localDefaultIdentity) {
      defaultIdentity = localDefaultIdentity;
      return SyncPromise.resolve();
    }, function(ex) {
      // Create a default identity name.
      randomComponent = Crypto.randomBytes(4);
      defaultIdentity = new Name().append("tmp-identity")
        .append(new Blob(randomComponent, false));

      return SyncPromise.resolve();
    })
    .then(function() {
      return thisKeyChain.identityManager_.createIdentityAndCertificatePromise
        (defaultIdentity, KeyChain.getDefaultKeyParams(), useSync);
    })
    .then(function() {
      return thisKeyChain.identityManager_.setDefaultIdentityPromise
        (defaultIdentity, useSync);
    });
  });
};

KeyChain.defaultPibLocator_ = null // string
KeyChain.defaultTpmLocator_ = null // string
KeyChain.pibFactories_ = null // string => MakePibImpl
KeyChain.tpmFactories_ = null // string => MakeTpmBackEnd
KeyChain.defaultSigningInfo_ = new SigningInfo();
KeyChain.defaultKeyParams_ = new RsaKeyParams();

/**
 * Create an InvalidSigningInfoError which extends KeyChain.Error to indicate
 * that the supplied SigningInfo is invalid.
 * Call with: throw new InvalidSigningInfoError(new Error("message")).
 * @param {Error} error The exception created with new Error.
 * @constructor
 */
var InvalidSigningInfoError = function InvalidSigningInfoError(error)
{
  // Call the base constructor.
  KeyChain.Error.call(this, error);
}

InvalidSigningInfoError.prototype = new KeyChain.Error();
InvalidSigningInfoError.prototype.name = "InvalidSigningInfoError";

exports.InvalidSigningInfoError = InvalidSigningInfoError;

exports.InvalidSigningInfoError = InvalidSigningInfoError;

/**
 * Create a LocatorMismatchError which extends KeyChain.Error to indicate that
 * the supplied TPM locator does not match the locator stored in the PIB.
 * Call with: throw new LocatorMismatchError(new Error("message")).
 * @param {Error} error The exception created with new Error.
 * @constructor
 */
var LocatorMismatchError = function LocatorMismatchError(error)
{
  // Call the base constructor.
  KeyChain.Error.call(this, error);
}

LocatorMismatchError.prototype = new KeyChain.Error();
LocatorMismatchError.prototype.name = "LocatorMismatchError";

exports.LocatorMismatchError = LocatorMismatchError;

// Put these last to avoid a require loop.
/** @ignore */
var Pib = require('./pib/pib.js').Pib; /** @ignore */
var PibImpl = require('./pib/pib-impl.js').PibImpl; /** @ignore */
var PibKey = require('./pib/pib-key.js').PibKey; /** @ignore */
var PibSqlite3 = require('./pib/pib-sqlite3.js').PibSqlite3; /** @ignore */
var PibMemory = require('./pib/pib-memory.js').PibMemory;