Source: tie-client.js

'use strict'

var Buffer = require('safe-buffer').Buffer
var inherits = require('inherits')
var dxl = require('@opendxl/dxl-client')
var Request = dxl.Request
var bootstrap = require('@opendxl/dxl-bootstrap')
var Client = bootstrap.Client
var MessageUtils = bootstrap.MessageUtils

var CertProvider = require('./constants/cert-provider')
var CertRepChangeEventProp = require('./constants/cert-rep-change-event-prop')
var CertReputationProp = require('./constants/cert-reputation-prop')
var CertReputationOverriddenProp = require('./constants/cert-reputation-overridden-prop')
var DetectionEventProp = require('./constants/detection-event-prop')
var FileProvider = require('./constants/file-provider')
var FileRepChangeEventProp = require('./constants/file-rep-change-event-prop')
var FirstInstanceEventProp = require('./constants/first-instance-event-prop')
var HashType = require('./constants/hash-type')
var ReputationProp = require('./constants/reputation-prop')
var RepChangeEventProp = require('./constants/rep-change-event-prop')

// Topic used to retrieve the reputation of a file
var TIE_GET_FILE_REPUTATION_TOPIC = '/mcafee/service/tie/file/reputation'
// Topic used to set the reputation of a file
var TIE_SET_FILE_REPUTATION_TOPIC = '/mcafee/service/tie/file/reputation/set'
// Topic used to retrieve systems that have referenced the file
var TIE_GET_FILE_FIRST_REFS_TOPIC = '/mcafee/service/tie/file/agents'
// Topic used to retrieve the reputation of a certificate
var TIE_GET_CERT_REPUTATION_TOPIC = '/mcafee/service/tie/cert/reputation'
// Topic used to set the reputation of a certificate
var TIE_SET_CERT_REPUTATION_TOPIC = '/mcafee/service/tie/cert/reputation/set'
// Topic used to retrieve systems that have referenced the certificate
var TIE_GET_CERT_FIRST_REFS_TOPIC = '/mcafee/service/tie/cert/agents'

// Topic used to notify that a file detection has occurred
var TIE_EVENT_FILE_DETECTION_TOPIC = '/mcafee/event/tie/file/detection'
// Topic used to notify when the first instance of a file has been found
var TIE_EVENT_FILE_FIRST_INSTANCE_TOPIC = '/mcafee/event/tie/file/firstinstance'
// Topic used to notify that a file reputation has changed
var TIE_EVENT_FILE_REPUTATION_CHANGE_TOPIC = '/mcafee/event/tie/file/repchange/broadcast'
// Topic used to notify that a certificate reputation has changed
var TIE_EVENT_CERT_REPUTATION_CHANGE_TOPIC = '/mcafee/event/tie/cert/repchange/broadcast'

// Default maximum number of results to use for first references queries
var DEFAULT_QUERY_LIMIT = 500

/**
 * @classdesc Responsible for all communication with the
 * Data Exchange Layer (DXL) fabric.
 * @external DxlClient
 * @see {@link https://opendxl.github.io/opendxl-client-javascript/jsdoc/Client.html}
 */

/**
 * @classdesc Event messages are sent using the
 * [sendEvent]{@link https://opendxl.github.io/opendxl-client-javascript/jsdoc/Client.html#sendEvent} method of a client
 * instance.
 * @external Event
 * @see {@link https://opendxl.github.io/opendxl-client-javascript/jsdoc/Event.html}
 */

/**
 * Converts from a base64 value to a hex string
 * @param {String} base64Value - The base64 value
 * @returns {String} The corresponding hex string
 * @private
 */
function base64ToHex (base64Value) {
  return Buffer.from(base64Value, 'base64').toString('hex')
}

/**
 * Converts from a hex string to a base64 string
 * @param {String} hexValue - The hex value
 * @returns {String} The corresponding base64 string
 * @private
 */
function hexToBase64 (hexValue) {
  return Buffer.from(hexValue, 'hex').toString('base64')
}

/**
 * Transforms an object containing properties where the key is a hash type
 * and the value is a corresponding hash value into the standard TIE format for
 * hashes - where each hash in a separate object in an array with the hash
 * type and value in separate object properties.
 *
 * For example, this would translate
 *
 * ```js
 * {
 *   md5: 'eb5e2b9dc51817a086d7b97eb52410ab',
 *   sha1: '435dfd470f727437c7cb4f07cba1f9a1f4272656'
 * }
 * ```
 *
 * to:
 *
 * ```js
 * [
 *   {
 *     type: 'md5',
 *     value: 'eb5e2b9dc51817a086d7b97eb52410ab'
 *   },
 *   {
 *     type: 'sha1',
 *     value: '435dfd470f727437c7cb4f07cba1f9a1f4272656'
 *   }
 * ]
 * ```
 * @param {Object} obj - Object containing an object of hash properties to
 *   transform.
 * @param {String} hashesKey - Name of the key in the `obj` which contains the
 *   object of hash properties to transform.
 * @returns {Object} The value for `obj` with the hashes under the `hashesKey`
 *   key having been transformed.
 * @private
 */
function transformHashesForServer (obj, hashesKey) {
  obj.hashes = []
  Object.keys(hashesKey).forEach(function (type) {
    obj.hashes.push({type: type, value: hexToBase64(hashesKey[type])})
  })
  return obj
}

/**
 * Transforms an array of hashes in standard TIE format to a simplified form
 * that is an object where the hash type is the key.
 *
 * For example, this would translate
 *
 * ```js
 * [
 *   {
 *     type: 'md5',
 *     value: 'eb5e2b9dc51817a086d7b97eb52410ab'
 *   },
 *   {
 *     type: 'sha1',
 *     value: '435dfd470f727437c7cb4f07cba1f9a1f4272656'
 *   }
 * ]
 * ```
 *
 * to
 *
 * ```js
 * {
 *   md5: 'eb5e2b9dc51817a086d7b97eb52410ab',
 *   sha1: '435dfd470f727437c7cb4f07cba1f9a1f4272656'
 * }
 * ```
 *
 * @param {Object} obj - Object containing the array of hashes to transform.
 * @param {String} hashesKey - Name of the key in the `obj` which contains
 *   the array of hashes to transform.
 * @returns {Object} The value for `obj` with the hashes under the `hashesKey`
 *   key having been transformed.
 * @private
 */
