UNPKG

ndn-js

Version:

A JavaScript client library for Named Data Networking

1,149 lines (1,053 loc) 116 kB
/** * 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