Source: security/identity/identity-manager.js

/**
 * Copyright (C) 2014-2016 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.
 */

// Use capitalized Crypto to not clash with the browser's crypto.subtle.
/** @ignore */
var Crypto = require('../../crypto.js'); /** @ignore */
var Name = require('../../name.js').Name; /** @ignore */
var Data = require('../../data.js').Data; /** @ignore */
var Blob = require('../../util/blob.js').Blob; /** @ignore */
var DigestSha256Signature = require('../../digest-sha256-signature.js').DigestSha256Signature; /** @ignore */
var Sha256WithRsaSignature = require('../../sha256-with-rsa-signature.js').Sha256WithRsaSignature; /** @ignore */
var KeyLocatorType = require('../../key-locator.js').KeyLocatorType; /** @ignore */
var WireFormat = require('../../encoding/wire-format.js').WireFormat; /** @ignore */
var SecurityException = require('../security-exception.js').SecurityException; /** @ignore */
var DigestAlgorithm = require('../security-types.js').DigestAlgorithm; /** @ignore */
var KeyType = require('../security-types.js').KeyType; /** @ignore */
var RsaKeyParams = require('../key-params.js').RsaKeyParams; /** @ignore */
var IdentityCertificate = require('../certificate/identity-certificate.js').IdentityCertificate; /** @ignore */
var PublicKey = require('../certificate/public-key.js').PublicKey; /** @ignore */
var CertificateSubjectDescription = require('../certificate/certificate-subject-description.js').CertificateSubjectDescription; /** @ignore */
var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
var BasicIdentityStorage = require('./basic-identity-storage.js').BasicIdentityStorage; /** @ignore */
var FilePrivateKeyStorage = require('./file-private-key-storage.js').FilePrivateKeyStorage;

/**
 * An IdentityManager is the interface of operations related to identity, keys,
 * and certificates.
 *
 * Create a new IdentityManager to use the IdentityStorage and
 * PrivateKeyStorage.
 * @param {IdentityStorage} identityStorage An object of a subclass of
 * IdentityStorage. In Node.js, if this is omitted then use BasicIdentityStorage.
 * @param {PrivateKeyStorage} privateKeyStorage An object of a subclass of
 * PrivateKeyStorage. In Node.js, if this is omitted then use the default
 * PrivateKeyStorage for your system, which is FilePrivateKeyStorage for any
 * system other than OS X. (OS X key chain storage is not yet implemented, so
 * you must supply a different PrivateKeyStorage.)
 * @throws SecurityException if this is not in Node.js and identityStorage or
 * privateKeyStorage is omitted.
 * @constructor
 */
var IdentityManager = function IdentityManager
  (identityStorage, privateKeyStorage)
{
  if (!identityStorage) {
    if (!BasicIdentityStorage)
      // Assume we are in the browser.
      throw new SecurityException(new Error
        ("IdentityManager: If not in Node.js then you must supply an identityStorage."));

    identityStorage = new BasicIdentityStorage();
  }
  if (!privateKeyStorage) {
    if (!FilePrivateKeyStorage)
      // Assume we are in the browser.
      throw new SecurityException(new Error
        ("IdentityManager: If not in Node.js then you must supply a privateKeyStorage."));

    // Assume we are in Node.js, so check the system.
    if (process.platform === "darwin")
      throw new SecurityException(new Error
        ("IdentityManager: OS X key chain storage is not yet implemented. You must supply a privateKeyStorage."));
    else
      privateKeyStorage = new FilePrivateKeyStorage();
  }

  this.identityStorage = identityStorage;
  this.privateKeyStorage = privateKeyStorage;
};

exports.IdentityManager = IdentityManager;

/**
 * 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.
 * @params {KeyParams} params The key parameters if a key needs to be generated
 * for the 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 which returns the name of the default
 * certificate of the identity.
 */
IdentityManager.prototype.createIdentityAndCertificatePromise = function
  (identityName, params, useSync)
{
  var thisManager = this;
  var generateKey = true;
  var keyName = null;

  return this.identityStorage.addIdentityPromise(identityName, useSync)
  .then(function() {
    return thisManager.identityStorage.getDefaultKeyNameForIdentityPromise
      (identityName, useSync)
    .then(function(localKeyName) {
      keyName = localKeyName;

      // Set generateKey.
      return thisManager.identityStorage.getKeyPromise(keyName, useSync)
      .then(function(publicKeyDer) {
        var key = new PublicKey(publicKeyDer);
        if (key.getKeyType() == params.getKeyType())
          // The key exists and has the same type, so don't need to generate one.
          generateKey = false;
        return SyncPromise.resolve();
      });
    }, function(err) {
      if (!(err instanceof SecurityException))
        throw err;

      // The key doesn't exist, so leave generateKey true.
      return SyncPromise.resolve();
    });
  })
  .then(function() {
    if (generateKey)
      return thisManager.generateKeyPairPromise(identityName, true, params, useSync)
      .then(function(localKeyName) {
        keyName = localKeyName;
        return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
          (keyName, useSync);
      });
    else
      // Don't generate a key pair. Use the existing keyName.
      return SyncPromise.resolve();
  })
  .then(function() {
    return thisManager.identityStorage.getDefaultCertificateNameForKeyPromise
      (keyName, useSync)
    .then(function(certName) {
      // The cert exists, so don't need to make it.
      return SyncPromise.resolve(certName);
    }, function(err) {
      if (!(err instanceof SecurityException))
        throw err;

      // The cert doesn't exist, so make one.
      var certName;
      return thisManager.selfSignPromise(keyName, useSync)
      .then(function(selfCert) {
        certName = selfCert.getName();
        return thisManager.addCertificateAsIdentityDefaultPromise(selfCert, useSync);
      })
      .then(function() {
        return SyncPromise.resolve(certName);
      });
    });
  });
};