function transformHashesFromServer (obj, hashesKey) {
  var hashes = obj[hashesKey]
  if (hashes) {
    var newHashes = {}
    Object.keys(hashes).forEach(function (key) {
      var value = hashes[key]
      newHashes[value.type] = base64ToHex(value.value)
    })
    obj[hashesKey] = newHashes
  }
}

/**
 * Transforms the specified object of reputations from the standard TIE format
 * to a simplified form (hex vs base64 hashes, etc.).
 * @param {Object} reputations - Object of reputations in the standard TIE
 *   format.
 * @returns {Object} The object of reputations in a simplified form.
 * @private
 */
function transformReputations (reputations) {
  var returnValue = {}
  reputations.forEach(function (reputation) {
    returnValue[reputation[ReputationProp.PROVIDER_ID]] = reputation
    var overridden = reputation[CertReputationProp.OVERRIDDEN]
    if (overridden && overridden[CertReputationOverriddenProp.FILES]) {
      var overriddenFiles = overridden[CertReputationOverriddenProp.FILES]
      overriddenFiles.forEach(function (overriddenFile) {
        transformHashesFromServer(overriddenFile, 'hashes')
      })
    }
  })
  return returnValue
}

/**
 * Transforms the payload received from TIE for a file or certificate reputation
 * change event into a simplified form (hex vs base64 hashes, etc.).
 * @param {Object} repChangePayload - Payload received in a reputation changed
 *   event.
 * @returns {Object} - Value for the `repChangePayload` parameter with
 *   subordinate objects changed into a simplified form.
 * @private
 */
function transformChangedReputationPayload (repChangePayload) {
  transformHashesFromServer(repChangePayload, RepChangeEventProp.HASHES)
  var newReputations = repChangePayload[RepChangeEventProp.NEW_REPUTATIONS]
  if (newReputations) {
    repChangePayload[RepChangeEventProp.NEW_REPUTATIONS] =
      transformReputations(newReputations.reputations)
  }
  var oldReputations = repChangePayload[RepChangeEventProp.OLD_REPUTATIONS]
  if (oldReputations) {
    repChangePayload[RepChangeEventProp.OLD_REPUTATIONS] =
      transformReputations(oldReputations.reputations)
  }
  var relationships = repChangePayload[FileRepChangeEventProp.RELATIONSHIPS]
  if (relationships) {
    var certificate = relationships.certificate
    if (certificate) {
      transformHashesFromServer(certificate, 'hashes')
      if (certificate.publicKeySha1) {
        certificate.publicKeySha1 = base64ToHex(certificate.publicKeySha1)
      }
    }
  }
  if (repChangePayload[CertRepChangeEventProp.PUBLIC_KEY_SHA1]) {
    repChangePayload[CertRepChangeEventProp.PUBLIC_KEY_SHA1] =
      base64ToHex(repChangePayload[CertRepChangeEventProp.PUBLIC_KEY_SHA1])
  }
}

/**
 * @classdesc This client provides a high level wrapper for communicating with
 * the McAfee Threat Intelligence Exchange (TIE) DXL service.
 *
 * The purpose of this client is to allow users to access the features of TIE
 * (manage reputations, determine where a file has executed, etc.) without
 * having to focus on lower-level details such as TIE-specific DXL topics and
 * message formats.
 * @param {external:DxlClient} dxlClient - The DXL client to use for
 *   communication with the TIE DXL service.
 * @constructor
 */
function TieClient (dxlClient) {
  Client.call(this, dxlClient)

  /**
   * Register an event callback with the DXL fabric.
   * @param {String} topic - Topic of the event to register
   * @param {Function} transform - Transform function to invoke with the content
   *   of the payload received for an event.
   * @param {Function} callback - Callback to invoke with the content returned
   *   from the `transform` function for the event payload as the first
   *   parameter and the original DXL event as the second parameter.
   * @private
   */
  this._addCallback = function (topic, transform, callback) {
    this._dxlClient.addEventCallback(topic, function (event) {
      var payload = MessageUtils.jsonPayloadToObject(event)
      transform(payload)
      callback(payload, event)
    })
  }

  /**
   * Common helper for retrieving reputations for files and certificates.
   * @param {Function} callback - Callback function to invoke with the results
   *   of the reputation lookup. If an error occurs when performing the
   *   lookup, the first parameter supplied to the callback contains an `Error`
   *   object with failure details. On successful lookup, a reputations object
   *   is provided as the second parameter to the callback.
   * @param {String} topic - DXL topic to query for reputations.
   * @param {Object} hashes - An object of hashes that identify the file or
   *   certificate to retrieve the reputations for. The `key` for each property
   *   is the `hash type` and the `value` is the `hex` representation of the
   *   hash value. See the [HashType]{@link module:HashType} module for the list
   *   of `hash type` constants.
   * @param {Object} payload - Initial content for the payload to send in the
   *   DXL reputation request. A transformed representation of the hashes is
   *   added to the payload before it is sent to the DXL fabric.
   * @private
   */
  this._getReputation = function (callback, topic, hashes, payload) {
    payload = payload || {}
    var request = new Request(topic)
    MessageUtils.objectToJsonPayload(request,
      transformHashesForServer(payload, hashes))
    this._dxlClient.asyncRequest(request, function (error, response) {
      var reputations = null
      if (response) {
        try {
          var responseObj = MessageUtils.jsonPayloadToObject(response)
          var reputationArr = responseObj.reputations
          reputations = reputationArr ? transformReputations(reputationArr) : {}
        } catch (err) {
          error = err
          reputations = null
        }
      }
      callback(error, reputations)
    })
  }

  /**
   * Common helper for setting reputations for files and certificates.
   * @param {Function} callback - Callback to invoke after setting the
   *   reputation. If an error occurs when attempting to set the reputation, the
   *   first parameter supplied to the callback contains an `Error` object with
   *   failure details. If no error occurs, the first parameter supplied to the
   *   callback is `null`.
   * @param {String} topic - DXL topic to which the set reputation request is
   *   sent.
   * @param {Number} trustLevel - The new `trust level` for the file or
   *   certificate. The list of standard `trust levels` can be found in the
   *   [TrustLevel]{@link module:TrustLevel} constants module.
   * @param {Number} providerId - Id of the reputation provider.
   * @param {Object} hashes - An object of hashes that identify the file or
   *   certificate to update the reputation for. The `key` in each property is
   *   the `hash type` and the `value` is the `hex` representation of the hash
   *   value. See the [HashType]{@link module:HashType} module for the list of
   *   `hash type` constants.
   * @param {Object} payload - Initial content for the payload to send in the
   *   DXL reputation set request. Other parameters, including the `hashes`
   *   `trustLevel`, and `providerId` are added to the payload before it is
   *   sent to the DXL fabric.
   * @param {String} [comment] - A comment to associate with the file.
   * @private
   */
  this._setReputation = function (callback, topic, trustLevel, providerId,
                                  hashes, payload, comment) {
    if ((typeof trustLevel === 'undefined') || (trustLevel === null)) {
      throw new TypeError('trustLevel was not specified')
    }
    trustLevel = Number(trustLevel)
    if (isNaN(trustLevel)) {
      throw new TypeError('trustLevel was not a number')
    }
    payload = payload || {}
    comment = comment || ''
    var request = new Request(topic)
    payload.trustLevel = trustLevel
    payload.providerId = providerId
    payload.comment = comment
    transformHashesForServer(payload, hashes)
    MessageUtils.objectToJsonPayload(request, payload)
    this._dxlClient.asyncRequest(request, function (error) {
      if (callback) {
        callback(error)
      }
    })
  }

  /**
   * Common helper for retrieving the set of systems which have referenced the
   * specified file or certificate (as identified by hashes).
   * @param {Function} callback - Callback function to invoke with the results
   *   of the first references lookup. If an error occurs when performing the
   *   lookup, the first parameter supplied to the callback contains an `Error`
   *   object with failure details. On successful lookup, an array containing an
   *   object for each system that has referenced the file is provided as the
   *   second parameter to the callback.
   * @param {String} topic - DXL topic to which the first references request is
   *   sent.
   * @param {Object} hashes - An object of hashes that identify the file or
   *   certificate to lookup.  The `key` for each property is the `hash type`
   *   and the corresponding `value` is the `hex` representation of the hash
   *   value. See the [HashType]{@link module:HashType} module for the list of
   *   `hash type` constants.
   * @param {Object} payload - Initial content for the payload to send in the
   *   DXL first references request. A transformed representation of the hashes
   *   is added to the payload before it is sent to the DXL fabric.
   * @param {Number} [queryLimit=500] - The maximum number of results to return.
   * @private
   */
  this._getFirstReferences =
    function (callback, topic, hashes, payload, queryLimit) {
      payload = payload || {}
      if (typeof queryLimit === 'undefined') {
        queryLimit = DEFAULT_QUERY_LIMIT
      }
      var request = new Request(topic)
      payload.queryLimit = queryLimit
      transformHashesForServer(payload, hashes)
      MessageUtils.objectToJsonPayload(request, payload)
      this._dxlClient.asyncRequest(request, function (error, response) {
        var agents = null
        if (response) {
          try {
            var responseObj = MessageUtils.jsonPayloadToObject(response)
            agents = responseObj.agents || []
          } catch (err) {
            error = err
          }
        }
        callback(error, agents)
      })
    }
}

