Source: key-locator.js

/**
 * This class represents an NDN KeyLocator object.
 * Copyright (C) 2014-2015 Regents of the University of California.
 * @author: Meki Cheraoui
 * @author: Jeff Thompson <[email protected]>
 *
 * 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.
 */

var Blob = require('./util/blob.js').Blob;
var ChangeCounter = require('./util/change-counter.js').ChangeCounter;
var Name = require('./name.js').Name;
var NDNProtocolDTags = require('./util/ndn-protoco-id-tags.js').NDNProtocolDTags;
var PublisherID = require('./publisher-id.js').PublisherID;
var LOG = require('./log.js').Log.LOG;

/**
 * KeyLocator
 */
var KeyLocatorType = {
  KEYNAME: 1,
  KEY_LOCATOR_DIGEST: 2,
  // KeyLocatorType KEY and CERTIFICATE are not supported in NDN-TLV encoding and are deprecated.
  KEY: 3,
  CERTIFICATE: 4
};

exports.KeyLocatorType = KeyLocatorType;

/**
 * @constructor
 */
var KeyLocator = function KeyLocator(input, type)
{
  if (typeof input === 'object' && input instanceof KeyLocator) {
    // Copy from the input KeyLocator.
    this.type_ = input.type_;
    this.keyName_ = new ChangeCounter(new KeyName());
    this.keyName_.get().setContentName(input.keyName_.get().getContentName());
    this.keyName_.get().publisherID = input.keyName_.get().publisherID;
    this.keyData_ = input.keyData_;
    this.publicKey_ = input.publicKey_ == null ? null : new Buffer(input.publicKey_);
    this.certificate_ = input.certificate_ == null ? null : new Buffer(input.certificate_);
  }
  else {
    this.type_ = type;
    this.keyName_ = new ChangeCounter(new KeyName());
    this.keyData_ = new Blob();

    if (type == KeyLocatorType.KEYNAME)
      this.keyName_.set(input);
    else if (type == KeyLocatorType.KEY_LOCATOR_DIGEST)
      this.keyData_ = new Blob(input);
    else if (type == KeyLocatorType.KEY) {
      this.keyData_ = new Blob(input);
      // Set for backwards compatibility.
      this.publicKey_ = this.keyData_;
    }
    else if (type == KeyLocatorType.CERTIFICATE) {
      this.keyData_ = new Blob(input);
      // Set for backwards compatibility.
      this.certificate_ = this.keyData_;
    }
  }

  this.changeCount_ = 0;
};

exports.KeyLocator = KeyLocator;

/**
 * Get the key locator type. If KeyLocatorType.KEYNAME, you may also
 * getKeyName().  If KeyLocatorType.KEY_LOCATOR_DIGEST, you may also
 * getKeyData() to get the digest.
 * @returns {number} The key locator type, or null if not specified.
 */
KeyLocator.prototype.getType = function() { return this.type_; };

/**
 * Get the key name.  This is meaningful if getType() is KeyLocatorType.KEYNAME.
 * @returns {Name} The key name. If not specified, the Name is empty.
 */
KeyLocator.prototype.getKeyName = function()
{
  return this.keyName_.get().getContentName();
};

/**
 * Get the key data. If getType() is KeyLocatorType.KEY_LOCATOR_DIGEST, this is
 * the digest bytes. If getType() is KeyLocatorType.KEY, this is the DER
 * encoded public key. If getType() is KeyLocatorType.CERTIFICATE, this is the
 * DER encoded certificate.
 * @returns {Blob} The key data, or null if not specified.
 */
KeyLocator.prototype.getKeyData = function()
{
  if (this.type_ == KeyLocatorType.KEY)
    return new Blob(this.publicKey_);
  else if (this.type_ == KeyLocatorType.CERTIFICATE)
    return new Blob(this.certificate_);
  else
    return this.keyData_;
};

/**
 * @deprecated Use getKeyData. This method returns a Buffer which is the former
 * behavior of getKeyData, and should only be used while updating your code.
 */
KeyLocator.prototype.getKeyDataAsBuffer = function()
{
  return this.getKeyData().buf();
};

/**
 * Set the key locator type.  If KeyLocatorType.KEYNAME, you must also
 * setKeyName().  If KeyLocatorType.KEY_LOCATOR_DIGEST, you must also
 * setKeyData() to the digest.
 * @param {number} type The key locator type.  If null, the type is unspecified.
 */
KeyLocator.prototype.setType = function(type)
{
  this.type_ = type;
  ++this.changeCount_;
};