/**
 * 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.
 * @params {KeyParams} params The key parameters if a key needs to be generated
 * for the identity.
 * @param {function} onComplete (optional) This calls onComplete(certificateName)
 * with the 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.)
 * @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.)
 * @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.
 */
IdentityManager.prototype.createIdentityAndCertificate = function
  (identityName, params, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.createIdentityAndCertificatePromise(identityName, params, !onComplete));
};

/**
 * 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.
 * @params {KeyParams} params The key parameters if a key needs to be generated
 * for the identity.
 * @return {Name} The key name of the auto-generated KSK of the identity.
 */
IdentityManager.prototype.createIdentity = function(identityName, params)
{
  return IdentityCertificate.certificateNameToPublicKeyName
    (this.createIdentityAndCertificate(identityName, params));
};

/**
 * Delete the identity from the public and private key storage. If the
 * identity to be deleted is the current default system default, this will not
 * delete the identity and will return immediately.
 * @param {Name} identityName The name of the identity.
 * @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.)
 * @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.)
 */
IdentityManager.prototype.deleteIdentity = function
  (identityName, onComplete, onError)
{
  var useSync = !onComplete;
  var thisManager = this;

  var doDelete = true;

  var mainPromise = this.identityStorage.getDefaultIdentityPromise(useSync)
  .then(function(defaultIdentityName) {
    if (defaultIdentityName.equals(identityName))
      // Don't delete the default identity!
      doDelete = false;

    return SyncPromise.resolve();
  }, function(err) {
    // There is no default identity to check.
    return SyncPromise.resolve();
  })
  .then(function() {
    if (!doDelete)
      return SyncPromise.resolve();

    var keysToDelete = [];
    return thisManager.identityStorage.getAllKeyNamesOfIdentityPromise
      (identityName, keysToDelete, true)
    .then(function() {
      return thisManager.identityStorage.getAllKeyNamesOfIdentityPromise
        (identityName, keysToDelete, false);
    })
    .then(function() {
      return thisManager.identityStorage.deleteIdentityInfoPromise(identityName);
    })
    .then(function() {
      // Recursively loop through keysToDelete, calling deleteKeyPairPromise.
      function deleteKeyLoop(i) {
        if (i >= keysToDelete.length)
          return SyncPromise.resolve();

        return thisManager.privateKeyStorage.deleteKeyPairPromise(keysToDelete[i])
        .then(function() {
          return deleteKeyLoop(i + 1);
        });
      }

      return deleteKeyLoop(0);
    });
  });

  return SyncPromise.complete(onComplete, onError, mainPromise);
};

/**
 * Set the default identity.  If the identityName does not exist, then clear the
 * default identity so that getDefaultIdentity() throws an exception.
 * @param {Name} identityName The default identity 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 fulfills when the default
 * identity is set.
 */
IdentityManager.prototype.setDefaultIdentityPromise = function
  (identityName, useSync)
{
  return this.identityStorage.setDefaultIdentityPromise(identityName, useSync);
};

/**
 * Set the default identity.  If the identityName does not exist, then clear the
 * default identity so that getDefaultIdentity() throws an exception.
 * @param {Name} identityName The default identity name.
 * @param {function} onComplete (optional) This calls onComplete() when complete.
 * (Some database libraries only use a callback, so onComplete is required to
 * use these.)
 * @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.)
 */
IdentityManager.prototype.setDefaultIdentity = function
  (identityName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.setDefaultIdentityPromise(identityName, !onComplete));
};

/**
 * Get the default 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 which returns the Name of default
 * identity, or a promise rejected with SecurityException if the default
 * identity is not set.
 */
IdentityManager.prototype.getDefaultIdentityPromise = function(useSync)
{
  return this.identityStorage.getDefaultIdentityPromise(useSync);
};

/**
 * 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 database libraries only use a callback, so onComplete is required
 * to use these.)
 * @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.)
 * @returns {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.
 * @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).
 */
IdentityManager.prototype.getDefaultIdentity = function(onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getDefaultIdentityPromise(!onComplete));
};

/**
 * Get the certificate of the default 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 which returns the requested
 * IdentityCertificate or null if not found.
 */
IdentityManager.prototype.getDefaultCertificatePromise = function(useSync)
{
  return this.identityStorage.getDefaultCertificatePromise(useSync);
};

/**
 * Generate a pair of RSA keys for the specified identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
 * a Data-Signing-Key (DSK).
 * @param {number} keySize The size of the key.
 * @return {Name} The generated key name.
 */