inherits(TieClient, Client)

/**
 * Registers a callback with the client to receive `file detection` events.
 * Each `detection event` that is received from the DXL fabric will cause this
 * method to be invoked with the corresponding `detection information`.
 *
 * ** Detection Information **
 *
 * The `detection` information is provided as an object via the first parameter
 * delivered to the callback.
 *
 * An example `detection` object is shown below:
 *
 * ```js
 * {
 *   "agentGuid": "{68125cd6-a5d8-11e6-348e-000c29663178}",
 *   "detectionTime": 1481301038,
 *   "hashes": {
 *      "md5": "eb5e2b9dc51817a086d7b97eb52410ab",
 *      "sha1": "435dfd470f727437c7cb4f07cba1f9a1f4272656",
 *      "sha256": "414bb16b10ece2db2d8448cb9f313f80cb77c310ca0c19ee03c73cba0c16fedb"
 *   },
 *   "localReputation": 1,
 *   "name": "TEST_MALWARE.EXE",
 *   "remediationAction": 5
 * }
 * ```
 *
 * The top level property names in the object are listed in the
 * [DetectionEventProp]{@link module:DetectionEventProp} constants module.
 *
 * The information provided in the object includes:
 *
 * * System the detection occurred on.
 * * Time the detection occurred (Epoch time).
 * * File that triggered the detection (file name and associated hashes).
 * * Reputation value that was calculated locally which triggered the detection.
 * * Remediation action that occurred in response to the detection.
 * @example
 * tieClient.addFileDetectionCallback(function (detectionObj, originalEvent) {
 *   console.log('Detection on topic: ' + originalEvent.destinationTopic)
 *   console.log(MessageUtils.objectToJson(detectionObj, true))
 * })
 * @param {Function} detectionCallback - Callback that will receive
 *   `file detection` events. The first parameter passed to the callback
 *   function is an object decoded from the JSON payload of the event content.
 *   The second parameter passed to the callback function is the full DXL
 *   [Event]{@link external:Event} object.
 */
TieClient.prototype.addFileDetectionCallback =
  function (detectionCallback) {
    this._addCallback(TIE_EVENT_FILE_DETECTION_TOPIC,
      function (payload) {
        transformHashesFromServer(payload, DetectionEventProp.HASHES)
      },
      detectionCallback)
  }

/**
 * Unregisters a callback from the client so that it will no longer receive
 * `file detection` events.
 * @param {Function} detectionCallback - The callback instance to unregister.
 */
TieClient.prototype.removeFileDetectionCallback =
  function (detectionCallback) {
    this._dxlClient.addEventCallback(TIE_EVENT_FILE_DETECTION_TOPIC,
      detectionCallback)
  }

/**
 * Registers a callback with the client to receive `file first instance` events.
 * The "first instance" event indicates that this is the first time the file has
 * been encountered within the local enterprise. Each `first instance event`
 * that is received from the DXL fabric will cause this method to be invoked
 * with the corresponding `first instance information`.
 *
 * **First Instance Information**
 *
 * The `first instance` information is provided as an object via the first
 * parameter delivered to the callback.
 *
 * An example `first instance` object is shown below:
 *
 * ```js
 * {
 *   "agentGuid": "{68125cd6-a5d8-11e6-348e-000c29663178}",
 *   "hashes": {
 *     "md5": "31dbe8cc443d2ca7fd236ac00a52fb17",
 *     "sha1": "2d6ca45061b7972312e00e5933fdff95bb90b61b",
 *     "sha256": "aa3c461d4c21a392e372d0d6ca4ceb1e4d88098d587659454eaf4d93c661880f"
 *   },
 *   "name": "MORPH.EXE"
 * }
 * ```
 *
 * The top level property names in the object are listed in the
 * [FirstInstanceEventProp]{@link module:FirstInstanceEventProp} constants
 * module.
 *
 * The information provided in the object includes:
 *
 * * System the first instance of the file was found on
 * * File information (file name and associated hashes)
 * @example
 * tieClient.addFileFirstInstanceCallback(
 *   function (firstInstanceObj, originalEvent) {
 *     console.log('First instance on topic: ' + originalEvent.destinationTopic)
 *     console.log(MessageUtils.objectToJson(firstInstanceObj, true))
 *   }
 * )
 * @param {Function} firstInstanceCallback - Callback that will receive
 *   `file first instance` events. The first parameter passed to the callback
 *   function is an object decoded from the JSON payload of the event content.
 *   The second parameter passed to the callback function is the full DXL
 *   [Event]{@link external:Event} object.
 */