/**
 * Set key name to a copy of the given Name.  This is the name if getType()
 * is KeyLocatorType.KEYNAME.
 * @param {Name} name The key name which is copied.
 */
KeyLocator.prototype.setKeyName = function(name)
{
  this.keyName_.get().setContentName(name);
  ++this.changeCount_;
};

/**
 * Set the key data to the given value. This is the digest bytes if getType() is
 * KeyLocatorType.KEY_LOCATOR_DIGEST.
 * @param {Blob} keyData A Blob with the key data bytes.
 */
KeyLocator.prototype.setKeyData = function(keyData)
{
  this.keyData_ = typeof keyData === 'object' && keyData instanceof Blob ?
    keyData : new Blob(keyData);
  // Set for backwards compatibility.
  this.publicKey_ = this.keyData_.buf();
  this.certificate_ = this.keyData_.buf();
  ++this.changeCount_;
};

/**
 * Clear the keyData and set the type to not specified.
 */
KeyLocator.prototype.clear = function()
{
  this.type_ = null;
  this.keyName_.set(new KeyName());
  this.keyData_ = new Blob();
  this.publicKey_ = null;
  this.certificate_ = null;
  ++this.changeCount_;
};

/**
 * If the signature is a type that has a KeyLocator (so that
 * getFromSignature will succeed), return true.
 * Note: This is a static method of KeyLocator instead of a method of
 * Signature so that the Signature base class does not need to be overloaded
 * with all the different kinds of information that various signature
 * algorithms may use.
 * @param {Signature} signature An object of a subclass of Signature.
 * @returns {boolean} True if the signature is a type that has a KeyLocator,
 * otherwise false.
 */
KeyLocator.canGetFromSignature = function(signature)
{
  return signature instanceof Sha256WithRsaSignature;
}

/**
 * If the signature is a type that has a KeyLocator, then return it. Otherwise
 * throw an error.
 * @param {Signature} signature An object of a subclass of Signature.
 * @returns {KeyLocator} The signature's KeyLocator. It is an error if signature
 * doesn't have a KeyLocator.
 */
KeyLocator.getFromSignature = function(signature)
{
  if (signature instanceof Sha256WithRsaSignature)
    return signature.getKeyLocator();
  else
    throw new Error
      ("KeyLocator.getFromSignature: Signature type does not have a KeyLocator");
}

KeyLocator.prototype.from_ndnb = function(decoder) {

  decoder.readElementStartDTag(this.getElementLabel());

  if (decoder.peekDTag(NDNProtocolDTags.Key))
  {
    try {
      var encodedKey = decoder.readBinaryDTagElement(NDNProtocolDTags.Key);
      // This is a DER-encoded SubjectPublicKeyInfo.

      //TODO FIX THIS, This should create a Key Object instead of keeping bytes

      this.publicKey =   encodedKey;//CryptoUtil.getPublicKey(encodedKey);
      this.type = KeyLocatorType.KEY;

      if (LOG > 4) console.log('PUBLIC KEY FOUND: '+ this.publicKey);
    }
    catch (e) {
      throw new Error("Cannot parse key: ", e);
    }

    if (null == this.publicKey)
      throw new Error("Cannot parse key: ");
  }
  else if (decoder.peekDTag(NDNProtocolDTags.Certificate)) {
    try {
      var encodedCert = decoder.readBinaryDTagElement(NDNProtocolDTags.Certificate);

      /*
       * Certificates not yet working
       */

      this.certificate = encodedCert;
      this.type = KeyLocatorType.CERTIFICATE;

      if (LOG > 4) console.log('CERTIFICATE FOUND: '+ this.certificate);
    }
    catch (e) {
      throw new Error("Cannot decode certificate: " +  e);
    }
    if (null == this.certificate)
      throw new Error("Cannot parse certificate! ");
  } else  {
    this.type = KeyLocatorType.KEYNAME;

    this.keyName_.set(new KeyName());
    this.keyName_.get().from_ndnb(decoder);
  }
  decoder.readElementClose();
};