IdentityManager.prototype.generateRSAKeyPair = function
  (identityName, isKsk, keySize)
{
  // For now, require sync. This method may be removed from the API.
  return SyncPromise.getValue
    (this.generateKeyPairPromise
     (identityName, isKsk, new RsaKeyParams(keySize), true));
};

/**
 * 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.)
 * @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.)
 */
IdentityManager.prototype.setDefaultKeyForIdentity = function
  (keyName, identityNameCheck, onComplete, onError)
{
  onError = (typeof identityNameCheck === "function") ? onComplete : onError;
  onComplete = (typeof identityNameCheck === "function") ?
    identityNameCheck : onComplete;
  identityNameCheck = (typeof identityNameCheck === "function" || !identityNameCheck) ?
    new Name() : identityNameCheck;

  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.setDefaultKeyNameForIdentityPromise
      (keyName, identityNameCheck, !onComplete));
};

/**
 * Get the default key for an identity.
 * @param {Name} identityName The name of the identity.
 * @param {function} onComplete (optional) This calls onComplete(keyName)
 * with name of the default key. If omitted, the return value is described
 * below. (Some database libraries only use a callback, so onComplete is required
 * to use these.)
 * @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 default key name.
 * Otherwise, if onComplete is supplied then return undefined and use onComplete
 * as described above.
 * @throws SecurityException if the default key name for the identity is not set.
 * However, if onComplete and onError are defined, then if there is an exception
 * return undefined and call onError(exception).
 */
IdentityManager.prototype.getDefaultKeyNameForIdentity = function
  (identityName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getDefaultKeyNameForIdentityPromise
      (identityName, !onComplete));
};

/**
 * Generate a pair of RSA keys for the specified identity and set it as default
 * key for the identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
 * a Data-Signing-Key (DSK).
 * @param {number} keySize The size of the key.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which returns the generated key name.
 */
IdentityManager.prototype.generateRSAKeyPairAsDefaultPromise = function
  (identityName, isKsk, keySize, useSync)
{
  var newKeyName;
  var thisManager = this;
  return this.generateKeyPairPromise(identityName, isKsk, new RsaKeyParams(keySize))
  .then(function(localKeyName) {
    newKeyName = localKeyName;

    return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
      (newKeyName);
  })
  .then(function() {
    return SyncPromise.resolve(newKeyName);
  });
};

/**
 * Generate a pair of RSA keys for the specified identity and set it as default
 * key for the identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk True for generating a Key-Signing-Key (KSK), false for
 * a Data-Signing-Key (DSK).
 * @param {number} keySize The size of the key.
 * @return {Name} The generated key name.
 */
IdentityManager.prototype.generateRSAKeyPairAsDefault = function
  (identityName, isKsk, keySize)
{
  return SyncPromise.getValue
    (this.generateRSAKeyPairAsDefaultPromise(identityName, isKsk, keySize, true));
};

/**
 * Get the public key with the specified name.
 * @param {Name} keyName The name of the key.
 * @param {function} onComplete (optional) This calls onComplete(publicKey)
 * with PublicKey. If omitted, the return value is described below. (Some database
 * libraries only use a callback, so onComplete is required to use these.)
 * @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 {PublicKey} If onComplete is omitted, return the public key.
 * Otherwise, if onComplete is supplied then return undefined and use onComplete
 * as described above.
 */
IdentityManager.prototype.getPublicKey = function(keyName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getKeyPromise(keyName, !onComplete)
    .then(function(keyDer) {
      return SyncPromise.resolve(new PublicKey(keyDer));
    }));
};

// TODO: Add two versions of createIdentityCertificate.

/**
 * Prepare an unsigned identity certificate.
 * @param {Name} keyName The key name, e.g., `/{identity_name}/ksk-123456`.
 * @param {PublicKey} publicKey (optional) The public key to sign. If ommited,
 * use the keyName to get the public key from the identity storage.
 * @param {Name} signingIdentity The signing identity.
 * @param {number} notBefore See IdentityCertificate.
 * @param {number} notAfter See IdentityCertificate.
 * @param {Array<CertificateSubjectDescription>} subjectDescription A list of
 * CertificateSubjectDescription. See IdentityCertificate. If null or empty,
 * this adds a an ATTRIBUTE_NAME based on the keyName.
 * @param {Name} certPrefix (optional) The prefix before the `KEY` component. If
 * null or omitted, this infers the certificate name according to the relation
 * between the signingIdentity and the subject identity. If the signingIdentity
 * is a prefix of the subject identity, `KEY` will be inserted after the
 * signingIdentity, otherwise `KEY` is inserted after subject identity (i.e.,
 * before `ksk-...`).
 * @param {function} onComplete (optional) This calls onComplete(certificate)
 * with the unsigned IdentityCertificate, or null if the inputs are invalid. If
 * omitted, the return value is described below. (Some database libraries only
 * use a callback, so onComplete is required to use these.)
 * @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 {IdentityCertificate} If onComplete is omitted, return the the
 * unsigned IdentityCertificate, or null if the inputs are invalid. Otherwise,
 * if onComplete is supplied then return undefined and use onComplete as
 * described above.
 */