TieClient.prototype.addFileFirstInstanceCallback =
  function (firstInstanceCallback) {
    this._addCallback(TIE_EVENT_FILE_FIRST_INSTANCE_TOPIC,
      function (payload) {
        transformHashesFromServer(payload, FirstInstanceEventProp.HASHES)
      },
      firstInstanceCallback)
  }

/**
 * Unregisters a callback from the client so that it will no longer receive
 * `file first instance` events.
 * @param {Function} firstInstanceCallback - The callback instance to
 *   unregister.
 */
TieClient.prototype.removeFileFirstInstanceCallback =
  function (firstInstanceCallback) {
    this._dxlClient.removeEventCallback(TIE_EVENT_FILE_FIRST_INSTANCE_TOPIC,
      firstInstanceCallback)
  }

/**
 * Registers a callback with the client to receive `file reputation` change
 * events. Each `reputation change event` that is received from the DXL fabric
 * will cause this method to be invoked with the corresponding `reputation
 * change information`.
 *
 * **Reputation Change Information**
 *
 * The `Reputation Change` information is provided as an object via the first
 * parameter delivered to the callback.
 *
 * An example `reputation change` object is shown below:
 *
 * ```js
 * {
 *   "hashes": {
 *     "md5": "f2c7bb8acc97f92e987a2d4087d021b1",
 *     "sha1": "7eb0139d2175739b3ccb0d1110067820be6abd29",
 *     "sha256": "142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2"
 *   },
 *   "newReputations": {
 *     "1": {
 *       "attributes": {
 *         "2120340": "2139160704"
 *       },
 *       "createDate": 1480455704,
 *       "providerId": 1,
 *       "trustLevel": 99
 *     },
 *     "3": {
 *       "attributes": {
 *         "2101652": "235",
 *         "2102165": "1476902802",
 *         "2111893": "244",
 *         "2114965": "4",
 *         "2139285": "73183493944770750"
 *       },
 *       "createDate": 1476902802,
 *       "providerId": 3,
 *       "trustLevel": 99
 *     }
 *   },
 *   "oldReputations": {
 *     "1": {
 *       "attributes": {
 *         "2120340": "2139160704"
 *       },
 *       "createDate": 1480455704,
 *       "providerId": 1,
 *       "trustLevel": 99
 *     },
 *     "3": {
 *       "attributes": {
 *         "2101652": "235",
 *         "2102165": "1476902802",
 *         "2111893": "244",
 *         "2114965": "4",
 *         "2139285": "73183493944770750"
 *       },
 *       "createDate": 1476902802,
 *       "providerId": 3,
 *       "trustLevel": 85
 *     }
 *   },
 *   "updateTime": 1481219581
 * }
 * ```
 *
 * The top level property names in the object are listed in the
 * [FileRepChangeEventProp]{@link module:FileRepChangeEventProp} constants
 * module.
 *
 * The `reputation change` information is separated into 4 distinct sections:
 *
 * **Hash values**
 *
 * Keyed in the object by the `"hashes"` string.
 *
 * An object of hashes that identify the file whose reputation has changed. The
 * `key` in each property is the `hash type` and the ``value`` is the `hex`
 * representation of the hash value. See the [HashType]{@link module:HashType}
 * module for the list of `hash type` constants.
 *
 * **New reputations**
 *
 * Keyed in the object by the ``"newReputations"`` string.
 *
 * The new `Reputations` for the file whose reputation has changed, as an
 * object.
 *
 * The `key` for each property in the object corresponds to a particular
 * `provider` of the associated `reputation`. The list of `file reputation
 * providers` can be found in the [FileProvider]{@link module:FileProvider}
 * constants module.
 *
 * Each reputation contains a standard set of properties (trust level, creation
 * date, etc.). These properties are listed in the
 * [FileReputationProp]{@link module:FileReputationProp} constants module.
 *
 * Each reputation can also contain a provider-specific set of attributes as an
 * object. These attributes can be found in the following modules:
 *
 * | Module                                                    | Description |
 * | --------------------------------------------------------- | ----------- |
 * | [FileEnterpriseAttrib]{@link module:FileEnterpriseAttrib} | Attributes associated with the `Enterprise` reputation provider for files. |
 * | [FileGtiAttrib]{@link module:FileGtiAttrib}               | Attributes associated with the `Global Threat Intelligence (GTI)` reputation provider for files. |
 * | [AtdAttrib]{@link module:AtdAttrib}                       | Attributes associated with the `Advanced Threat Defense (ATD)` reputation provider. |
 *
 * **Old reputations**
 *
 * Keyed in the object by the ``"oldReputations"`` string.
 *
 * The previous `Reputations` for the file whose reputation has changed, as an
 * object.
 *
 * See the "New reputations" section above for additional information regarding
 * reputation details.
 *
 * **Change time**
 *
 * Keyed in the object by the ``"updateTime"`` string.
 *
 * The time the reputation change occurred (Epoch time).
 * @example
 * tieClient.addFileReputationChangeCallback(
 *   function (repChangeObj, originalEvent) {
 *     console.log('Reputation change on topic: ' +
 *       originalEvent.destinationTopic)
 *     console.log(MessageUtils.objectToJson(repChangeObj, true))
 *   }
 * )
 * @param {Function} repChangeCallback - Callback that will receive
 *   `file reputation` change events. The first parameter passed to the callback
 *   function is an object decoded from the JSON payload of the event content.
 *   The second parameter passed to the callback function is the full DXL
 *   [Event]{@link external:Event} object.
 */
TieClient.prototype.addFileReputationChangeCallback =
  function (repChangeCallback) {
    this._addCallback(TIE_EVENT_FILE_REPUTATION_CHANGE_TOPIC,
      transformChangedReputationPayload,
      repChangeCallback)
  }

