ndn-js
Version:
A JavaScript client library for Named Data Networking
1,149 lines (1,053 loc) • 116 kB
JavaScript
/**
* Copyright (C) 2014-2019 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 SafeBag = require('./safe-bag.js').SafeBag; /** @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, policyManager = null) - Create a security v1
* KeyChain to use the optional identityManager and policyManager.
* KeyChain(pibImpl, tpmBackEnd, policyManager = null) - Create a security v2
* KeyChain with explicitly-created PIB and TPM objects, and that optionally
* 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 An explicitly-created PIB object of a subclass of
* PibImpl.
* @param {TpmBackEnd} tpmBackEnd: An explicitly-created TPM object of a
* subclass of TpmBackEnd.
* @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_ = new NoVerifyPolicyManager(); // 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;
if (policyManager == undefined)
policyManager = new NoVerifyPolicyManager()
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 peri