IdentityManager.prototype.prepareUnsignedIdentityCertificate = function
  (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
   certPrefix, onComplete, onError)
{
  if (!(publicKey instanceof PublicKey)) {
    // The publicKey was omitted. Shift arguments.
    onError = onComplete;
    onComplete = certPrefix;
    certPrefix = subjectDescription;
    subjectDescription = notAfter;
    notAfter = notBefore;
    notBefore = signingIdentity;
    signingIdentity = publicKey;
    publicKey = null;
  }

  // certPrefix may be omitted or null, so check for it and the following args.
  var arg7 = certPrefix;
  var arg8 = onComplete;
  var arg9 = onError;
  if (arg7 instanceof Name)
    certPrefix = arg7;
  else
    certPrefix = null;

  if (typeof arg7 === 'function') {
    onComplete = arg7;
    onError = arg8;
  }
  else if (typeof arg8 === 'function') {
    onComplete = arg8;
    onError = arg9;
  }
  else {
    onComplete = null;
    onError = null;
  }

  var promise;
  if (publicKey == null)
    promise =  this.prepareUnsignedIdentityCertificatePromise
      (keyName, signingIdentity, notBefore, notAfter, subjectDescription,
       certPrefix, !onComplete);
  else
    promise =  this.prepareUnsignedIdentityCertificatePromise
      (keyName, publicKey, signingIdentity, notBefore, notAfter,
       subjectDescription, certPrefix, !onComplete);
  return SyncPromise.complete(onComplete, onError, promise);
};

/**
 * Prepare an unsigned identity certificate.
 * @param {Name} keyName The key name, e.g., `/{identity_name}/ksk-123456`.
 * @param {PublicKey} publicKey (optional) The public key to sign. If ommited,
 * use the keyName to get the public key from the identity storage.
 * @param {Name} signingIdentity The signing identity.
 * @param {number} notBefore See IdentityCertificate.
 * @param {number} notAfter See IdentityCertificate.
 * @param {Array<CertificateSubjectDescription>} subjectDescription A list of
 * CertificateSubjectDescription. See IdentityCertificate. If null or empty,
 * this adds a an ATTRIBUTE_NAME based on the keyName.
 * @param {Name} certPrefix (optional) The prefix before the `KEY` component. If
 * null or omitted, this infers the certificate name according to the relation
 * between the signingIdentity and the subject identity. If the signingIdentity
 * is a prefix of the subject identity, `KEY` will be inserted after the
 * signingIdentity, otherwise `KEY` is inserted after subject identity (i.e.,
 * before `ksk-...`).
 * @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 unsigned
 * IdentityCertificate, or that returns null if the inputs are invalid.
 */
IdentityManager.prototype.prepareUnsignedIdentityCertificatePromise = function
  (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
   certPrefix, useSync)
{
  if (!(publicKey instanceof PublicKey)) {
    // The publicKey was omitted. Shift arguments.
    useSync = certPrefix;
    certPrefix = subjectDescription;
    subjectDescription = notAfter;
    notAfter = notBefore;
    notBefore = signingIdentity;
    signingIdentity = publicKey;
    publicKey = null;
  }

  // certPrefix may be omitted or null, so check for it and the following arg.
  var arg7 = certPrefix;
  var arg8 = useSync;
  if (arg7 instanceof Name)
    certPrefix = arg7;
  else
    certPrefix = null;

  if (typeof arg7 === 'boolean')
    useSync = arg7;
  else if (typeof arg8 === 'boolean')
    useSync = arg8;
  else
    useSync = false;

  var promise;
  if (publicKey == null) {
    promise = this.identityStorage.getKeyPromise(keyName, useSync)
    .then(function(keyDer) {
      publicKey = new PublicKey(keyDer);
      return SyncPromise.resolve();
    });
  }
  else
    promise = SyncPromise.resolve();

  return promise
  .then(function() {
    return SyncPromise.resolve
      (IdentityManager.prepareUnsignedIdentityCertificateHelper_
       (keyName, publicKey, signingIdentity, notBefore, notAfter,
        subjectDescription, certPrefix));
  });
};

/**
 * A helper for prepareUnsignedIdentityCertificatePromise where the publicKey
 * is known.
 */