/**
 * Unregisters a callback from the client so that it will no longer receive
 * `file reputation` change events.
 * @param {Function} repChangeCallback - The callback instance to unregister.
 */
TieClient.prototype.removeFileReputationChangeCallback =
  function (repChangeCallback) {
    this._dxlClient.addEventCallback(TIE_EVENT_FILE_REPUTATION_CHANGE_TOPIC,
      repChangeCallback)
  }

/**
 * Registers a callback with the client to receive `certificate reputation`
 * change events. Each `reputation change event` that is received from the DXL
 * fabric will cause this method to be invoked with the corresponding
 * `reputation change information`.
 *
 * **Reputation Change Information**
 *
 * The `Reputation Change` information is provided as an object via the first
 * parameter delivered to the callback.
 *
 * An example `reputation change` object is shown below:
 *
 * ```js
 * {
 *   "hashes": {
 *     "md5": "f2c7bb8acc97f92e987a2d4087d021b1",
 *     "sha1": "7eb0139d2175739b3ccb0d1110067820be6abd29",
 *     "sha256": "142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2"
 *   },
 *   "newReputations": {
 *     "1": {
 *       "attributes": {
 *         "2120340": "2139160704"
 *       },
 *       "createDate": 1480455704,
 *       "providerId": 1,
 *       "trustLevel": 99
 *     },
 *     "3": {
 *       "attributes": {
 *         "2101652": "235",
 *         "2102165": "1476902802",
 *         "2111893": "244",
 *         "2114965": "4",
 *         "2139285": "73183493944770750"
 *       },
 *       "createDate": 1476902802,
 *       "providerId": 3,
 *       "trustLevel": 99
 *     }
 *   },
 *   "oldReputations": {
 *     "1": {
 *       "attributes": {
 *         "2120340": "2139160704"
 *       },
 *       "createDate": 1480455704,
 *       "providerId": 1,
 *       "trustLevel": 99
 *     },
 *     "3": {
 *       "attributes": {
 *         "2101652": "235",
 *         "2102165": "1476902802",
 *         "2111893": "244",
 *         "2114965": "4",
 *         "2139285": "73183493944770750"
 *       },
 *       "createDate": 1476902802,
 *       "providerId": 3,
 *       "trustLevel": 85
 *     }
 *   },
 *   "updateTime": 1481219581
 * }
 * ```
 *
 * The top level property names in the object are listed in the
 * [CertRepChangeEventProp]{@link module:CertRepChangeEventProp} constants
 * module.
 *
 * The `reputation change` information is separated into 4 distinct sections:
 *
 * **Hash values**
 *
 * Keyed in the object by the `"hashes"` string.
 *
 * An object of hashes that identify the certificate whose reputation has
 * changed. The `key` in the object is the `hash type` and the ``value`` is
 * the `hex` representation of the hash value. See the [HashType]{@link
 * module:HashType} module for the list of `hash type` constants.
 *
 * There may also be a top-level property named ``"publicKeySha1"`` that
 * contains the SHA-1 of the certificate's public key.
 *
 * **New reputations**
 *
 * Keyed in the object by the ``"newReputations"`` string.
 *
 * The new `Reputations` for the file whose reputation has changed, as an
 * object.
 *
 * The `key` for each property in the object corresponds to a particular
 * `provider` of the associated `reputation`. The list of `certificate
 * reputation providers` can be found in the
 * [CertProvider]{@link module:CertProvider} constants module.
 *
 * Each reputation contains a standard set of properties (trust level, creation
 * date, etc.). These properties are listed in the
 * [CertReputationProp]{@link module:CertReputationProp} constants module.
 *
 * Each reputation can also contain a provider-specific set of attributes as an
 * object. These attributes can be found in the following modules:
 *
 * | Module                                                    | Description |
 * | --------------------------------------------------------- | ----------- |
 * | [CertEnterpriseAttrib]{@link module:CertEnterpriseAttrib} | Attributes associated with the `Enterprise` reputation provider for certificates. |
 * | [CertGtiAttrib]{@link module:CertGtiAttrib}               | Attributes associated with the `Global Threat Intelligence (GTI)` reputation provider for certificates. |
 *
 * **Old reputations**
 *
 * Keyed in the object by the ``"oldReputations"`` string.
 *
 * The previous `Reputations` for the file whose reputation has changed, as an
 * object.
 *
 * See the "New reputations" section above for additional information regarding
 * reputation details.
 *
 * **Change time**
 *
 * Keyed in the object by the ``"updateTime"`` string.
 *
 * The time the reputation change occurred (Epoch time).
 * @example
 * tieClient.addCertificateReputationChangeCallback(
 *   function (repChangeObj, originalEvent) {
 *     console.log('Reputation change on topic: ' +
 *       originalEvent.destinationTopic)
 *     console.log(MessageUtils.objectToJson(repChangeObj, true))
 *   }
 * )
 * @param {Function} repChangeCallback - Callback that will receive
 *   `certificate reputation` change events. The first parameter passed to the
 *   callback function is an object decoded from the JSON payload of the event
 *   content. The second parameter passed to the callback function is the full
 *   DXL [Event]{@link external:Event} object.
 */
TieClient.prototype.addCertificateReputationChangeCallback =
  function (repChangeCallback) {
    this._addCallback(TIE_EVENT_CERT_REPUTATION_CHANGE_TOPIC,
      transformChangedReputationPayload,
      repChangeCallback)
  }

/**
 * Unregisters a callback from the client so that it will no longer receive
 * `certificate reputation` change events.
 * @param {Function} repChangeCallback - The callback instance to unregister.
 */
TieClient.prototype.removeCertificateReputationChangeCallback =
  function (repChangeCallback) {
    this._dxlClient.removeEventCallback(TIE_EVENT_CERT_REPUTATION_CHANGE_TOPIC,
      repChangeCallback)
  }

