js: documentation

--

* Fixed errors:
- src/Message.js post(): Set chunksize to defined default value instead
  of hardcoded
- src/Keys.js: added getHasSecret() to refreshKey operation.

* Reviewed and updated the documentation

* non-documentation changes which do not affect functionality:
- src/Errors: disabled a console.warn that is only useful for debugging
- helpers.js: renamed "string" to "value" in isFingerprint and isLongId
  to avoid confusion
- src/Keyring: prepare_sync, search are both explicitly set to false by
  default
This commit is contained in:
Maximilian Krambach 2018-07-10 14:32:26 +02:00
parent 8964627f6a
commit 4015f5b498
11 changed files with 458 additions and 236 deletions

View File

@ -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"]

View File

@ -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');

View File

@ -21,6 +21,9 @@
* Maximilian Krambach <mkrambach@intevation.de>
*/
/**
* 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'){

View File

@ -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);
}

View File

@ -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);
},

View File

@ -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 <foo@bar.baz>"
* @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 <foo@bar.baz>'
* @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',

View File

@ -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);

View File

@ -20,17 +20,16 @@
* Author(s):
* Maximilian Krambach <mkrambach@intevation.de>
*/
/**
* Validates a signature object and returns
* @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
*/
import { gpgme_error } from './Errors';
/**
* 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 {@link expKeys}, {@link expSum},
* {@link expNote}.
* @returns {GPGME_Signature|GPGME_Error} Signature Object
*/
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);
}
/**

View File

@ -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');

View File

@ -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
*/

View File

@ -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