IdentityManager.prepareUnsignedIdentityCertificateHelper_ = function
  (keyName, publicKey, signingIdentity, notBefore, notAfter, subjectDescription,
   certPrefix)
{
  if (keyName.size() < 1)
    return null;

  var tempKeyIdPrefix = keyName.get(-1).toEscapedString();
  if (tempKeyIdPrefix.length < 4)
    return null;
  keyIdPrefix = tempKeyIdPrefix.substr(0, 4);
  if (keyIdPrefix != "ksk-" && keyIdPrefix != "dsk-")
    return null;

  var certificate = new IdentityCertificate();
  var certName = new Name();

  if (certPrefix == null) {
    // No certificate prefix hint, so infer the prefix.
    if (signingIdentity.match(keyName))
      certName.append(signingIdentity)
        .append("KEY")
        .append(keyName.getSubName(signingIdentity.size()))
        .append("ID-CERT")
        .appendVersion(new Date().getTime());
    else
      certName.append(keyName.getPrefix(-1))
        .append("KEY")
        .append(keyName.get(-1))
        .append("ID-CERT")
        .appendVersion(new Date().getTime());
  }
  else {
    // A cert prefix hint is supplied, so determine the cert name.
    if (certPrefix.match(keyName) && !certPrefix.equals(keyName))
      certName.append(certPrefix)
        .append("KEY")
        .append(keyName.getSubName(certPrefix.size()))
        .append("ID-CERT")
        .appendVersion(new Date().getTime());
    else
      return null;
  }

  certificate.setName(certName);
  certificate.setNotBefore(notBefore);
  certificate.setNotAfter(notAfter);
  certificate.setPublicKeyInfo(publicKey);

  if (subjectDescription == null || subjectDescription.length === 0)
    certificate.addSubjectDescription(new CertificateSubjectDescription
      ("2.5.4.41", keyName.getPrefix(-1).toUri()));
  else {
    for (var i = 0; i < subjectDescription.length; ++i)
      certificate.addSubjectDescription(subjectDescription[i]);
  }

  try {
    certificate.encode();
  } catch (ex) {
    throw SecurityException(new Error("DerEncodingException: " + ex));
  }

  return certificate;
};

/**
 * Add a certificate into the public key identity storage.
 * @param {IdentityCertificate} certificate The certificate to to added. This
 * makes a copy of 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.)
 * @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.)
 */
IdentityManager.prototype.addCertificate = function
  (certificate, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.addCertificatePromise(certificate, !onComplete));
};

/**
 * Set the certificate as the default for its corresponding key.
 * @param {IdentityCertificate} certificate The certificate.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which fulfills when the default
 * certificate is set.
 */
IdentityManager.prototype.setDefaultCertificateForKeyPromise = function
  (certificate, useSync)
{
  var thisManager = this;

  var keyName = certificate.getPublicKeyName();
  return this.identityStorage.doesKeyExistPromise(keyName, useSync)
  .then(function(exists) {
    if (!exists)
      throw new SecurityException(new Error
        ("No corresponding Key record for certificate!"));

    return thisManager.identityStorage.setDefaultCertificateNameForKeyPromise
      (keyName, certificate.getName(), useSync);
  });
};

/**
 * 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.)
 * @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.)
 */
IdentityManager.prototype.setDefaultCertificateForKey = function
  (certificate, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.setDefaultCertificateForKeyPromise(certificate, !onComplete));
};

/**
 * Add a certificate into the public key identity storage and set the
 * certificate as the default for its corresponding identity.
 * @param {IdentityCertificate} certificate The certificate to be added. This
 * makes a copy of the certificate.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which fulfills when the certificate
 * is added.
 */
IdentityManager.prototype.addCertificateAsIdentityDefaultPromise = function
  (certificate, useSync)
{
  var thisManager = this;
  return this.identityStorage.addCertificatePromise(certificate, useSync)
  .then(function() {
    var keyName = certificate.getPublicKeyName();
    return thisManager.identityStorage.setDefaultKeyNameForIdentityPromise
      (keyName, useSync);
  })
  .then(function() {
    return thisManager.setDefaultCertificateForKeyPromise(certificate, useSync);
  });
};

/**
 * Add a certificate into the public key identity storage and set the
 * certificate as the default of its corresponding key.
 * @param {IdentityCertificate} certificate The certificate to be added. This
 * makes a copy of 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.)
 * @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.)
 */
IdentityManager.prototype.addCertificateAsDefault = function
  (certificate, onComplete, onError)
{
  var useSync = !onComplete;
  var thisManager = this;

  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.addCertificatePromise(certificate, useSync)
    .then(function() {
      return thisManager.setDefaultCertificateForKeyPromise(certificate, useSync);
    }));
};

/**
 * 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 database libraries only use a callback, so onComplete
 * is required to use these.)
 * @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 {IdentityCertificate} If onComplete is omitted, return the requested
 * certificate. Otherwise, if onComplete is supplied then return undefined and
 * use onComplete as described above.
 */
IdentityManager.prototype.getCertificate = function
  (certificateName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getCertificatePromise
      (certificateName, false, !onComplete));
};

/**
 * Get the default certificate name for the specified identity.
 * @param {Name} identityName The identity 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 the default certificate
 * Name, or a promise rejected with SecurityException if the default key name
 * for the identity is not set or the default certificate name for the key name
 * is not set.
 */
IdentityManager.prototype.getDefaultCertificateNameForIdentityPromise = function
  (identityName, useSync)
{
  return this.identityStorage.getDefaultCertificateNameForIdentityPromise
    (identityName, useSync);
}

/**
 * Get the default certificate name for the specified identity, which will be
 * used when signing is performed based on identity.
 * @param {Name} identityName The name of the specified identity.
 * @param {function} onComplete (optional) This calls onComplete(certificateName)
 * with name of the default certificate. If omitted, the return value is described
 * below. (Some database libraries only use a callback, so onComplete is required
 * to use these.)
 * @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 default certificate name.
 * Otherwise, if onComplete is supplied then return undefined and use
 * onComplete as described above.
 * @throws SecurityException if 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).
 */