/**
 * Retrieves the reputations for the specified file (as identified by hashes)
 *
 * At least one `hash value` of a particular `hash type` (MD5, SHA-1, etc.) must
 * be specified. Passing additional hashes increases the likelihood of other
 * reputations being located across the set of `file reputation providers`.
 *
 * **Reputations**
 *
 * The `Reputations` for the file is provided as an object via the second
 * parameter delivered to the callback.
 *
 * The `key` for each property in the object corresponds to a particular
 * `provider` of the associated `reputation`. The list of `file reputation
 * providers` can be found in the [FileProvider]{@link module:FileProvider}
 * constants module.
 *
 * An example reputations object is shown below:
 *
 * ```js
 * {
 *   "1": {
 *     "attributes": {
 *       "2120340": "2139160704"
 *     },
 *     "createDate": 1480455704,
 *     "providerId": 1,
 *     "trustLevel": 99
 *   },
 *   "3": {
 *     "attributes": {
 *       "2101652": "52",
 *       "2102165": "1476902802",
 *       "2111893": "56",
 *       "2114965": "1",
 *       "2139285": "73183493944770750"
 *     },
 *     "createDate": 1476902802,
 *     "providerId": 3,
 *     "trustLevel": 99
 *   }
 *}
 * ```
 *
 * The `"1"` `key` corresponds to a reputation from the
 * "Global Threat Intelligence (GTI)" reputation provider while the `"3"` `key`
 * corresponds to a reputation from the "Enterprise" reputation provider.
 *
 * Each reputation contains a standard set of properties (trust level, creation
 * date, etc.). These properties are listed in the
 * [FileReputationProp]{@link module:FileReputationProp} constants module.
 *
 * The following example shows how to access the `trust level` property of the
 * "Enterprise" reputation:
 *
 * ```js
 * var trustLevel = reputationsObj[FileProvider.ENTERPRISE][FileReputationProp.TRUST_LEVEL]
 * ```
 *
 * Each reputation can also contain a provider-specific set of attributes as an
 * object. These attributes can be found in the following modules:
 *
 * | Module                                                    | Description |
 * | --------------------------------------------------------- | ----------- |
 * | [FileEnterpriseAttrib]{@link module:FileEnterpriseAttrib} | Attributes associated with the `Enterprise` reputation provider for files. |
 * | [FileGtiAttrib]{@link module:FileGtiAttrib}               | Attributes associated with the `Global Threat Intelligence (GTI)` reputation provider for files. |
 * | [AtdAttrib]{@link module:AtdAttrib}                       | Attributes associated with the `Advanced Threat Defense (ATD)` reputation provider. |
 *
 * The following example shows how to access the `prevalence` attribute from the
 * "Enterprise" reputation:
 *
 * ```js
 * var entRep = reputationsObj[FileProvider.ENTERPRISE]
 * var entRepAttribs = entRep[FileReputationProp.ATTRIBUTES]
 * var prevalence = Number(entRepAttribs[FileEnterpriseAttrib.PREVALENCE])
 * ```
 * @example
 * var hashes = {}
 * hashes[HashType.MD5] = 'f2c7bb8acc97f92e987a2d4087d021b1'
 * hashes[HashType.SHA1] = '7eb0139d2175739b3ccb0d1110067820be6abd29'
 * hashes[HashType.SHA256] = '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2'
 * tieClient.getFileReputation(
 *   function (error, reputations) {
 *     if (error) {
 *       console.log('Error getting file reputations: ' + error.message)
 *     } else {
 *       console.log('File reputations:')
 *       console.log(MessageUtils.objectToJson(reputations, true))
 *     }
 *   },
 *   hashes
 * )
 * @param {Function} callback - Callback function to invoke with the results
 *   of the reputation lookup. If an error occurs when performing the
 *   lookup, the first parameter supplied to the callback contains an `Error`
 *   object with failure details. On successful lookup, a reputations object is
 *   provided as the second parameter to the callback. Each property `value`
 *   in the reputations object is a reputation from a particular
 *   `reputation provider`, which is identified by the key. The list of
 *   `file reputation providers` can be found in the
 *   [FileProvider]{@link module:FileProvider} constants module.
 * @param {Object} hashes - An object of hashes that identify the file to
 *   retrieve the reputations for. The `key` for each property is the
 *   `hash type` and the `value` is the `hex` representation of the hash value.
 *   See the [HashType]{@link module:HashType} module for the list of
 *   `hash type` constants.
 */
TieClient.prototype.getFileReputation =
  function (callback, hashes) {
    this._getReputation(callback, TIE_GET_FILE_REPUTATION_TOPIC, hashes)
  }

/**
 * Sets the "Enterprise" reputation  (`trust level`) of a specified file (as
 * identified by hashes).
 *
 * **Client Authorization**
 *
 * The OpenDXL Python client invoking this method must have permission to send
 * messages to the `/mcafee/service/tie/file/reputation/set` topic, which is
 * part of the `TIE Server Set Enterprise Reputation` authorization group.
 *
 * The following page provides an example of authorizing a Python client to
 * send messages to an `authorization group`. While the example is based on
 * McAfee Active Response (MAR), the instructions are the same with the
 * exception of swapping the `TIE Server Set Enterprise Reputation`
 * `authorization group` in place of `Active Response Server API`:
 *
 * <https://opendxl.github.io/opendxl-client-python/pydoc/marsendauth.html>
 * @example
 * var hashes = {}
 * hashes[HashType.MD5] = 'f2c7bb8acc97f92e987a2d4087d021b1'
 * hashes[HashType.SHA1] = '7eb0139d2175739b3ccb0d1110067820be6abd29'
 * hashes[HashType.SHA256] = '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2'
 * tieClient.setFileReputation(
 *   function (error) {
 *     if (error) {
 *       console.log('Error: ' + error.message)
 *     } else {
 *       console.log('Succeeded')
 *     }
 *   },
 *   TrustLevel.KNOWN_TRUSTED,
 *   hashes,
 *   'notepad.exe',
 *   'Reputation set via OpenDXL'
 * )
 * @param {Function} callback - Callback to invoke after setting the file
 *   reputation. If an error occurs when attempting to set the reputation, the
 *   first parameter supplied to the callback contains an `Error` object with
 *   failure details. If no error occurs, the first parameter supplied to the
 *   callback is `null`.
 * @param {Number} trustLevel - The new `trust level` for the file. The list of
 *   standard `trust levels` can be found in the
 *   [TrustLevel]{@link module:TrustLevel} constants module.
 * @param {Object} hashes - An object of hashes that identify the file to
 *   update the reputation for. The `key` in each property is the `hash type`
 *   and the `value` is the `hex` representation of the hash value. See the
 *   [HashType]{@link module:HashType} module for the list of `hash type`
 *   constants.
 * @param {String} [filename] - A file name to associate with the file.
 * @param {String} [comment] - A comment to associate with the file.
 */
TieClient.prototype.setFileReputation =
  function (callback, trustLevel, hashes, filename, comment) {
    filename = filename || ''
    var payload = {filename: filename}
    this._setReputation(callback, TIE_SET_FILE_REPUTATION_TOPIC,
      trustLevel, FileProvider.ENTERPRISE, hashes, payload, comment)
  }

