diff options
Diffstat (limited to 'lang/js')
| -rw-r--r-- | lang/js/README | 29 | ||||
| -rw-r--r-- | lang/js/src/Connection.js | 58 | ||||
| -rw-r--r-- | lang/js/src/Errors.js | 18 | ||||
| -rw-r--r-- | lang/js/src/Helpers.js | 34 | ||||
| -rw-r--r-- | lang/js/src/Key.js | 145 | ||||
| -rw-r--r-- | lang/js/src/Keyring.js | 132 | ||||
| -rw-r--r-- | lang/js/src/Message.js | 52 | ||||
| -rw-r--r-- | lang/js/src/Signature.js | 29 | ||||
| -rw-r--r-- | lang/js/src/gpgmejs.js | 146 | ||||
| -rw-r--r-- | lang/js/src/index.js | 4 | ||||
| -rw-r--r-- | lang/js/src/permittedOperations.js | 43 | 
11 files changed, 456 insertions, 234 deletions
| diff --git a/lang/js/README b/lang/js/README index 86d2616a..b7cd3d72 100644 --- a/lang/js/README +++ b/lang/js/README @@ -1,10 +1,17 @@ -gpgmejs, as contained in this directory, is a javascript library for direct use +gpgme.js, as contained in this directory, is a javascript library for direct use  of gnupg in browsers, with the help of nativeMessaging. -Installation -------------- -gpgmejs uses webpack, and thus depends on nodejs for building. All dependencies -will be installed (in a local subdirectory) with the command `npm install`. +Prerequisites: +-------------- +gpgme.js will make use of the application gpgme-json, which is distributed with +gpgme. Gpgme-json needs to be installed; it will further need to accept the +browser extension in the manifest file. + +Building gpgme.js +----------------- +gpgme.js uses webpack, and thus depends on nodejs for building. All +dependencies will be installed (in a local subdirectory) with the command +`npm install`.  To create a current version of the package, the command is  `npx webpack --config webpack.conf.js`. @@ -14,7 +21,7 @@ in webpack.conf.js.  Demo and Test WebExtension:  --------------------------- -The Demo Extension shows simple examples of the usage of gpgmejs. +The Demo Extension shows simple examples of the usage of gpgme.js.  The BrowsertestExtension runs more intensive tests (using the mocha and chai  frameworks). Tests from BrowserTestExtension/tests will be run against the @@ -22,11 +29,11 @@ gpgmejs.bundle.js itself. They aim to test the outward facing functionality  and API.  Unittests as defined in ./unittests.js will be bundled in -gpgmejs_unittests.bundle.js, and test the separate components of gpgmejs, +gpgmejs_unittests.bundle.js, and test the separate components of gpgme.js,  which mostly are not exported. -. The file `build_extension.sh` may serve as a pointer on how to -build and assemble these two Extensions and their dependencies. It can directly +The file `build_extension.sh` may serve as a pointer on how to build and +assemble these two Extensions and their dependencies. It can directly  be used in most linux systems.  The resulting folders can just be included in the extensions tab of the browser @@ -46,7 +53,7 @@ is needed, with the following content:    ```    {      "name": "gpgmejson", -    "description": "This is a test application for gpgmejs", +    "description": "This is a test application for gpgme.js",      "path": "/usr/bin/gpgme-json",      "type": "stdio",      "allowed_origins": ["chrome-extension://ExtensionIdentifier/"] @@ -61,7 +68,7 @@ is needed, with the following content:    ```    {      "name": "gpgmejson", -    "description": "This is a test application for gpgmejs", +    "description": "This is a test application for gpgme.js",      "path": "/usr/bin/gpgme-json",      "type": "stdio",      "allowed_extensions": ["ExtensionIdentifier@temporary-addon"] diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index 945932e9..ef54dd64 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -28,7 +28,12 @@ import { gpgme_error } from './Errors';  import { GPGME_Message, createMessage } from './Message';  /** - * A Connection handles the nativeMessaging interaction. + * A Connection handles the nativeMessaging interaction via a port. As the + * protocol only allows up to 1MB of message sent from the nativeApp to the + * browser, the connection will stay open until all parts of a communication + * are finished. For a new request, a new port will open, to avoid mixing + * contexts. + * @class   */  export class Connection{ @@ -37,19 +42,26 @@ export class Connection{      }      /** -     * Retrieves the information about the backend. -     * @param {Boolean} details (optional) If set to false, the promise will -     *  just return a connection status -     * @returns {Promise<Object>} result -     * @returns {String} result.gpgme Version number of gpgme -     * @returns {Array<Object>} result.info Further information about the -     * backends. -     *      Example: +     * @typedef {Object} backEndDetails +     * @property {String} gpgme Version number of gpgme +     * @property {Array<Object>} info Further information about the backend +     * and the used applications (Example: +     * {       *          "protocol":     "OpenPGP",       *          "fname":        "/usr/bin/gpg",       *          "version":      "2.2.6",       *          "req_version":  "1.4.0",       *          "homedir":      "default" +     * } +     */ + +    /** +     * Retrieves the information about the backend. +     * @param {Boolean} details (optional) If set to false, the promise will +     *  just return if a connection was successful. +     * @returns {Promise<backEndDetails>|Promise<Boolean>} Details from the +     * backend +     * @async       */      checkConnection(details = true){          if (details === true) { @@ -74,7 +86,7 @@ export class Connection{      }      /** -     * Immediately closes the open port. +     * Immediately closes an open port.       */      disconnect() {          if (this._connection){ @@ -93,10 +105,13 @@ export class Connection{      }      /** -     * Sends a message and resolves with the answer. +     * Sends a {@link GPGME_Message} via tghe nativeMessaging port. It resolves +     * with the completed answer after all parts have been received and +     * reassembled, or rejects with an {@link GPGME_Error}. +     *       * @param {GPGME_Message} message -     * @returns {Promise<Object>} the gnupg answer, or rejection with error -     * information. +     * @returns {Promise<Object>} The collected answer +     * @async       */      post(message){          if (!message || !(message instanceof GPGME_Message)){ @@ -170,16 +185,25 @@ export class Connection{  /**   * A class for answer objects, checking and processing the return messages of   * the nativeMessaging communication. - * @param {String} operation The operation, to look up validity of returning - * messages + * @protected   */  class Answer{ +    /** +     * @param {GPGME_Message} message +     */      constructor(message){          this.operation = message.operation;          this.expect = message.expect;      } +    /** +     * Adds incoming base64 encoded data to the existing response +     * @param {*} msg base64 encoded data. +     * @returns {Boolean} +     * +     * @private +     */      collect(msg){          if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) {              return gpgme_error('CONN_UNEXPECTED_ANSWER'); @@ -195,6 +219,10 @@ class Answer{          }      } +    /** +     * Returns the base64 encoded answer data with the content verified against +     * {@link permittedOperations}. +     */      get message(){          if (this._responseb64 === undefined){              return gpgme_error('CONN_UNEXPECTED_ANSWER'); diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index a8cd8b56..cb5c94c2 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -21,6 +21,9 @@   *     Maximilian Krambach <[email protected]>   */ +/** + * Listing of all possible error codes and messages of a {@link GPGME_Error}. + */  const err_list = {      // Connection      'CONN_NO_CONNECT': { @@ -107,10 +110,11 @@ const err_list = {  };  /** - * Checks the given error code and returns an error object with some - * information about meaning and origin + * Checks the given error code and returns an {@link GPGME_Error} error object + * with some information about meaning and origin   * @param {*} code Error code. Should be in err_list or 'GNUPG_ERROR'   * @param {*} info Error message passed through if code is 'GNUPG_ERROR' + * @returns {GPGME_Error}   */  export function gpgme_error(code = 'GENERIC_ERROR', info){      if (err_list.hasOwnProperty(code)){ @@ -119,7 +123,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){          }          if (err_list[code].type === 'warning'){              // eslint-disable-next-line no-console -            console.warn(code + ': ' + err_list[code].msg); +            // console.warn(code + ': ' + err_list[code].msg);          }          return null;      } else if (code === 'GNUPG_ERROR'){ @@ -130,6 +134,14 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){      }  } +/** + * An error class with additional info about the origin of the error, as string + * @property {String} code Short description of origin and type of the error + * @property {String} msg Additional info + * @class + * @protected + * @extends Error + */  class GPGME_Error extends Error{      constructor(code, msg=''){          if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){ diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js index b01fbc30..0fd14994 100644 --- a/lang/js/src/Helpers.js +++ b/lang/js/src/Helpers.js @@ -26,11 +26,11 @@ import { GPGME_Key } from './Key';  /**   * Tries to return an array of fingerprints, either from input fingerprints or - * from Key objects (openpgp Keys or GPGME_Keys are both expected) - * @param {Object |Array<Object>| String|Array<String>} input - * @returns {Array<String>} Array of fingerprints. + * from Key objects (openpgp Keys or GPGME_Keys are both accepted). + * + * @param {Object | Array<Object> | String | Array<String>} input + * @returns {Array<String>} Array of fingerprints, or an empty array   */ -  export function toKeyIdArray(input){      if (!input){          return []; @@ -44,6 +44,8 @@ export function toKeyIdArray(input){              if (isFingerprint(input[i]) === true){                  result.push(input[i]);              } else { +                // MSG_NOT_A_FPR is just a console warning if warning enabled +                // in src/Errors.js                  gpgme_error('MSG_NOT_A_FPR');              }          } else if (typeof(input[i]) === 'object'){ @@ -71,9 +73,11 @@ export function toKeyIdArray(input){  }  /** - * check if values are valid hexadecimal values of a specified length - * @param {*} key input value. + * Check if values are valid hexadecimal values of a specified length + * @param {String} key input value.   * @param {int} len the expected length of the value + * @returns {Boolean} true if value passes test + * @private   */  function hextest(key, len){      if (!key || typeof(key) !== 'string'){ @@ -87,15 +91,21 @@ function hextest(key, len){  }  /** - * check if the input is a valid Hex string with a length of 40 + * check if the input is a valid Fingerprint + *      (Hex string with a length of 40 characters) + * @param {String} value to check + * @returns {Boolean} true if value passes test   */ -export function isFingerprint(string){ -    return hextest(string, 40); +export function isFingerprint(value){ +    return hextest(value, 40);  }  /** - *  check if the input is a valid Hex string with a length of 16 + * check if the input is a valid gnupg long ID (Hex string with a length of 16 + * characters) + * @param {String} value to check + * @returns {Boolean} true if value passes test   */ -export function isLongId(string){ -    return hextest(string, 16); +export function isLongId(value){ +    return hextest(value, 16);  } diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index 88c2b92f..30f507c1 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -26,8 +26,9 @@ import { gpgme_error } from './Errors';  import { createMessage } from './Message';  /** - * Validates the fingerprint. + * Validates the given fingerprint and creates a new {@link GPGME_Key}   * @param {String} fingerprint + * @returns {GPGME_Key|GPGME_Error}   */  export function createKey(fingerprint){      if (!isFingerprint(fingerprint)){ @@ -37,12 +38,13 @@ export function createKey(fingerprint){  }  /** - * Representing the Keys as stored in GPG + * Represents the Keys as stored in the gnupg backend   * It allows to query almost all information defined in gpgme Key Objects - * Refer to validKeyProperties for available information, and the gpgme + * Refer to {@link validKeyProperties} for available information, and the gpgme   * documentation on their meaning   * (https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)   * + * @class   */  export class GPGME_Key { @@ -62,6 +64,9 @@ export class GPGME_Key {          }      } +    /** +     * @returns {String} The fingerprint defining this Key +     */      get fingerprint(){          if (!this._data || !this._data.fingerprint){              return gpgme_error('KEY_INVALID'); @@ -70,11 +75,11 @@ export class GPGME_Key {      }      /** -     * -     * @param {Object} data Bulk set data for this key, with the Object as sent +     * @param {Object} data Bulk set the data for this key, with an Object sent       * by gpgme-json. -     * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have -     * been set +     * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, an +     * error if something went wrong +     * @private       */      setKeyData(data){          if (this._data === undefined) { @@ -126,12 +131,17 @@ export class GPGME_Key {      }      /** -     * Query any property of the Key list -     * @param {String} property Key property to be retreived -     * @param {*} cached (optional) if false, the data will be directly queried -     * from gnupg. -     *  @returns {*|Promise<*>} the value, or if not cached, a Promise -     * resolving on the value +     * Query any property of the Key listed in {@link validKeyProperties} +     * @param {String} property property to be retreived +     * @param {Boolean} cached (optional) if false, the data will be directly +     * queried from gnupg, and the operation will be asynchronous. Else, the +     * data will be fetched from the state of the initialization of the Key. +     * The cached mode may contain outdated information, but can be used as +     * synchronous operation, where the backend is not expected to change Keys +     * during a session. The key still can be reloaded by invoking +     * {@link refreshKey}. +     * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). +     * If 'cached' is true, the value will be resolved as a Promise.       */      get(property, cached=true) {          if (cached === false) { @@ -164,7 +174,12 @@ export class GPGME_Key {      }      /** -     * Reloads the Key from gnupg +     * Reloads the Key information from gnupg. This is only useful if you use +     * the GPGME_Keys cached. Note that this is a performance hungry operation. +     * If you desire more than a few refreshs, it may be advisable to run +     * {@link Keyring.getKeys} instead. +     * @returns {Promise<GPGME_Key|GPGME_Error>} +     * @async       */      refreshKey() {          let me = this; @@ -178,7 +193,12 @@ export class GPGME_Key {              msg.post().then(function(result){                  if (result.keys.length === 1){                      me.setKeyData(result.keys[0]); -                    resolve(me); +                    me.getHasSecret().then(function(){ +                        //TODO retrieve armored Key +                        resolve(me); +                    }, function(error){ +                        reject(error); +                    });                  } else {                      reject(gpgme_error('KEY_NOKEY'));                  } @@ -189,9 +209,9 @@ export class GPGME_Key {      }      /** -     * Query the armored block of the non- secret parts of the Key directly -     * from gpg. -     * @returns {Promise<String>} +     * Query the armored block of the Key directly from gnupg. Please note that +     * this will not get you any export of the secret/private parts of a Key +     * @returns {Promise<String|GPGME_Error>}       * @async       */      getArmor(){ @@ -213,9 +233,12 @@ export class GPGME_Key {      }      /** -     * Find out if the Key includes a secret part -     * @returns {Promise<Boolean>} -     * +     * Find out if the Key includes a secret part. Note that this is a rather +     * nonperformant operation, as it needs to query gnupg twice. If you want +     * this inforrmation about more than a few Keys, it may be advisable to run +     * {@link Keyring.getKeys} instead. +     * @returns {Promise<Boolean|GPGME_Error>} True if a secret/private Key is +     * available in the gnupg Keyring       * @async       */      getHasSecret(){ @@ -253,25 +276,30 @@ export class GPGME_Key {       */      /** -     * @returns {String} The armored public Key block +     * Property for the export of armored Key. If the armored Key is not +     * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. +     * Running {@link refreshKey} may help in this case. +     * @returns {String|GPGME_Error} The armored public Key block.       */      get armored(){          return this.get('armored', true);      }      /** -     * @returns {Boolean} If the key is considered a "private Key", -     * i.e. owns a secret subkey. +     * Property indicating if the Key possesses a private/secret part. If this +     * information is not yet cached, it returns an {@link GPGME_Error} with +     * code 'KEY_NO_INIT'.  Running {@link refreshKey} may help in this case. +     * @returns {Boolean} If the Key has a secret subkey.       */      get hasSecret(){          return this.get('hasSecret', true);      }      /** -     * Deletes the public Key from the GPG Keyring. Note that a deletion of a +     * Deletes the (public) Key from the GPG Keyring. Note that a deletion of a       * secret key is not supported by the native backend. -     * @returns {Promise<Boolean>} Success if key was deleted, rejects with a -     * GPG error otherwise +     * @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted, +     * rejects with a GPG error otherwise.       */      delete(){          let me = this; @@ -291,10 +319,17 @@ export class GPGME_Key {  }  /** - * The subkeys of a Key. Currently, they cannot be refreshed separately + * Representing a subkey of a Key. + * @class + * @protected   */  class GPGME_Subkey { +    /** +     * Initializes with the json data sent by gpgme-json +     * @param {Object} data +     * @private +     */      constructor(data){          let keys = Object.keys(data);          for (let i=0; i< keys.length; i++) { @@ -302,6 +337,13 @@ class GPGME_Subkey {          }      } +    /** +     * Validates a subkey property against {@link validSubKeyProperties} and +     * sets it if validation is successful +     * @param {String} property +     * @param {*} value +     * @param private +     */      setProperty(property, value){          if (!this._data){              this._data = {}; @@ -318,10 +360,9 @@ class GPGME_Subkey {      }      /** -     * +     * Fetches any information about this subkey       * @param {String} property Information to request -     * @returns {String | Number} -     * TODO: date properties are numbers with Date in seconds +     * @returns {String | Number | Date}       */      get(property) {          if (this._data.hasOwnProperty(property)){ @@ -330,15 +371,31 @@ class GPGME_Subkey {      }  } +/** + * Representing user attributes associated with a Key or subkey + * @class + * @protected + */  class GPGME_UserId { +    /** +     * Initializes with the json data sent by gpgme-json +     * @param {Object} data +     * @private +     */      constructor(data){          let keys = Object.keys(data);          for (let i=0; i< keys.length; i++) {              this.setProperty(keys[i], data[keys[i]]);          }      } - +    /** +     * Validates a subkey property against {@link validUserIdProperties} and +     * sets it if validation is successful +     * @param {String} property +     * @param {*} value +     * @param private +     */      setProperty(property, value){          if (!this._data){              this._data = {}; @@ -356,10 +413,9 @@ class GPGME_UserId {      }      /** -     * +     * Fetches information about the user       * @param {String} property Information to request       * @returns {String | Number} -     * TODO: date properties are numbers with Date in seconds       */      get(property) {          if (this._data.hasOwnProperty(property)){ @@ -368,6 +424,13 @@ class GPGME_UserId {      }  } +/** + * Validation definition for userIds. Each valid userId property is represented + * as a key- Value pair, with their value being a validation function to check + * against + * @protected + * @const + */  const validUserIdProperties = {      'revoked': function(value){          return typeof(value) === 'boolean'; @@ -419,6 +482,12 @@ const validUserIdProperties = {      }  }; +/** + * Validation definition for subKeys. Each valid userId property is represented + * as a key-value pair, with the value being a validation function + * @protected + * @const + */  const validSubKeyProperties = {      'invalid': function(value){          return typeof(value) === 'boolean'; @@ -471,8 +540,14 @@ const validSubKeyProperties = {          return (Number.isInteger(value) && value > 0);      }  }; + +/** + * Validation definition for Keys. Each valid Key property is represented + * as a key-value pair, with their value being a validation function + * @protected + * @const + */  const validKeyProperties = { -    //TODO better validation?      'fingerprint': function(value){          return isFingerprint(value);      }, diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index 09c43f73..a0bdfcb2 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -27,24 +27,30 @@ import {createKey} from './Key';  import { isFingerprint } from './Helpers';  import { gpgme_error } from './Errors'; +/** + * This class offers access to the gnupg keyring + */  export class GPGME_Keyring {      constructor(){      }      /** -     * @param {String} pattern (optional) pattern A pattern to search for, -     * in userIds or KeyIds -     * @param {Boolean} prepare_sync (optional, default true) if set to true, -     * Key.armor and Key.hasSecret will be called, so they can be used -     * inmediately. This allows for full synchronous use. If set to false, -     * these will initially only be available as Promises in getArmor() and -     * getHasSecret() -     * @param {Boolean} search (optional) retrieve the Keys from servers with -     * the method(s) defined in gnupg (e.g. WKD/HKP lookup) -     * @returns {Promise.<Array<GPGME_Key>>} +     * Queries Keys (all Keys or a subset) from gnupg.       * +     * @param {String | Array<String>} pattern (optional) A pattern to search +     * for in userIds or KeyIds. +     * @param {Boolean} prepare_sync (optional) if set to true, the 'hasSecret' +     * and 'armored' properties will be fetched for the Keys as well. These +     * require additional calls to gnupg, resulting in a performance hungry +     * operation. Calling them here enables direct, synchronous use of these +     * properties for all keys, without having to resort to a refresh() first. +     * @param {Boolean} search (optional) retrieve Keys from external servers +     * with the method(s) defined in gnupg (e.g. WKD/HKP lookup) +     * @returns {Promise.<Array<GPGME_Key>|GPGME_Error>} +     * @static +     * @async       */ -    getKeys(pattern, prepare_sync, search){ +    getKeys(pattern, prepare_sync=false, search=false){          return new Promise(function(resolve, reject) {              let msg = createMessage('keylist');              if (pattern !== undefined){ @@ -102,10 +108,15 @@ export class GPGME_Keyring {      }      /** -     * Fetches the armored public Key blocks for all Keys matchin the pattern -     * (if no pattern is given, fetches all known to gnupg) -     * @param {String|Array<String>} pattern (optional) -     * @returns {Promise<String>} Armored Key blocks +     * Fetches the armored public Key blocks for all Keys matching the pattern +     * (if no pattern is given, fetches all keys known to gnupg). Note that the +     * result may be one big armored block, instead of several smaller armored +     * blocks +     * @param {String|Array<String>} pattern (optional) The Pattern to search +     * for +     * @returns {Promise<String|GPGME_Error>} Armored Key blocks +     * @static +     * @async       */      getKeysArmored(pattern) {          return new Promise(function(resolve, reject) { @@ -123,15 +134,14 @@ export class GPGME_Keyring {      }      /** -     * Returns the Key to be used by default for signing operations, -     * looking up the gpg configuration, or returning the first key that -     * contains a secret key. -     * @returns {Promise<GPGME_Key>} -     * +     * Returns the Key used by default in gnupg. +     * (a.k.a. 'primary Key or 'main key'). +     * It looks up the gpg configuration if set, or the first key that contains +     * a secret key.       * -     * TODO: getHasSecret always returns false at this moment, so this fucntion -     * still does not fully work as intended. -     * * @async +     * @returns {Promise<GPGME_Key|GPGME_Error>} +     * @async +     * @static       */      getDefaultKey() {          let me = this; @@ -177,30 +187,40 @@ export class GPGME_Keyring {      }      /** +     * @typedef {Object} importResult The result of a Key update +     * @property {Object} summary Numerical summary of the result. See the +     * feedbackValues variable for available Keys values and the gnupg +     * documentation. +     * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html +     * for details on their meaning. +     * @property {Array<importedKeyResult>} Keys Array of Object containing +     * GPGME_Keys with additional import information       * +     */ + +    /** +     * @typedef {Object} importedKeyResult +     * @property {GPGME_Key} key The resulting key +     * @property {String} status: +     *  'nochange' if the Key was not changed, +     *  'newkey' if the Key was imported in gpg, and did not exist previously, +     *  'change' if the key existed, but details were updated. For details, +     *    Key.changes is available. +     * @property {Boolean} changes.userId Changes in userIds +     * @property {Boolean} changes.signature Changes in signatures +     * @property {Boolean} changes.subkey Changes in subkeys +     */ + +    /** +     * Import an armored Key block into gnupg. Note that this currently will +     * not succeed on private Key blocks.       * @param {String} armored Armored Key block of the Key(s) to be imported       * into gnupg       * @param {Boolean} prepare_sync prepare the keys for synched use -     * (see getKeys()). -     * -     * @returns {Promise<Object>} result: A summary and an array of Keys -     * considered -     * -     * @returns result.summary: Numerical summary of the result. See the -     * feedbackValues variable for available values and the gnupg documentation -     * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html -     * for details on their meaning. -     * @returns {Array<Object>} result.Keys: Array of objects containing: -     * @returns {GPGME_Key} Key.key The resulting key -     * @returns {String} Key.status: -     *      'nochange' if the Key was not changed, -     *      'newkey' if the Key was imported in gpg, and did not exist -     *         previously, -     *      'change' if the key existed, but details were updated. For -     *         details, Key.changes is available. -     * @returns {Boolean} Key.changes.userId: userIds changed -     * @returns {Boolean} Key.changes.signature: signatures changed -     * @returns {Boolean} Key.changes.subkey: subkeys changed +     * (see {@link getKeys}). +     * @returns {Promise<importResult>} A summary and Keys considered. +     * @async +     * @static       */      importKey(armored, prepare_sync) {          let feedbackValues = ['considered', 'no_user_id', 'imported', @@ -283,25 +303,36 @@ export class GPGME_Keyring {      } +    /** +     * Convenience function for deleting a Key. See {@link Key.delete} for +     * further information about the return values. +     * @param {String} fingerprint +     * @returns {Promise<Boolean|GPGME_Error>} +     * @async +     * @static +     */      deleteKey(fingerprint){          if (isFingerprint(fingerprint) === true) {              let key = createKey(fingerprint); -            key.delete(); +            return key.delete(); +        } else { +            return Promise.reject(gpgme_error('KEY_INVALID'));          }      }      /**       * Generates a new Key pair directly in gpg, and returns a GPGME_Key       * representing that Key. Please note that due to security concerns, secret -     * Keys can not be _deleted_ from inside gpgmejs. +     * Keys can not be deleted or exported from inside gpgme.js.       * -     * @param {String} userId The user Id, e.g. "Foo Bar <[email protected]>" -     * @param {*} algo (optional) algorithm (and optionally key size to be -     *  used. See {@link supportedKeyAlgos } below for supported values. +     * @param {String} userId The user Id, e.g. 'Foo Bar <[email protected]>' +     * @param {String} algo (optional) algorithm (and optionally key size) to +     * be used. See {@link supportedKeyAlgos} below for supported values.       * @param {Date} expires (optional) Expiration date. If not set, expiration       * will be set to 'never'       * -     * @return {Promise<Key>} +     * @return {Promise<Key|GPGME_Error>} +     * @async       */      generateKey(userId, algo = 'default', expires){          if ( @@ -336,7 +367,8 @@ export class GPGME_Keyring {  }  /** - * A list of algorithms supported for key generation. + * List of algorithms supported for key generation. Please refer to the gnupg + * documentation for details   */  const supportedKeyAlgos = [      'default', diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index 7ccf7efc..2e38142c 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -25,6 +25,12 @@ import { permittedOperations } from './permittedOperations';  import { gpgme_error } from './Errors';  import { Connection } from './Connection'; +/** + * Initializes a message for gnupg, validating the message's purpose with + *   {@link permittedOperations} first + * @param {String} operation + * @returns {GPGME_Message|GPGME_Error} The Message object + */  export function createMessage(operation){      if (typeof(operation) !== 'string'){          return gpgme_error('PARAM_WRONG'); @@ -37,12 +43,13 @@ export function createMessage(operation){  }  /** - * Prepares a communication request. It checks operations and parameters in - * ./permittedOperations. - * @param {String} operation + * A Message collects, validates and handles all information required to + * successfully establish a meaningful communication with gpgme-json via + * {@link Connection.post}. The definition on which communication is available + * can be found in {@link permittedOperations}. + * @class   */  export class GPGME_Message { -    //TODO getter      constructor(operation){          this.operation = operation; @@ -63,9 +70,13 @@ export class GPGME_Message {      }      /** -     * Set the maximum size of responses from gpgme in bytes. Values allowed -     * range from 10kB to 1MB. The lower limit is arbitrary, the upper limit -     * fixed by browsers' nativeMessaging specifications +     * The maximum size of responses from gpgme in bytes. As of July 2018, +     * most browsers will only accept answers up to 1 MB of size. Everything +     * above that threshold will not pass through nativeMessaging; answers that +     * are larger need to be sent in parts. The lower limit is set to 10 KB. +     * Messages smaller than the threshold will not encounter problems, larger +     * messages will be received in chunks. +     * If the value is not explicitly specified, 1023 KB is used.       */      set chunksize(value){          if ( @@ -85,8 +96,9 @@ export class GPGME_Message {      }      /** -     * If expect is set to 'base64', the response is expected to be base64 -     * encoded binary +     * Expect indicates which format data is expected to be in. It currently +     * only supports 'base64', indicating that the response data from gnupg are +     * expected to be base64 encoded binary       */      set expect(value){          if (value ==='base64'){ @@ -103,13 +115,13 @@ export class GPGME_Message {      /** -     * Sets a parameter for the message. Note that the operation has to be set -     * first, to be able to check if the parameter is permittted +     * Sets a parameter for the message. It validates with +     *      {@link permittedOperations}       * @param {String} param Parameter to set -     * @param {any} value Value to set //TODO: Some type checking +     * @param {any} value Value to set       * @returns {Boolean} If the parameter was set successfully       */ -    setParameter(param,value){ +    setParameter( param,value ){          if (!param || typeof(param) !== 'string'){              return gpgme_error('PARAM_WRONG');          } @@ -125,6 +137,7 @@ export class GPGME_Message {          } else {              return gpgme_error('PARAM_WRONG');          } +        // check incoming value for correctness          let checktype = function(val){              switch(typeof(val)){              case 'string': @@ -187,9 +200,9 @@ export class GPGME_Message {      }      /** -     * Check if the message has the minimum requirements to be sent, according -     * to the definitions in permittedOperations -     * @returns {Boolean} +     * Check if the message has the minimum requirements to be sent, that is +     * all 'required' parameters according to {@link permittedOperations}. +     * @returns {Boolean} true if message is complete.       */      get isComplete(){          if (!this._msg.op){ @@ -222,13 +235,18 @@ export class GPGME_Message {      } +    /** +     * Sends the Message via nativeMessaging and resolves with the answer. +     * @returns {Promise<Object|GPGME_Error>} +     * @async +     */      post(){          let me = this;          return new Promise(function(resolve, reject) {              if (me.isComplete === true) {                  let conn  = new Connection;                  if (me._msg.chunksize === undefined){ -                    me._msg.chunksize = 1023*1024; +                    me._msg.chunksize = me.chunksize;                  }                  conn.post(me).then(function(response) {                      resolve(response); diff --git a/lang/js/src/Signature.js b/lang/js/src/Signature.js index c3c511a8..191e00ab 100644 --- a/lang/js/src/Signature.js +++ b/lang/js/src/Signature.js @@ -20,17 +20,16 @@   * Author(s):   *     Maximilian Krambach <[email protected]>   */ +import { gpgme_error } from './Errors';  /** - * Validates a signature object and returns + * Validates an object containing a signature, as sent by the nativeMessaging + * interface   * @param {Object} sigObject Object as returned by gpgme-json. The definition - * of the expected values are to be found in the constants 'expKeys', 'expSum', - * 'expNote' in this file. - * @returns {GPGME_Signature} Signature Object + * of the expected values are to be found in {@link expKeys}, {@link expSum}, + * {@link expNote}. + * @returns {GPGME_Signature|GPGME_Error} Signature Object   */ - -import { gpgme_error } from './Errors'; -  export function createSignature(sigObject){      if (          typeof(sigObject) !=='object' || @@ -72,18 +71,20 @@ export function createSignature(sigObject){  /** - * Representing the details of a signature. It is supposed to be read-only. The - * full details as given by gpgme-json can be accessed from the _rawSigObject. - * ) + * Representing the details of a signature. The full details as given by + * gpgme-json can be read from the _rawSigObject. + * + * Note to reviewers: This class should be read only except via + * {@link createSignature} + * @protected + * @class   */  class GPGME_Signature { +      constructor(sigObject){          this._rawSigObject = sigObject;      } -    /** -     * The signatures' fingerprint -     */      get fingerprint(){          return this._rawSigObject.fingerprint;      } @@ -105,7 +106,7 @@ class GPGME_Signature {       * @returns {Date}       */      get timestamp(){ -        return new Date(this._rawSigObject.timestamp* 1000); +        return new Date(this._rawSigObject.timestamp * 1000);      }      /** diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index f49361dc..3f6dc947 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -28,10 +28,62 @@ import { gpgme_error } from './Errors';  import { GPGME_Keyring } from './Keyring';  import { createSignature } from './Signature'; +/** + * @typedef {Object} decrypt_result + * @property {String} data The decrypted data + * @property {Boolean} base64 indicating whether data is base64 encoded. + * @property {Boolean} is_mime (optional) the data claims to be a MIME + * object. + * @property {String} file_name (optional) the original file name + * @property {signatureDetails} signatures Verification details for + * signatures + */ + +/** + * @typedef {Object} signatureDetails + * @property {Boolean} all_valid Summary if all signatures are fully valid + * @property {Number} count Number of signatures found + * @property {Number} failures Number of invalid signatures + * @property {Array<GPGME_Signature>} signatures.good All valid signatures + * @property {Array<GPGME_Signature>} signatures.bad All invalid signatures + */ + +/** + * @typedef {Object} encrypt_result The result of an encrypt operation + * @property {String} data The encrypted message + * @property {Boolean} base64 Indicating whether data is base64 encoded. + */ + +/** + * @typedef { GPGME_Key | String | Object } inputKeys + * Accepts different identifiers of a gnupg Key that can be parsed by + * {@link toKeyIdArray}. Expected inputs are: One or an array of + * GPGME_Keys; one or an array of fingerprint strings; one or an array of + * openpgpjs Key objects. + */ + +/** + * @typedef {Object} signResult The result of a signing operation + * @property {String} data The resulting data. Includes the signature in + *  clearsign mode + * @property {String} signature The detached signature (if in detached mode) + */ + +/** @typedef {Object} verifyResult The result of a verification + * @property {Boolean} data: The verified data + * @property {Boolean} is_mime (optional) the data claims to be a MIME + * object. + * @property {String} file_name (optional) the original file name + * @property {signatureDetails} signatures Verification details for + * signatures + */ + +/** + * The main entry point for gpgme.js. + * @class + */  export class GpgME { -    /** -     * initializes GpgME by opening a nativeMessaging port -     */ +      constructor(){      } @@ -41,6 +93,9 @@ export class GpgME {          }      } +    /** +     * Accesses the {@link GPGME_Keyring}. +     */      get Keyring(){          if (!this._Keyring){              this._Keyring = new GPGME_Keyring; @@ -49,23 +104,23 @@ export class GpgME {      }      /** -     * Encrypt (and optionally sign) a Message +     * Encrypt (and optionally sign) data       * @param {String|Object} data text/data to be encrypted as String. Also       * accepts Objects with a getText method -     * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys +     * @param {inputKeys} publicKeys       * Keys used to encrypt the message -     * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} secretKeys -     * (optional) Keys used to sign the message +     * @param {inputKeys} secretKeys (optional) Keys used to sign the message. +     * If Keys are present, the  operation requested is assumed to be 'encrypt +     * and sign'       * @param {Boolean} base64 (optional) The data will be interpreted as -     * base64 encoded data -     * @param {Boolean} armor (optional) Request the output as armored block +     * base64 encoded data. +     * @param {Boolean} armor (optional) Request the output as armored block.       * @param {Boolean} wildcard (optional) If true, recipient information will -     *  not be added to the message -     * @param {Object} additional use additional gpg options -     * (refer to src/permittedOperations) -     * @returns {Promise<Object>} Encrypted message: -     *   data: The encrypted message -     *   base64: Boolean indicating whether data is base64 encoded. +     * not be added to the message. +     * @param {Object} additional use additional valid gpg options as defined +     * in {@link permittedOperations} +     * @returns {Promise<encrypt_result>} Object containing the encrypted +     * message and additional info.       * @async       */      encrypt(data, publicKeys, secretKeys, base64=false, armor=true, @@ -105,28 +160,12 @@ export class GpgME {      }      /** -    * Decrypt a Message +    * Decrypts a Message      * @param {String|Object} data text/data to be decrypted. Accepts Strings      *  and Objects with a getText method      * @param {Boolean} base64 (optional) false if the data is an armored block,      *   true if it is base64 encoded binary data -    * @returns {Promise<Object>} result: Decrypted Message and information -    * @returns {String} result.data: The decrypted data. -    * @returns {Boolean} result.base64: indicating whether data is base64 -    *   encoded. -    * @returns {Boolean} result.is_mime: Indicating whether the data is a MIME -    *   object. -    * @returns {String} result.file_name: The optional original file name -    * @returns {Object} message.signatures Verification details for signatures: -    * @returns {Boolean} message.signatures.all_valid: true if all signatures -    *   are valid -    * @returns {Number} message.signatures.count: Number of signatures found -    * @returns {Number} message.signatures.failures Number of invalid -    *   signatures -    * @returns {Array<Object>} message.signatures.signatures. Two arrays -    *   (good & bad) of {@link GPGME_Signature} objects, offering further -    *   information. -    * +    * @returns {Promise<decrypt_result>} Decrypted Message and information      * @async      */      decrypt(data, base64=false){ @@ -169,16 +208,13 @@ export class GpgME {      /**       * Sign a Message       * @param {String|Object} data text/data to be signed. Accepts Strings -     * and Objects with a gettext methos -     * @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} keys The -     * key/keys to use for signing -     * @param {*} mode The signing mode. Currently supported: -     *      'clearsign': (default) The Message is embedded into the signature -     *      'detached': The signature is stored separately -     * @param {*} base64 input is considered base64 -     * @returns {Promise<Object>} -     *    data: The resulting data. Includes the signature in clearsign mode -     *    signature: The detached signature (if in detached mode) +     * and Objects with a getText method. +     * @param {inputKeys} keys The key/keys to use for signing +     * @param {String} mode The signing mode. Currently supported: +     *  'clearsign':The Message is embedded into the signature; +     *  'detached': The signature is stored separately +     * @param {Boolean} base64 input is considered base64 +     * @returns {Promise<signResult>}       * @async       */      sign(data, keys, mode='clearsign', base64=false) { @@ -221,20 +257,12 @@ export class GpgME {      /**       * Verifies data.       * @param {String|Object} data text/data to be verified. Accepts Strings -     * and Objects with a gettext method +     * and Objects with a getText method       * @param {String} (optional) A detached signature. If not present, opaque       * mode is assumed       * @param {Boolean} (optional) Data and signature are base64 encoded -     * // TODO verify if signature really is assumed to be base64 -     * @returns {Promise<Object>} result: -     * @returns {Boolean} result.data: The verified data -     * @returns {Boolean} result.is_mime: The message claims it is MIME -     * @returns {String} result.file_name: The optional filename of the message -     * @returns {Boolean} result.all_valid: true if all signatures are valid -     * @returns {Number} result.count: Number of signatures found -     * @returns {Number} result.failures Number of unsuccessful signatures -     * @returns {Array<Object>} result.signatures. Two arrays (good & bad) of -     *      {@link GPGME_Signature} objects, offering further information. +     * @returns {Promise<verifyResult>} +     *@async       */      verify(data, signature, base64 = false){          let msg = createMessage('verify'); @@ -275,7 +303,10 @@ export class GpgME {  /**   * Sets the data of the message, setting flags according on the data type   * @param {GPGME_Message} message The message where this data will be set - * @param {*} data The data to enter + * @param { String| Object } data The data to enter. Expects either a string of + * data, or an object with a getText method + * @returns {undefined| GPGME_Error} Error if not successful, nothing otherwise + * @private   */  function putData(message, data){      if (!message || !(message instanceof GPGME_Message) ) { @@ -301,6 +332,11 @@ function putData(message, data){      }  } +/** + * Parses, validates and converts incoming objects into signatures. + * @param {Array<Object>} sigs + * @returns {signatureDetails} Details about the signatures + */  function collectSignatures(sigs){      if (!Array.isArray(sigs)){          return gpgme_error('SIG_NO_SIGS'); diff --git a/lang/js/src/index.js b/lang/js/src/index.js index 6db28733..dc613fc7 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -27,8 +27,8 @@ import { gpgme_error } from './Errors';  import { Connection } from './Connection';  /** - * Tests nativeMessaging once and returns a GpgME object if successful. - * @returns {GpgME | Error} + * Initializes gpgme.js by testing the nativeMessaging connection once. + * @returns {Promise<GpgME> | GPGME_Error}   *   * @async   */ diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js index e7f53965..0b9c891f 100644 --- a/lang/js/src/permittedOperations.js +++ b/lang/js/src/permittedOperations.js @@ -22,27 +22,30 @@   */  /** -  * Definition of the possible interactions with gpgme-json. -  * operation: <Object> -      required: Array<Object> -            <String> name The name of the property -            allowed: Array of allowed types. Currently accepted values: -                ['number', 'string', 'boolean', 'Uint8Array'] -            array_allowed: Boolean. If the value can be an array of the above -            allowed_data: <Array> If present, restricts to the given value -      optional: Array<Object> -            see 'required', with these parameters not being mandatory for a -            complete message -      pinentry: boolean If a pinentry dialog is expected, and a timeout of -                5000 ms would be too short -      answer: <Object> -          type: <String< The content type of answer expected -          data: <Object> -            the properties expected and their type, eg: {'data':'string'} -      } -  } -*/ + * @typedef {Object} messageProperty + * A message Property is defined by it's key. + * @property {Array<String>} allowed Array of allowed types. + * Currently accepted values are 'number', 'string', 'boolean'. + * @property {Boolean} array_allowed If the value can be an array of types + *      defined in allowed + * @property {<Array>} allowed_data (optional) restricts to the given values +  */ +/** + * Definition of the possible interactions with gpgme-json. + * @param {Object} operation Each operation is named by a key and contains + * the following properties: + * @property {messageProperty} required An object with all required parameters + * @property {messageProperty} optional An object with all optional parameters + * @property {Boolean} pinentry (optional) If true, a password dialog is + *      expected, thus a connection tuimeout is not advisable + * @property {Object} answer The definition on what to expect as answer, if the + *      answer is not an error + * @property {Array<String>} answer.type the type(s) as reported by gpgme-json. + * @property {Object} answer.data key-value combinations of expected properties + * of an answer and their type ('boolean', 'string', object) +  @const +*/  export const permittedOperations = {      encrypt: {          pinentry: true, //TODO only with signing_keys | 