KeyLocator.prototype.to_ndnb = function(encoder)
{
  if (LOG > 4) console.log('type is is ' + this.type);

  if (this.type == KeyLocatorType.KEY_LOCATOR_DIGEST)
    // encodeSignedInfo already encoded this as the publisherPublicKeyDigest,
    //   so do nothing here.
    return;

  encoder.writeElementStartDTag(this.getElementLabel());

  if (this.type == KeyLocatorType.KEY) {
    if (LOG > 5) console.log('About to encode a public key' +this.publicKey);
    encoder.writeDTagElement(NDNProtocolDTags.Key, this.publicKey);
  }
  else if (this.type == KeyLocatorType.CERTIFICATE) {
    try {
      encoder.writeDTagElement(NDNProtocolDTags.Certificate, this.certificate);
    }
    catch (e) {
      throw new Error("CertificateEncodingException attempting to write key locator: " + e);
    }
  }
  else if (this.type == KeyLocatorType.KEYNAME)
    this.keyName_.get().to_ndnb(encoder);

  encoder.writeElementClose();
};

KeyLocator.prototype.getElementLabel = function()
{
  return NDNProtocolDTags.KeyLocator;
};

/**
 * Get the change count, which is incremented each time this object (or a child
 * object) is changed.
 * @returns {number} The change count.
 */
KeyLocator.prototype.getChangeCount = function()
{
  // Make sure each of the checkChanged is called.
  var changed = this.keyName_.checkChanged();
  if (changed)
    // A child object has changed, so update the change count.
    ++this.changeCount_;

  return this.changeCount_;
};

// Define properties so we can change member variable types and implement changeCount_.
Object.defineProperty(KeyLocator.prototype, "type",
  { get: function() { return this.getType(); },
    set: function(val) { this.setType(val); } });
/**
 * @deprecated Use getKeyName and setKeyName.
 */
Object.defineProperty(KeyLocator.prototype, "keyName",
  { get: function() { return this.keyName_.get(); },
    set: function(val) {
      this.keyName_.set(val == null ? new KeyName() : val);
      ++this.changeCount_;
    } });
/**
 * @@deprecated Use getKeyData and setKeyData.
 */
Object.defineProperty(KeyLocator.prototype, "keyData",
  { get: function() { return this.getKeyDataAsBuffer(); },
    set: function(val) { this.setKeyData(val); } });
/**
 * @deprecated
 */
Object.defineProperty(KeyLocator.prototype, "publicKey",
  { get: function() { return this.publicKey_; },
    set: function(val) { this.publicKey_ = val; ++this.changeCount_; } });
/**
 * @deprecated
 */
Object.defineProperty(KeyLocator.prototype, "certificate",
  { get: function() { return this.certificate_; },
    set: function(val) { this.certificate_ = val; ++this.changeCount_; } });

/**
 * @deprecated Use KeyLocator getKeyName and setKeyName. This is only needed to
 * support NDNx and will be removed.
 */
var KeyName = function KeyName()
{
  this.contentName_ = new ChangeCounter(new Name());
  this.publisherID = this.publisherID;  //publisherID
  this.changeCount_ = 0;
};

exports.KeyName = KeyName;

KeyName.prototype.getContentName = function()
{
  return this.contentName_.get();
};

KeyName.prototype.setContentName = function(name)
{
  this.contentName_.set(typeof name === 'object' && name instanceof Name ?
    new Name(name) : new Name());
  ++this.changeCount_;
};

KeyName.prototype.from_ndnb = function(decoder)
{
  decoder.readElementStartDTag(this.getElementLabel());

  this.contentName = new Name();
  this.contentName.from_ndnb(decoder);

  if (LOG > 4) console.log('KEY NAME FOUND: ');

  if (PublisherID.peek(decoder)) {
    this.publisherID = new PublisherID();
    this.publisherID.from_ndnb(decoder);
  }

  decoder.readElementClose();
};

KeyName.prototype.to_ndnb = function(encoder)
{
  encoder.writeElementStartDTag(this.getElementLabel());

  this.contentName.to_ndnb(encoder);
  if (null != this.publisherID)
    this.publisherID.to_ndnb(encoder);

  encoder.writeElementClose();
};

KeyName.prototype.getElementLabel = function() { return NDNProtocolDTags.KeyName; };

/**
 * Get the change count, which is incremented each time this object (or a child
 * object) is changed.
 * @returns {number} The change count.
 */
KeyName.prototype.getChangeCount = function()
{
  // Make sure each of the checkChanged is called.
  var changed = this.contentName_.checkChanged();
  if (changed)
    // A child object has changed, so update the change count.
    ++this.changeCount_;

  return this.changeCount_;
};

// Define properties so we can change member variable types and implement changeCount_.
Object.defineProperty(KeyName.prototype, "contentName",
  { get: function() { return this.getContentName(); },
    set: function(val) { this.setContentName(val); } });

// Put this last to avoid a require loop.
var Sha256WithRsaSignature = require('./sha256-with-rsa-signature.js').Sha256WithRsaSignature;