/**
 * Retrieves the set of systems which have referenced (typically executed) the
 * specified file (as identified by hashes).
 *
 * **Systems**
 *
 * The systems that have referenced the file is provided as an array of objects
 * via the second parameter delivered to the callback.
 *
 * An example array is shown below:
 *
 * ```js
 * [
 *   {
 *     "agentGuid": "{3a6f574a-3e6f-436d-acd4-bcde336b054d}",
 *     "date": 1475873692
 *   },
 *   {
 *     "agentGuid": "{68125cd6-a5d8-11e6-348e-000c29663178}",
 *     "date": 1478626172
 *   }
 * ]
 * ```
 *
 * Each entry in the array is an object containing details about a system that
 * has referenced the file. See the [FirstRefProp]{@link module:FirstRefProp}
 * constants module for details about the information that is available for each
 * system in the object.
 * @example
 * var fileHashes = {}
 * fileHashes[HashType.MD5] = 'f2c7bb8acc97f92e987a2d4087d021b1'
 * fileHashes[HashType.SHA1] = '7eb0139d2175739b3ccb0d1110067820be6abd29'
 * fileHashes[HashType.SHA256] = '142e1d688ef0568370c37187fd9f2351d7ddeda574f8bfa9b0fa4ef42db85aa2'
 *
 * tieClient.getFileFirstReferences(
 *   function (error, systems) {
 *     if (error) {
 *       console.log('Error getting file references: ' + error.message)
 *     } else {
 *       console.log('\nSystems that have referenced the file:\n')
 *       systems.forEach(function (system) {
 *         console.log('\t' + system[FirstRefProp.SYSTEM_GUID] + ': ' +
 *           EpochUtil.toLocalTimeString(system[FirstRefProp.DATE])
 *         )
 *       })
 *     }
 *   },
 *   fileHashes
 * )
 * @param {Function} callback - Callback function to invoke with the results
 *   of the first references lookup. If an error occurs when performing the
 *   lookup, the first parameter supplied to the callback contains an `Error`
 *   object with failure details. On successful lookup, an array containing an
 *   object for each system that has referenced the file is provided as the
 *   second parameter to the callback.
 * @param {Object} hashes - An object of hashes that identify the file to
 *   lookup.  The `key` for each property is the `hash type` and the
 *   corresponding `value` is the `hex` representation of the hash value. See
 *   the [HashType]{@link module:HashType} module for the list of `hash type`
 *   constants.
 * @param {Number} [queryLimit=500] - The maximum number of results to return.
 */
TieClient.prototype.getFileFirstReferences =
  function (callback, hashes, queryLimit) {
    this._getFirstReferences(callback, TIE_GET_FILE_FIRST_REFS_TOPIC, hashes,
      {}, queryLimit)
  }

/**
 * Retrieves the reputations for the specified certificate (as identified by the
 * SHA-1 of the certificate and optionally the SHA-1 of the certificate's public
 * key)
 *
 * While the SHA-1 of the certificate is required, passing the optional SHA-1 of
 * the certificate's public key can result in additional reputations being
 * located across the set of `certificate reputation providers`.
 *
 * **Reputations**
 *
 * The `Reputations` for the file is provided as an object via the second
 * parameter delivered to the callback.
 *
 * The `key` for each property in the object corresponds to a particular
 * `provider` of the associated `reputation`. The list of `file reputation
 * providers` can be found in the [FileProvider]{@link module:FileProvider}
 * constants module.
 *
 * An example reputations object is shown below:
 *
 * ```js
 * {
 *   "2": {
 *     "attributes": {
 *       "2108821": "92",
 *       "2109077": "1454912619",
 *       "2117524": "0",
 *       "2120596": "0"
 *     },
 *     "createDate": 1476318514,
 *     "providerId": 2,
 *     "trustLevel": 99
 *   },
 *   "4": {
 *     "attributes": {
 *       "2109333": "4",
 *       "2109589": "1476318514",
 *       "2139285": "73183493944770750"
 *     },
 *     "createDate": 1476318514,
 *     "providerId": 4,
 *     "trustLevel": 0
 *   }
 * }
 * ```
 *
 * The `"2"` `key` corresponds to a reputation from the
 * "Global Threat Intelligence (GTI)" reputation provider while the `"4"` `key`
 * corresponds to a reputation from the "Enterprise" reputation provider.
 *
 * Each reputation contains a standard set of properties (trust level, creation
 * date, etc.). These properties are listed in the
 * [CertReputationProp]{@link module:CertReputationProp} constants module.
 *
 * The following example shows how to access the `trust level` property of the
 * "Enterprise" reputation:
 *
 * ```js
 * var trustLevel = reputationsObj[CertProvider.ENTERPRISE][CertReputationProp.TRUST_LEVEL]
 * ```
 *
 * Each reputation can also contain a provider-specific set of attributes as an
 * object. These attributes can be found in the following modules:
 *
 * | Module                                                    | Description |
 * | --------------------------------------------------------- | ----------- |
 * | [CertEnterpriseAttrib]{@link module:CertEnterpriseAttrib} | Attributes associated with the `Enterprise` reputation provider for certificates. |
 * | [CertGtiAttrib]{@link module:CertGtiAttrib}               | Attributes associated with the `Global Threat Intelligence (GTI)` reputation provider for certificates. |
 *
 * The following example shows how to access the `prevalence` attribute from the
 * "Enterprise" reputation:
 *
 * ```js
 * var entRep = reputationsObj[CertProvider.ENTERPRISE]
 * var entRepAttribs = entRep[CertReputationProp.ATTRIBUTES]
 * var prevalence = Number(entRepAttribs[CertEnterpriseAttrib.PREVALENCE])
 * ```
 * @example
 * var CERTIFICATE_BODY_SHA1 = '6eae26db8c13182a7947982991b4321732cc3de2'
 * var CERTIFICATE_PUBLIC_KEY_SHA1 = '3b87a2d6f39770160364b79a152fcc73bae27adf'
 * tieClient.getCertReputation(
 *   function (error, reputations) {
 *     if (error) {
 *       console.log('Error getting certificate reputations: ' + error.message)
 *     } else {
 *       console.log('Certificate reputations:')
 *       console.log(MessageUtils.objectToJson(reputations, true))
 *     }
 *   },
 *   CERTIFICATE_BODY_SHA1,
 *   CERTIFICATE_PUBLIC_KEY_SHA1
 * )
 * @param {Function} callback - Callback function to invoke with the results
 *   of the reputation lookup. If an error occurs when performing the
 *   lookup, the first parameter supplied to the callback contains an `Error`
 *   object with failure details. On successful lookup, a reputations object is
 *   provided as the second parameter to the callback. Each property `value`
 *   in the reputations object is a reputation from a particular
 *   `reputation provider`, which is identified by the key. The list of
 *   `certificate reputation providers` can be found in the
 *   [CertProvider]{@link module:CertProvider} constants module.
 * @param {String} sha1 - The SHA-1 of the certificate.
 * @param {String} [publicKeySha1] - The SHA-1 of the certificate's public
 *   key.
 */