IdentityManager.prototype.getDefaultCertificateNameForIdentity = function
  (identityName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getDefaultCertificateNameForIdentityPromise
      (identityName, !onComplete));
};

/**
 * 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 database libraries only use a callback, so onComplete is required
 * to use these.)
 * @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 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).
 */
IdentityManager.prototype.getDefaultCertificateName = function
  (onComplete, onError)
{
  var useSync = !onComplete;
  var thisManager = this;

  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getDefaultIdentityPromise(useSync)
    .then(function(identityName) {
      return thisManager.identityStorage.getDefaultCertificateNameForIdentityPromise
        (identityName, useSync);
    }));
};

/**
 * Append all the identity names to the nameList.
 * @param {Array<Name>} nameList Append result names to nameList.
 * @param {boolean} isDefault If true, add only the default identity name. If
 * false, add only the non-default identity names.
 * @param {function} onComplete (optional) This calls onComplete() when finished
 * adding to nameList. If omitted, this returns when complete. (Some database
 * libraries only use a callback, so onComplete is required to use these.)
 * @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 {void} If onComplete is omitted, return when complete. Otherwise, if
 * onComplete is supplied then return undefined and use onComplete as described
 * above.
 */
IdentityManager.prototype.getAllIdentities = function
  (nameList, isDefault, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getAllIdentitiesPromise
      (nameList, isDefault, !onComplete));
};

/**
 * Append all the key names of a particular identity to the nameList.
 * @param {Name} identityName The identity name to search for.
 * @param {Array<Name>} nameList Append result names to nameList.
 * @param {boolean} isDefault If true, add only the default key name. If false,
 * add only the non-default key names.
 * @param {function} onComplete (optional) This calls onComplete() when finished
 * adding to nameList. If omitted, this returns when complete. (Some database
 * libraries only use a callback, so onComplete is required to use these.)
 * @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 {void} If onComplete is omitted, return when complete. Otherwise, if
 * onComplete is supplied then return undefined and use onComplete as described
 * above.
 */
IdentityManager.prototype.getAllKeyNamesOfIdentity = function
  (identityName, nameList, isDefault, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getAllKeyNamesOfIdentityPromise
      (identityName, nameList, isDefault, !onComplete));
};

/**
 * Append all the certificate names of a particular key name to the nameList.
 * @param {Name} keyName The key name to search for.
 * @param {Array<Name>} nameList Append result names to nameList.
 * @param {boolean} isDefault If true, add only the default certificate name. If
 * false, add only the non-default certificate names.
 * @param {function} onComplete (optional) This calls onComplete() when finished
 * adding to nameList. If omitted, this returns when complete. (Some database
 * libraries only use a callback, so onComplete is required to use these.)
 * @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 {void} If onComplete is omitted, return when complete. Otherwise, if
 * onComplete is supplied then return undefined and use onComplete as described
 * above.
 */
IdentityManager.prototype.getAllCertificateNamesOfKey = function
  (keyName, nameList, isDefault, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.identityStorage.getAllCertificateNamesOfKeyPromise
      (keyName, nameList, isDefault, !onComplete));
};

/**
 * Sign the Data packet or byte array data based on the certificate name.
 * @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 a
 * Buffer, sign it to produce a Signature object.
 * @param {Name} certificateName The Name identifying the certificate which
 * identifies the signing key.
 * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
 * WireFormat.getDefaultWireFormat() if omitted.
 * @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 generated Signature
 * object (if target is a Buffer) or the target (if target is Data).
 */