TieClient.prototype.getCertificateReputation =
  function (callback, sha1, publicKeySha1) {
    var payload = publicKeySha1 ? {publicKeySha1: hexToBase64(publicKeySha1)} : {}
    var hashes = {}
    hashes[HashType.SHA1] = sha1
    this._getReputation(callback, TIE_GET_CERT_REPUTATION_TOPIC, hashes, payload)
  }

/**
 * Sets the "Enterprise" reputation (`trust level`) of a specified certificate
 * (as identified by hashes).
 *
 * **Client Authorization**
 *
 * The OpenDXL Python client invoking this method must have permission to send
 * messages to the `/mcafee/service/tie/cert/reputation/set` topic, which is
 * part of the `TIE Server Set Enterprise Reputation` authorization group.
 *
 * The following page provides an example of authorizing a Python client to
 * send messages to an `authorization group`. While the example is based on
 * McAfee Active Response (MAR), the instructions are the same with the
 * exception of swapping the `TIE Server Set Enterprise Reputation`
 * `authorization group` in place of `Active Response Server API`:
 *
 * <https://opendxl.github.io/opendxl-client-python/pydoc/marsendauth.html>
 * @example
 * var CERTIFICATE_BODY_SHA1 = '6eae26db8c13182a7947982991b4321732cc3de2'
 * var CERTIFICATE_PUBLIC_KEY_SHA1 = '3b87a2d6f39770160364b79a152fcc73bae27adf'
 * tieClient.setCertificateReputation(
 *   function (error) {
 *     if (error) {
 *       console.log('Error: ' + error.message)
 *     } else {
 *       console.log('Succeeded')
 *     }
 *   },
 *   TrustLevel.KNOWN_TRUSTED,
 *   CERTIFICATE_BODY_SHA1,
 *   CERTIFICATE_PUBLIC_KEY_SHA1,
 *   'Reputation set via OpenDXL'
 * )
 * @param {Function} callback - Callback to invoke after setting the certificate
 *   reputation. If an error occurs when attempting to set the reputation, the
 *   first parameter supplied to the callback contains an `Error` object with
 *   failure details. If no error occurs, the first parameter supplied to the
 *   callback is `null`.
 * @param {Number} trustLevel - The new `trust level` for the certificate. The
 *   list of standard `trust levels` can be found in the
 *   [TrustLevel]{@link module:TrustLevel} constants module.
 * @param {String} sha1 - The SHA-1 of the certificate.
 * @param {String} [publicKeySha1] - The SHA-1 of the certificate's public
 *   key.
 * @param {String} [comment] - A comment to associate with the certificate.
 */
TieClient.prototype.setCertificateReputation =
  function (callback, trustLevel, sha1, publicKeySha1, comment) {
    var payload = publicKeySha1 ? {publicKeySha1: hexToBase64(publicKeySha1)} : {}
    var hashes = {}
    hashes[HashType.SHA1] = sha1
    this._setReputation(callback, TIE_SET_CERT_REPUTATION_TOPIC,
      trustLevel, CertProvider.ENTERPRISE, hashes, payload, comment)
  }

/**
 * Retrieves the set of systems which have referenced the specified certificate
 * (as identified by hashes).
 *
 * **Systems**
 *
 * The systems that have referenced the file is provided as an array of objects
 * via the second parameter delivered to the callback.
 *
 * An example array is shown below:
 *
 * ```js
 * [
 *   {
 *     "agentGuid": "{3a6f574a-3e6f-436d-acd4-bcde336b054d}",
 *     "date": 1475873692
 *   },
 *   {
 *     "agentGuid": "{68125cd6-a5d8-11e6-348e-000c29663178}",
 *     "date": 1478626172
 *   }
 * ]
 * ```
 *
 * Each entry in the array is an object containing details about a system that
 * has referenced the certificate. See the
 * [FirstRefProp]{@link module:FirstRefProp} constants module for details about
 * the information that is available for each system in the object.
 * @example
 * var CERTIFICATE_BODY_SHA1 = '6eae26db8c13182a7947982991b4321732cc3de2'
 * var CERTIFICATE_PUBLIC_KEY_SHA1 = '3b87a2d6f39770160364b79a152fcc73bae27adf'
 *
 * tieClient.getCertificateFirstReferences(
 *   function (error, systems) {
 *     if (error) {
 *       console.log('Error getting certificate references: ' + error.message)
 *     } else {
 *       console.log('\nSystems that have referenced the certificate:\n')
 *       systems.forEach(function (system) {
 *         console.log('\t' + system[FirstRefProp.SYSTEM_GUID] + ': ' +
 *           EpochUtil.toLocalTimeString(system[FirstRefProp.DATE])
 *         )
 *       })
 *     }
 *   },
 *   CERTIFICATE_BODY_SHA1,
 *   CERTIFICATE_PUBLIC_KEY_SHA1
 * )
 * @param {Function} callback - Callback function to invoke with the results
 *   of the first references lookup. If an error occurs when performing the
 *   lookup, the first parameter supplied to the callback contains an `Error`
 *   object with failure details. On successful lookup, an array containing an
 *   object for each system that has referenced the certificate is provided as
 *   the second parameter to the callback.
 * @param {String} sha1 - The SHA-1 of the certificate.
 * @param {String} [publicKeySha1] - The SHA-1 of the certificate's
 *   public key.
 * @param {Number} [queryLimit=500] - The maximum number of results to return.
 */
TieClient.prototype.getCertificateFirstReferences =
  function (callback, sha1, publicKeySha1, queryLimit) {
    var payload = publicKeySha1 ? {publicKeySha1: hexToBase64(publicKeySha1)} : {}
    var hashes = {}
    hashes[HashType.SHA1] = sha1
    this._getFirstReferences(callback, TIE_GET_CERT_FIRST_REFS_TOPIC, hashes,
      payload, queryLimit)
  }

module.exports = TieClient