IdentityManager.prototype.signByCertificatePromise = function
  (target, certificateName, wireFormat, useSync)
{
  useSync = (typeof wireFormat === "boolean") ? wireFormat : useSync;
  wireFormat = (typeof wireFormat === "boolean" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;

  var keyName = IdentityManager.certificateNameToPublicKeyName(certificateName);

  var thisManager = this;
  if (target instanceof Data) {
    var data = target;
    var digestAlgorithm = [0];

    return this.makeSignatureByCertificatePromise
      (certificateName, digestAlgorithm, useSync)
    .then(function(signature) {
      data.setSignature(signature);
      // Encode once to get the signed portion.
      var encoding = data.wireEncode(wireFormat);

      return thisManager.privateKeyStorage.signPromise
        (encoding.signedBuf(), keyName, digestAlgorithm[0], useSync);
    })
    .then(function(signatureValue) {
      data.getSignature().setSignature(signatureValue);
      // Encode again to include the signature.
      data.wireEncode(wireFormat);

      return SyncPromise.resolve(data);
    });
  }
  else {
    var digestAlgorithm = [0];
    return this.makeSignatureByCertificatePromise
      (certificateName, digestAlgorithm, useSync)
    .then(function(signature) {
      return thisManager.privateKeyStorage.signPromise
        (target, keyName, digestAlgorithm[0], useSync);
    })
    .then(function (signatureValue) {
      signature.setSignature(signatureValue);
      return SyncPromise.resolve(signature);
    });
  }
};

/**
 * Sign the Data packet or byte array data based on the certificate name.
 * @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 a
 * Buffer, sign it to produce a Signature object.
 * @param {Name} certificateName The Name identifying the certificate which
 * identifies the signing key.
 * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
 * WireFormat.getDefaultWireFormat() if omitted.
 * @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.)
 * @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.)
 * @return {Signature} If onComplete is omitted, return the generated Signature
 * object (if target is a Buffer) or the target (if target is Data). Otherwise,
 * if onComplete is supplied then return undefined and use onComplete as described
 * above.
 */
IdentityManager.prototype.signByCertificate = function
  (target, certificateName, wireFormat, onComplete, onError)
{
  onError = (typeof wireFormat === "function") ? onComplete : onError;
  onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
  wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;

  return SyncPromise.complete(onComplete, onError,
    this.signByCertificatePromise
      (target, certificateName, wireFormat, !onComplete));
};

/**
 * Append a SignatureInfo to the Interest name, sign the name components and
 * append a final name component with the signature bits.
 * @param {Interest} interest The Interest object to be signed. This appends
 * name components of SignatureInfo and the signature bits.
 * @param {Name} certificateName The certificate name of the key to use for
 * signing.
 * @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 supplied Interest.
 */
IdentityManager.prototype.signInterestByCertificatePromise = function
  (interest, certificateName, wireFormat, useSync)
{
  useSync = (typeof wireFormat === "boolean") ? wireFormat : useSync;
  wireFormat = (typeof wireFormat === "boolean" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;

  var thisManager = this;
  var signature;
  var digestAlgorithm = [0];
  return this.makeSignatureByCertificatePromise
      (certificateName, digestAlgorithm, useSync)
  .then(function(localSignature) {
    signature = localSignature;
    // 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 keyName = IdentityManager.certificateNameToPublicKeyName
      (certificateName);

    return thisManager.privateKeyStorage.signPromise
      (encoding.signedBuf(), keyName, digestAlgorithm[0], useSync);
  })
  .then(function(signatureValue) {
    signature.setSignature(signatureValue);

    // Remove the empty signature and append the real one.
    interest.setName(interest.getName().getPrefix(-1).append
      (wireFormat.encodeSignatureValue(signature)));
    return SyncPromise.resolve(interest);
  });
};

/**
 * Append a SignatureInfo to the Interest name, sign the name components and
 * append a final name component with the signature bits.
 * @param {Interest} interest The Interest object to be signed. This appends
 * name components of SignatureInfo and the signature bits.
 * @param {Name} certificateName The certificate name of the key to use for
 * signing.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 * @param {function} onComplete (optional) This calls onComplete(interest) with
 * the supplied Interest object which has been modified to set its signature. If
 * omitted, then return when the interest has been signed. (Some crypto
 * libraries only use a callback, so onComplete is required to use these.)
 * @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.)
 * @return {Signature} If onComplete is omitted, return the interest. Otherwise,
 * if onComplete is supplied then return undefined and use onComplete as
 * described above.
 */
IdentityManager.prototype.signInterestByCertificate = function
  (interest, certificateName, wireFormat, onComplete, onError)
{
  onError = (typeof wireFormat === "function") ? onComplete : onError;
  onComplete = (typeof wireFormat === "function") ? wireFormat : onComplete;
  wireFormat = (typeof wireFormat === "function" || !wireFormat) ? WireFormat.getDefaultWireFormat() : wireFormat;

  return SyncPromise.complete(onComplete, onError,
    this.signInterestByCertificatePromise
      (interest, certificateName, wireFormat, !onComplete));
};

/**
 * Wire encode the Data object, digest it and set its SignatureInfo to a
 * DigestSha256.
 * @param {Data} data The Data object to be signed. This updates its signature
 * and wireEncoding.
 * @param {WireFormat} (optional) The WireFormat for calling encodeData, or
 * WireFormat.getDefaultWireFormat() if omitted.
 */
IdentityManager.prototype.signWithSha256 = function(data, wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

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

  // Digest and set the signature.
  var hash = Crypto.createHash('sha256');
  hash.update(encoding.signedBuf());
  data.getSignature().setSignature(new Blob(hash.digest(), false));

  // Encode again to include the signature.
  data.wireEncode(wireFormat);
};

/**
 * Append a SignatureInfo for DigestSha256 to the Interest name, digest the
   * name components and append a final name component with the signature bits
   * (which is the digest).
 * @param {Interest} interest The Interest object to be signed. This appends
 * name components of SignatureInfo and the signature bits.
 * @param {WireFormat} wireFormat (optional) A WireFormat object used to encode
 * the input. If omitted, use WireFormat getDefaultWireFormat().
 */
IdentityManager.prototype.signInterestWithSha256 = function(interest, wireFormat)
{
  wireFormat = (wireFormat || WireFormat.getDefaultWireFormat());

  var signature = new DigestSha256Signature();

  // 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);

  // Digest and set the signature.
  var hash = Crypto.createHash('sha256');
  hash.update(encoding.signedBuf());
  signature.setSignature(new Blob(hash.digest(), false));

  // Remove the empty signature and append the real one.
  interest.setName(interest.getName().getPrefix(-1).append
    (wireFormat.encodeSignatureValue(signature)));
};

/**
 * Generate a self-signed certificate for a public key.
 * @param {Name} keyName The name of the public key.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which returns the generated
 * IdentityCertificate.
 */
IdentityManager.prototype.selfSignPromise = function(keyName, useSync)
{
  var certificate = new IdentityCertificate();

  var thisManager = this;
  return this.identityStorage.getKeyPromise(keyName, useSync)
  .then(function(keyBlob) {
    var publicKey = new PublicKey(keyBlob);

    var notBefore = new Date().getTime();
    var notAfter = notBefore + 2 * 365 * 24 * 3600 * 1000; // about 2 years

    certificate.setNotBefore(notBefore);
    certificate.setNotAfter(notAfter);

    var certificateName = keyName.getPrefix(-1).append("KEY").append
      (keyName.get(-1)).append("ID-CERT").appendVersion(certificate.getNotBefore());
    certificate.setName(certificateName);

    certificate.setPublicKeyInfo(publicKey);
    certificate.addSubjectDescription(new CertificateSubjectDescription
      ("2.5.4.41", keyName.toUri()));
    certificate.encode();

    return thisManager.signByCertificatePromise
      (certificate, certificate.getName(), useSync);
  })
};

/**
 * Generate a self-signed certificate for a public key.
 * @param {Name} keyName The name of the public key.
 * @param {function} onComplete (optional) This calls onComplete(certificate)
 * with the the generated IdentityCertificate. If omitted, the return value is
 * described below. (Some crypto libraries only use a callback, so onComplete is
 * required to use these.)
 * @return {IdentityCertificate} If onComplete is omitted, return the
 * generated certificate. Otherwise, if onComplete is supplied then return
 * undefined and use onComplete as described above.
 * @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.)
 */
IdentityManager.prototype.selfSign = function(keyName, onComplete, onError)
{
  return SyncPromise.complete(onComplete, onError,
    this.selfSignPromise(keyName, !onComplete));
};

/**
 * Get the public key name from the full certificate name.
 *
 * @param {Name} certificateName The full certificate name.
 * @return {Name} The related public key name.
 * TODO: Move this to IdentityCertificate
 */
IdentityManager.certificateNameToPublicKeyName = function(certificateName)
{
  var i = certificateName.size() - 1;
  var idString = "ID-CERT";
  while (i >= 0) {
    if (certificateName.get(i).toEscapedString() == idString)
      break;
    --i;
  }

  var tmpName = certificateName.getSubName(0, i);
  var keyString = "KEY";
  i = 0;
  while (i < tmpName.size()) {
    if (tmpName.get(i).toEscapedString() == keyString)
      break;
    ++i;
  }

  return tmpName.getSubName(0, i).append(tmpName.getSubName
    (i + 1, tmpName.size() - i - 1));
};

/**
 * Return a new Signature object based on the signature algorithm of the public
 * key with keyName (derived from certificateName).
 * @param {Name} certificateName The certificate name.
 * @param {Array} digestAlgorithm Set digestAlgorithm[0] to the signature
 * algorithm's digest algorithm, e.g. DigestAlgorithm.SHA256.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which returns a new object of the
 * correct subclass of Signature.
 */
IdentityManager.prototype.makeSignatureByCertificatePromise = function
  (certificateName, digestAlgorithm, useSync)
{
  var keyName = IdentityManager.certificateNameToPublicKeyName(certificateName);
  return this.privateKeyStorage.getPublicKeyPromise(keyName, useSync)
  .then(function(publicKey) {
    var keyType = publicKey.getKeyType();

    var signature = null;
    if (keyType == KeyType.RSA) {
      signature = new Sha256WithRsaSignature();
      digestAlgorithm[0] = DigestAlgorithm.SHA256;

      signature.getKeyLocator().setType(KeyLocatorType.KEYNAME);
      signature.getKeyLocator().setKeyName(certificateName.getPrefix(-1));
    }
    else
      throw new SecurityException(new Error("Key type is not recognized"));

    return SyncPromise.resolve(signature);
  });
};

/**
 * A private method to generate a pair of keys for the specified identity.
 * @param {Name} identityName The name of the identity.
 * @param {boolean} isKsk true for generating a Key-Signing-Key (KSK), false for
 * a Data-Signing-Key (DSK).
 * @param {KeyParams} params The parameters of the key.
 * @param {boolean} useSync (optional) If true then return a SyncPromise which
 * is already fulfilled. If false, this may return a SyncPromise or an async
 * Promise.
 * @return {Promise|SyncPromise} A promise which returns the generated key name.
 */
IdentityManager.prototype.generateKeyPairPromise = function
  (identityName, isKsk, params, useSync)
{
  var keyName;
  var thisManager = this;
  return this.identityStorage.getNewKeyNamePromise(identityName, isKsk, useSync)
  .then(function(localKeyName) {
    keyName = localKeyName;
    return thisManager.privateKeyStorage.generateKeyPairPromise
      (keyName, params, useSync);
  })
  .then(function() {
    return thisManager.privateKeyStorage.getPublicKeyPromise
      (keyName, useSync);
  })
  .then(function(publicKey) {
    return thisManager.identityStorage.addKeyPromise
      (keyName, params.getKeyType(), publicKey.getKeyDer());
  })
  .then(function() {
    return SyncPromise.resolve(keyName);
  });
};