From 522121ea7e105acc22795b1997ca500c7b227b4f Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Fri, 27 Jul 2018 20:56:11 +0200 Subject: [PATCH] js: fix indentaion -- * doing the indentation changes that became neccesary in the last commit. --- lang/js/src/Connection.js | 374 ++++++++++----------- lang/js/src/Key.js | 503 ++++++++++++++-------------- lang/js/src/Keyring.js | 677 +++++++++++++++++++------------------- lang/js/src/Message.js | 305 +++++++++-------- lang/js/src/Signature.js | 116 +++---- lang/js/src/gpgmejs.js | 387 +++++++++++----------- 6 files changed, 1187 insertions(+), 1175 deletions(-) diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index d482667e..561a5b70 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -52,128 +52,128 @@ export class Connection{ }; - /** - * @typedef {Object} backEndDetails - * @property {String} gpgme Version number of gpgme - * @property {Array} 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" - * } - */ + /** + * @typedef {Object} backEndDetails + * @property {String} gpgme Version number of gpgme + * @property {Array} 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|Promise} Details from the - * backend - * @async - */ - this.checkConnection = function(details = true){ - const msg = createMessage('version'); - if (details === true) { - return this.post(msg); - } else { - let me = this; - return new Promise(function(resolve) { - Promise.race([ - me.post(msg), - new Promise(function(resolve, reject){ - setTimeout(function(){ - reject(gpgme_error('CONN_TIMEOUT')); - }, 500); - }) - ]).then(function(){ // success - resolve(true); - }, function(){ // failure - resolve(false); + /** + * 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|Promise} Details from the + * backend + * @async + */ + this.checkConnection = function(details = true){ + const msg = createMessage('version'); + if (details === true) { + return this.post(msg); + } else { + let me = this; + return new Promise(function(resolve) { + Promise.race([ + me.post(msg), + new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(gpgme_error('CONN_TIMEOUT')); + }, 500); + }) + ]).then(function(){ // success + resolve(true); + }, function(){ // failure + resolve(false); + }); }); - }); - } - }; + } + }; - /** - * 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} The collected answer - * @async - */ - this.post = function (message){ - if (!message || !(message instanceof GPGME_Message)){ - this.disconnect(); - return Promise.reject(gpgme_error( - 'PARAM_WRONG', 'Connection.post')); - } - if (message.isComplete() !== true){ - this.disconnect(); - return Promise.reject(gpgme_error('MSG_INCOMPLETE')); - } - let chunksize = message.chunksize; - return new Promise(function(resolve, reject){ - let answer = new Answer(message); - let listener = function(msg) { - if (!msg){ - _connection.onMessage.removeListener(listener); - _connection.disconnect(); - reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); - } else { - let answer_result = answer.collect(msg); - if (answer_result !== true){ + /** + * 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} The collected answer + * @async + */ + this.post = function (message){ + if (!message || !(message instanceof GPGME_Message)){ + this.disconnect(); + return Promise.reject(gpgme_error( + 'PARAM_WRONG', 'Connection.post')); + } + if (message.isComplete() !== true){ + this.disconnect(); + return Promise.reject(gpgme_error('MSG_INCOMPLETE')); + } + let chunksize = message.chunksize; + return new Promise(function(resolve, reject){ + let answer = new Answer(message); + let listener = function(msg) { + if (!msg){ _connection.onMessage.removeListener(listener); _connection.disconnect(); - reject(answer_result); + reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); } else { - if (msg.more === true){ - _connection.postMessage({ - 'op': 'getmore', - 'chunksize': chunksize - }); - } else { + let answer_result = answer.collect(msg); + if (answer_result !== true){ _connection.onMessage.removeListener(listener); _connection.disconnect(); - const message = answer.getMessage(); - if (message instanceof Error){ - reject(message); + reject(answer_result); + } else { + if (msg.more === true){ + _connection.postMessage({ + 'op': 'getmore', + 'chunksize': chunksize + }); } else { - resolve(message); + _connection.onMessage.removeListener(listener); + _connection.disconnect(); + const message = answer.getMessage(); + if (message instanceof Error){ + reject(message); + } else { + resolve(message); + } } } } - } - }; - _connection.onMessage.addListener(listener); - if (permittedOperations[message.operation].pinentry){ - return _connection.postMessage(message.message); - } else { - return Promise.race([ - _connection.postMessage(message.message), - function(resolve, reject){ - setTimeout(function(){ + }; + _connection.onMessage.addListener(listener); + if (permittedOperations[message.operation].pinentry){ + return _connection.postMessage(message.message); + } else { + return Promise.race([ + _connection.postMessage(message.message), + function(resolve, reject){ + setTimeout(function(){ + _connection.disconnect(); + reject(gpgme_error('CONN_TIMEOUT')); + }, 5000); + }]).then(function(result){ + return result; + }, function(reject){ + if(!(reject instanceof Error)) { _connection.disconnect(); - reject(gpgme_error('CONN_TIMEOUT')); - }, 5000); - }]).then(function(result){ - return result; - }, function(reject){ - if(!(reject instanceof Error)) { - _connection.disconnect(); - return gpgme_error('GNUPG_ERROR', reject); - } else { - return reject; - } - }); - } - }); - }; -} + return gpgme_error('GNUPG_ERROR', reject); + } else { + return reject; + } + }); + } + }); + }; + } } /** @@ -197,82 +197,84 @@ class Answer{ this.getExpect = function(){ return expect; }; - /** - * Adds incoming base64 encoded data to the existing response - * @param {*} msg base64 encoded data. - * @returns {Boolean} - * - * @private - */ - this.collect = function (msg){ - if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) { - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - if (response_b64 === null){ - response_b64 = msg.response; - return true; - } else { - response_b64 += msg.response; - return true; - } - }; - /** - * Returns the base64 encoded answer data with the content verified against - * {@link permittedOperations}. - */ - this.getMessage = function (){ - if (response_b64 === undefined){ - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - let _decodedResponse = JSON.parse(atob(response_b64)); - let _response = {}; - let messageKeys = Object.keys(_decodedResponse); - let poa = permittedOperations[this.getOperation()].answer; - if (messageKeys.length === 0){ - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - for (let i= 0; i < messageKeys.length; i++){ - let key = messageKeys[i]; - switch (key) { - case 'type': - if (_decodedResponse.type === 'error'){ - return (gpgme_error('GNUPG_ERROR', - decodeURIComponent(escape(_decodedResponse.msg)))); - } else if (poa.type.indexOf(_decodedResponse.type) < 0){ - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - break; - case 'base64': - break; - case 'msg': - if (_decodedResponse.type === 'error'){ - return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg)); - } - break; - default: - if (!poa.data.hasOwnProperty(key)){ - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - if( typeof(_decodedResponse[key]) !== poa.data[key] ){ - return gpgme_error('CONN_UNEXPECTED_ANSWER'); - } - if (_decodedResponse.base64 === true - && poa.data[key] === 'string' - && this.getExpect() === undefined - ){ - _response[key] = decodeURIComponent( - atob(_decodedResponse[key]).split('').map( - function(c) { - return '%' + - ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); - } else { - _response[key] = _decodedResponse[key]; - } - break; + + /** + * Adds incoming base64 encoded data to the existing response + * @param {*} msg base64 encoded data. + * @returns {Boolean} + * + * @private + */ + this.collect = function (msg){ + if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) { + return gpgme_error('CONN_UNEXPECTED_ANSWER'); } - } - return _response; - }; -} + if (response_b64 === null){ + response_b64 = msg.response; + return true; + } else { + response_b64 += msg.response; + return true; + } + }; + /** + * Returns the base64 encoded answer data with the content verified + * against {@link permittedOperations}. + */ + this.getMessage = function (){ + if (response_b64 === undefined){ + return gpgme_error('CONN_UNEXPECTED_ANSWER'); + } + let _decodedResponse = JSON.parse(atob(response_b64)); + let _response = {}; + let messageKeys = Object.keys(_decodedResponse); + let poa = permittedOperations[this.getOperation()].answer; + if (messageKeys.length === 0){ + return gpgme_error('CONN_UNEXPECTED_ANSWER'); + } + for (let i= 0; i < messageKeys.length; i++){ + let key = messageKeys[i]; + switch (key) { + case 'type': + if (_decodedResponse.type === 'error'){ + return (gpgme_error('GNUPG_ERROR', + decodeURIComponent(escape(_decodedResponse.msg)))); + } else if (poa.type.indexOf(_decodedResponse.type) < 0){ + return gpgme_error('CONN_UNEXPECTED_ANSWER'); + } + break; + case 'base64': + break; + case 'msg': + if (_decodedResponse.type === 'error'){ + return (gpgme_error('GNUPG_ERROR', + _decodedResponse.msg)); + } + break; + default: + if (!poa.data.hasOwnProperty(key)){ + return gpgme_error('CONN_UNEXPECTED_ANSWER'); + } + if( typeof(_decodedResponse[key]) !== poa.data[key] ){ + return gpgme_error('CONN_UNEXPECTED_ANSWER'); + } + if (_decodedResponse.base64 === true + && poa.data[key] === 'string' + && this.getExpect() === undefined + ){ + _response[key] = decodeURIComponent( + atob(_decodedResponse[key]).split('').map( + function(c) { + return '%' + + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + } else { + _response[key] = _decodedResponse[key]; + } + break; + } + } + return _response; + }; + } } diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index a7f7dd26..d5873a70 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -66,224 +66,226 @@ export class GPGME_Key { return _data.fingerprint; }; - /** - * 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. - */ - this.hasSecret= function (){ - return this.get('hasSecret', true); - }; + /** + * 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. + */ + this.hasSecret= function (){ + return this.get('hasSecret', true); + }; - /** - * @param {Object} data Bulk set the data for this key, with an Object sent - * by gpgme-json. - * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, an - * error if something went wrong - * @private - */ - this.setKeyData = function (data){ - if (typeof(data) !== 'object') { - return gpgme_error('KEY_INVALID'); - } - if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - let keys = Object.keys(data); - for (let i=0; i< keys.length; i++){ - if (!validKeyProperties.hasOwnProperty(keys[i])){ - return gpgme_error('KEY_INVALID'); - } - //running the defined validation function - if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ - return gpgme_error('KEY_INVALID'); - } - switch (keys[i]){ - case 'subkeys': - _data.subkeys = []; - for (let i=0; i< data.subkeys.length; i++) { - _data.subkeys.push( - new GPGME_Subkey(data.subkeys[i])); - } - break; - case 'userids': - _data.userids = []; - for (let i=0; i< data.userids.length; i++) { - _data.userids.push( - new GPGME_UserId(data.userids[i])); - } - break; - case 'last_update': - _data[keys[i]] = new Date( data[keys[i]] * 1000 ); - break; - default: - _data[keys[i]] = data[keys[i]]; + /** + * @param {Object} data Bulk set the data for this key, with an Object + * sent by gpgme-json. + * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, + * an error if something went wrong. + * @private + */ + this.setKeyData = function (data){ + if (typeof(data) !== 'object') { + return gpgme_error('KEY_INVALID'); } - } - return this; - }; + if (!data.fingerprint || data.fingerprint !== _data.fingerprint){ + return gpgme_error('KEY_INVALID'); + } + let keys = Object.keys(data); + for (let i=0; i< keys.length; i++){ + if (!validKeyProperties.hasOwnProperty(keys[i])){ + return gpgme_error('KEY_INVALID'); + } + //running the defined validation function + if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ + return gpgme_error('KEY_INVALID'); + } + switch (keys[i]){ + case 'subkeys': + _data.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + _data.subkeys.push( + new GPGME_Subkey(data.subkeys[i])); + } + break; + case 'userids': + _data.userids = []; + for (let i=0; i< data.userids.length; i++) { + _data.userids.push( + new GPGME_UserId(data.userids[i])); + } + break; + case 'last_update': + _data[keys[i]] = new Date( data[keys[i]] * 1000 ); + break; + default: + _data[keys[i]] = data[keys[i]]; + } + } + return this; + }; - /** - * Query any property of the Key listed in {@link validKeyProperties} - * @param {String} property property to be retreived - * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). - * If 'cached' is false, the value will be resolved as a Promise. - */ - this.get = function(property) { - if (this.isAsync === true) { + /** + * Query any property of the Key listed in {@link validKeyProperties} + * @param {String} property property to be retreived + * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). + * If 'cached' is false, the value will be resolved as a Promise. + */ + this.get = function(property) { + if (this.isAsync === true) { + let me = this; + return new Promise(function(resolve, reject) { + if (property === 'armored'){ + resolve(me.getArmor()); + } else if (property === 'hasSecret'){ + resolve(me.getHasSecret()); + } else if (validKeyProperties.hasOwnProperty(property)){ + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + if (result.keys && result.keys.length === 1 && + result.keys[0].hasOwnProperty(property)){ + resolve(result.keys[0][property]); + } else { + reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); + } + }, function(error){ + reject(gpgme_error(error)); + }); + } else { + reject(gpgme_error('PARAM_WRONG')); + } + }); + } else { + if (!validKeyProperties.hasOwnProperty(property)){ + return gpgme_error('PARAM_WRONG'); + } + if (!_data.hasOwnProperty(property)){ + return gpgme_error('KEY_NO_INIT'); + } else { + return (_data[property]); + } + } + }; + + /** + * 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} + * @async + */ + this.refreshKey = function() { let me = this; return new Promise(function(resolve, reject) { - if (property === 'armored'){ - resolve(me.getArmor()); - } else if (property === 'hasSecret'){ - resolve(me.getHasSecret()); - } else if (validKeyProperties.hasOwnProperty(property)){ - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys && result.keys.length === 1 && - result.keys[0].hasOwnProperty(property)){ - resolve(result.keys[0][property]); - } else { - reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); - } - }, function(error){ - reject(gpgme_error(error)); - }); - } else { - reject(gpgme_error('PARAM_WRONG')); + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); } - }); - } else { - if (!validKeyProperties.hasOwnProperty(property)){ - return gpgme_error('PARAM_WRONG'); - } - if (!_data.hasOwnProperty(property)){ - return gpgme_error('KEY_NO_INIT'); - } else { - return (_data[property]); - } - } - }; - - /** - * 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} - * @async - */ - this.refreshKey = function() { - let me = this; - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('keylist'); - msg.setParameter('sigs', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - if (result.keys.length === 1){ - me.setKeyData(result.keys[0]); - me.getHasSecret().then(function(){ - me.getArmor().then(function(){ - resolve(me); + let msg = createMessage('keylist'); + msg.setParameter('sigs', true); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + if (result.keys.length === 1){ + me.setKeyData(result.keys[0]); + me.getHasSecret().then(function(){ + me.getArmor().then(function(){ + resolve(me); + }, function(error){ + reject(error); + }); }, function(error){ reject(error); }); - }, function(error){ - reject(error); - }); - } else { - reject(gpgme_error('KEY_NOKEY')); + } else { + reject(gpgme_error('KEY_NOKEY')); + } + }, function (error) { + reject(gpgme_error('GNUPG_ERROR'), error); + }); + }); + }; + + /** + * 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} + * @async + */ + this.getArmor = function(){ + return new Promise(function(resolve, reject) { + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); } - }, function (error) { - reject(gpgme_error('GNUPG_ERROR'), error); + let msg = createMessage('export'); + msg.setParameter('armor', true); + msg.setParameter('keys', _data.fingerprint); + msg.post().then(function(result){ + _data.armored = result.data; + resolve(result.data); + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * 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} - * @async - */ - this.getArmor = function(){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('export'); - msg.setParameter('armor', true); - msg.setParameter('keys', _data.fingerprint); - msg.post().then(function(result){ - _data.armored = result.data; - resolve(result.data); - }, function(error){ - reject(error); - }); - }); - }; - - /** - * 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} True if a secret/private Key is - * available in the gnupg Keyring - * @async - */ - this.getHasSecret = function (){ - return new Promise(function(resolve, reject) { - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('keylist'); - msg.setParameter('keys', _data.fingerprint); - msg.setParameter('secret', true); - msg.post().then(function(result){ - _data.hasSecret = null; - if ( - result.keys && - result.keys.length === 1 && - result.keys[0].secret === true - ) { - _data.hasSecret = true; - resolve(true); - } else { - _data.hasSecret = false; - resolve(false); + /** + * 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} True if a secret/private Key + * is available in the gnupg Keyring + * @async + */ + this.getHasSecret = function (){ + return new Promise(function(resolve, reject) { + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); } - }, function(error){ - reject(error); + let msg = createMessage('keylist'); + msg.setParameter('keys', _data.fingerprint); + msg.setParameter('secret', true); + msg.post().then(function(result){ + _data.hasSecret = null; + if ( + result.keys && + result.keys.length === 1 && + result.keys[0].secret === true + ) { + _data.hasSecret = true; + resolve(true); + } else { + _data.hasSecret = false; + resolve(false); + } + }, function(error){ + reject(error); + }); }); - }); - }; + }; - /** - * 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} Success if key was deleted, - * rejects with a GPG error otherwise. - */ - this.delete= function (){ - return new Promise(function(resolve, reject){ - if (!_data.fingerprint){ - reject(gpgme_error('KEY_INVALID')); - } - let msg = createMessage('delete'); - msg.setParameter('key', _data.fingerprint); - msg.post().then(function(result){ - resolve(result.success); - }, function(error){ - reject(error); + /** + * 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} Success if key was deleted, + * rejects with a GPG error otherwise. + */ + this.delete= function (){ + return new Promise(function(resolve, reject){ + if (!_data.fingerprint){ + reject(gpgme_error('KEY_INVALID')); + } + let msg = createMessage('delete'); + msg.setParameter('key', _data.fingerprint); + msg.post().then(function(result){ + resolve(result.success); + }, function(error){ + reject(error); + }); }); - }); - }; + }; } /** @@ -320,42 +322,39 @@ class GPGME_Subkey { let _data = {}; let keys = Object.keys(data); - /** - * Validates a subkey property against {@link validSubKeyProperties} and - * sets it if validation is successful - * @param {String} property - * @param {*} value - * @param private - */ - const setProperty = function (property, value){ - if (validSubKeyProperties.hasOwnProperty(property)){ - if (validSubKeyProperties[property](value) === true) { - if (property === 'timestamp' || property === 'expires'){ - _data[property] = new Date(value * 1000); - } else { - _data[property] = value; + /** + * Validates a subkey property against {@link validSubKeyProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ + const setProperty = function (property, value){ + if (validSubKeyProperties.hasOwnProperty(property)){ + if (validSubKeyProperties[property](value) === true) { + if (property === 'timestamp' || property === 'expires'){ + _data[property] = new Date(value * 1000); + } else { + _data[property] = value; + } } } + }; + for (let i=0; i< keys.length; i++) { + setProperty(keys[i], data[keys[i]]); } - }; - for (let i=0; i< keys.length; i++) { - setProperty(keys[i], data[keys[i]]); + + /** + * Fetches any information about this subkey + * @param {String} property Information to request + * @returns {String | Number | Date} + */ + this.get = function(property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); + } + }; } - - - - /** - * Fetches any information about this subkey - * @param {String} property Information to request - * @returns {String | Number | Date} - */ - this.get = function(property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; -} - } /** @@ -388,28 +387,28 @@ class GPGME_UserId { 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 - */ + /** + * Validates a subkey property against {@link validUserIdProperties} and + * sets it if validation is successful + * @param {String} property + * @param {*} value + * @param private + */ - /** - * Fetches information about the user - * @param {String} property Information to request - * @returns {String | Number} - */ - this.get = function (property) { - if (_data.hasOwnProperty(property)){ - return (_data[property]); - } - }; + /** + * Fetches information about the user + * @param {String} property Information to request + * @returns {String | Number} + */ + this.get = function (property) { + if (_data.hasOwnProperty(property)){ + return (_data[property]); + } + }; + } } -} /** * Validation definition for userIds. Each valid userId property is represented * as a key- Value pair, with their value being a validation function to check diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d863fe73..31c4f92b 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -33,359 +33,368 @@ import { gpgme_error } from './Errors'; export class GPGME_Keyring { constructor(){ - /** - * Queries Keys (all Keys or a subset) from gnupg. - * - * @param {String | Array} 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.|GPGME_Error>} - * @static - * @async - */ - this.getKeys = function(pattern, prepare_sync=false, search=false){ - return new Promise(function(resolve, reject) { - let msg = createMessage('keylist'); - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); - } - msg.setParameter('sigs', true); - if (search === true){ - msg.setParameter('locate', true); - } - msg.post().then(function(result){ - let resultset = []; - if (result.keys.length === 0){ - resolve([]); - } else { - let secondrequest; - if (prepare_sync === true) { - secondrequest = function() { - let msg2 = createMessage('keylist'); - msg2.setParameter('keys', pattern); - msg2.setParameter('secret', true); - return msg2.post(); - }; + /** + * Queries Keys (all Keys or a subset) from gnupg. + * + * @param {String | Array} 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.|GPGME_Error>} + * @static + * @async + */ + this.getKeys = function(pattern, prepare_sync=false, search=false){ + return new Promise(function(resolve, reject) { + let msg = createMessage('keylist'); + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.setParameter('sigs', true); + if (search === true){ + msg.setParameter('locate', true); + } + msg.post().then(function(result){ + let resultset = []; + if (result.keys.length === 0){ + resolve([]); } else { - secondrequest = function() { - return Promise.resolve(true); - }; - } - secondrequest().then(function(answer) { - for (let i=0; i < result.keys.length; i++){ - if (prepare_sync === true){ - result.keys[i].hasSecret = false; - if (answer && answer.keys) { - for (let j=0; j < answer.keys.length; j++ ){ - if (result.keys[i].fingerprint === - answer.keys[j].fingerprint - ) { - if (answer.keys[j].secret === true){ - result.keys[i].hasSecret = true; + let secondrequest; + if (prepare_sync === true) { + secondrequest = function() { + let msg2 = createMessage('keylist'); + msg2.setParameter('keys', pattern); + msg2.setParameter('secret', true); + return msg2.post(); + }; + } else { + secondrequest = function() { + return Promise.resolve(true); + }; + } + secondrequest().then(function(answer) { + for (let i=0; i < result.keys.length; i++){ + if (prepare_sync === true){ + result.keys[i].hasSecret = false; + if (answer && answer.keys) { + for (let j=0; + j < answer.keys.length; j++ ){ + const a = answer.keys[j]; + const b = result.keys[i]; + if ( + a.fingerprint === b.fingerprint + ) { + if (a.secret === true){ + a.hasSecret = true; + } + break; } - break; } + // TODO getArmor() to be used in sync } - // TODO getArmor() to be used in sync + } + let k = createKey(result.keys[i].fingerprint); + k.setKeyData(result.keys[i]); + resultset.push(k); + } + resolve(resultset); + }, function(error){ + reject(error); + }); + } + }); + }); + }; + + /** + * @typedef {Object} exportResult The result of a getKeysArmored + * operation. + * @property {String} armored The public Key(s) as armored block. Note + * that the result is one armored block, and not a block per key. + * @property {Array} secret_fprs (optional) list of + * fingerprints for those Keys that also have a secret Key available in + * gnupg. The secret key will not be exported, but the fingerprint can + * be used in operations needing a secret key. + */ + + /** + * Fetches the armored public Key blocks for all Keys matching the + * pattern (if no pattern is given, fetches all keys known to gnupg). + * @param {String|Array} pattern (optional) The Pattern to + * search for + * @param {Boolean} with_secret_fpr (optional) also return a list of + * fingerprints for the keys that have a secret key available + * @returns {Promise} Object containing the + * armored Key(s) and additional information. + * @static + * @async + */ + this.getKeysArmored = function(pattern, with_secret_fpr) { + return new Promise(function(resolve, reject) { + let msg = createMessage('export'); + msg.setParameter('armor', true); + if (with_secret_fpr === true) { + msg.setParameter('with-sec-fprs', true); + } + if (pattern !== undefined && pattern !== null){ + msg.setParameter('keys', pattern); + } + msg.post().then(function(answer){ + const result = {armored: answer.data}; + if (with_secret_fpr === true + && answer.hasOwnProperty('sec-fprs') + ) { + result.secret_fprs = answer['sec-fprs']; + } + resolve(result); + }, function(error){ + reject(error); + }); + }); + }; + + /** + * 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. + * + * @returns {Promise} + * @async + * @static + */ + this.getDefaultKey = function() { + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('config_opt'); + msg.setParameter('component', 'gpg'); + msg.setParameter('option', 'default-key'); + msg.post().then(function(response){ + if (response.value !== undefined + && response.value.hasOwnProperty('string') + && typeof(response.value.string) === 'string' + ){ + me.getKeys(response.value.string,true).then( + function(keys){ + if(keys.length === 1){ + resolve(keys[0]); + } else { + reject(gpgme_error('KEY_NO_DEFAULT')); + } + }, function(error){ + reject(error); + }); + } else { + // TODO: this is overly 'expensive' in communication + // and probably performance, too + me.getKeys(null,true).then(function(keys){ + for (let i=0; i < keys.length; i++){ + if (keys[i].get('hasSecret') === true){ + resolve(keys[i]); + break; + } + if (i === keys.length -1){ + reject(gpgme_error('KEY_NO_DEFAULT')); } } - let k = createKey(result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); - resultset.push(k); - } - resolve(resultset); - }, function(error){ - reject(error); - }); - } - }); - }); - }; - - /** - * @typedef {Object} exportResult The result of a getKeysArmored operation. - * @property {String} armored The public Key(s) as armored block. Note that - * the result is one armored block, and not a block per key. - * @property {Array} secret_fprs (optional) list of fingerprints - * for those Keys that also have a secret Key available in gnupg. The - * secret key will not be exported, but the fingerprint can be used in - * operations needing a secret key. - */ - - /** - * Fetches the armored public Key blocks for all Keys matching the pattern - * (if no pattern is given, fetches all keys known to gnupg). - * @param {String|Array} pattern (optional) The Pattern to search - * for - * @param {Boolean} with_secret_fpr (optional) also return a list of - * fingerprints for the keys that have a secret key available - * @returns {Promise} Object containing the - * armored Key(s) and additional information. - * @static - * @async - */ - this.getKeysArmored = function(pattern, with_secret_fpr) { - return new Promise(function(resolve, reject) { - let msg = createMessage('export'); - msg.setParameter('armor', true); - if (with_secret_fpr === true) { - msg.setParameter('with-sec-fprs', true); - } - if (pattern !== undefined && pattern !== null){ - msg.setParameter('keys', pattern); - } - msg.post().then(function(answer){ - const result = {armored: answer.data}; - if (with_secret_fpr === true - && answer.hasOwnProperty('sec-fprs') - ) { - result.secret_fprs = answer['sec-fprs']; - } - resolve(result); - }, function(error){ - reject(error); - }); - }); - }; - - /** - * 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. - * - * @returns {Promise} - * @async - * @static - */ - this.getDefaultKey = function() { - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('config_opt'); - msg.setParameter('component', 'gpg'); - msg.setParameter('option', 'default-key'); - msg.post().then(function(response){ - if (response.value !== undefined - && response.value.hasOwnProperty('string') - && typeof(response.value.string) === 'string' - ){ - me.getKeys(response.value.string,true).then(function(keys){ - if(keys.length === 1){ - resolve(keys[0]); - } else { - reject(gpgme_error('KEY_NO_DEFAULT')); - } - }, function(error){ - reject(error); - }); - } else { - // TODO: this is overly 'expensive' in communication - // and probably performance, too - me.getKeys(null,true).then(function(keys){ - for (let i=0; i < keys.length; i++){ - if (keys[i].get('hasSecret') === true){ - resolve(keys[i]); - break; - } - if (i === keys.length -1){ - reject(gpgme_error('KEY_NO_DEFAULT')); - } - } - }, function(error){ - reject(error); - }); - } - }, function(error){ - reject(error); - }); - }); - }; - - /** - * @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} 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 {@link getKeys}). - * @returns {Promise} A summary and Keys considered. - * @async - * @static - */ - this.importKey = function (armored, prepare_sync) { - let feedbackValues = ['considered', 'no_user_id', 'imported', - 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', - 'new_signatures', 'new_revocations', 'secret_read', - 'secret_imported', 'secret_unchanged', 'skipped_new_keys', - 'not_imported', 'skipped_v3_keys']; - if (!armored || typeof(armored) !== 'string'){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('import'); - msg.setParameter('data', armored); - msg.post().then(function(response){ - let infos = {}; - let fprs = []; - for (let res=0; res} 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 {@link getKeys}). + * @returns {Promise} A summary and Keys considered. + * @async + * @static + */ + this.importKey = function (armored, prepare_sync) { + let feedbackValues = ['considered', 'no_user_id', 'imported', + 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', + 'new_signatures', 'new_revocations', 'secret_read', + 'secret_imported', 'secret_unchanged', 'skipped_new_keys', + 'not_imported', 'skipped_v3_keys']; + if (!armored || typeof(armored) !== 'string'){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('import'); + msg.setParameter('data', armored); + msg.post().then(function(response){ + let infos = {}; + let fprs = []; + for (let res=0; res} - * @async - * @static - */ - this.deleteKey = function(fingerprint){ - if (isFingerprint(fingerprint) === true) { - let key = createKey(fingerprint); - 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 or exported from inside gpgme.js. - * - * @param {String} userId The user Id, e.g. 'Foo Bar ' - * @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} - * @async - */ - this.generateKey = function (userId, algo = 'default', expires){ - if ( - typeof(userId) !== 'string' || - supportedKeyAlgos.indexOf(algo) < 0 || - (expires && !(expires instanceof Date)) - ){ - return Promise.reject(gpgme_error('PARAM_WRONG')); - } - let me = this; - return new Promise(function(resolve, reject){ - let msg = createMessage('createkey'); - msg.setParameter('userid', userId); - msg.setParameter('algo', algo ); - if (expires){ - msg.setParameter('expires', - Math.floor(expires.valueOf()/1000)); + /** + * Convenience function for deleting a Key. See {@link Key.delete} for + * further information about the return values. + * @param {String} fingerprint + * @returns {Promise} + * @async + * @static + */ + this.deleteKey = function(fingerprint){ + if (isFingerprint(fingerprint) === true) { + let key = createKey(fingerprint); + return key.delete(); + } else { + return Promise.reject(gpgme_error('KEY_INVALID')); } - msg.post().then(function(response){ - me.getKeys(response.fingerprint, true).then( - // TODO make prepare_sync (second parameter) optional here. - function(result){ - resolve(result); - }, function(error){ - reject(error); - }); - }, function(error) { - reject(error); + }; + + /** + * 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 or exported from inside gpgme.js. + * + * @param {String} userId The user Id, e.g. 'Foo Bar ' + * @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} + * @async + */ + this.generateKey = function (userId, algo = 'default', expires){ + if ( + typeof(userId) !== 'string' || + supportedKeyAlgos.indexOf(algo) < 0 || + (expires && !(expires instanceof Date)) + ){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } + let me = this; + return new Promise(function(resolve, reject){ + let msg = createMessage('createkey'); + msg.setParameter('userid', userId); + msg.setParameter('algo', algo ); + if (expires){ + msg.setParameter('expires', + Math.floor(expires.valueOf()/1000)); + } + msg.post().then(function(response){ + me.getKeys(response.fingerprint, true).then( + // TODO prepare_sync? + function(result){ + resolve(result); + }, function(error){ + reject(error); + }); + }, function(error) { + reject(error); + }); }); - }); - }; -} + }; + } } /** diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index cf9f84ef..c0b6ed57 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -61,162 +61,162 @@ export class GPGME_Message { return _msg.op; }; - /** - * 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. - */ - this.setChunksize = function (value){ - if ( - Number.isInteger(value) && - value > 10 * 1024 && - value <= 1024 * 1024 - ){ - _msg.chunksize = value; - } - }; - - this.getMsg = function(){ - return _msg; - }; - - this.getChunksize= function() { - return _msg.chunksize; - }; - - /** - * Sets a parameter for the message. It validates with - * {@link permittedOperations} - * @param {String} param Parameter to set - * @param {any} value Value to set - * @returns {Boolean} If the parameter was set successfully - */ - this.setParameter = function ( param,value ){ - if (!param || typeof(param) !== 'string'){ - return gpgme_error('PARAM_WRONG'); - } - let po = permittedOperations[_msg.op]; - if (!po){ - return gpgme_error('MSG_WRONG_OP'); - } - let poparam = null; - if (po.required.hasOwnProperty(param)){ - poparam = po.required[param]; - } else if (po.optional.hasOwnProperty(param)){ - poparam = po.optional[param]; - } else { - return gpgme_error('PARAM_WRONG'); - } - // check incoming value for correctness - let checktype = function(val){ - switch(typeof(val)){ - case 'string': - if (poparam.allowed.indexOf(typeof(val)) >= 0 - && val.length > 0) { - return true; - } - return gpgme_error('PARAM_WRONG'); - case 'number': - if ( - poparam.allowed.indexOf('number') >= 0 - && isNaN(value) === false){ - return true; - } - return gpgme_error('PARAM_WRONG'); - - case 'boolean': - if (poparam.allowed.indexOf('boolean') >= 0){ - return true; - } - return gpgme_error('PARAM_WRONG'); - case 'object': - if (Array.isArray(val)){ - if (poparam.array_allowed !== true){ - return gpgme_error('PARAM_WRONG'); - } - for (let i=0; i < val.length; i++){ - let res = checktype(val[i]); - if (res !== true){ - return res; - } - } - if (val.length > 0) { - return true; - } - } else if (val instanceof Uint8Array){ - if (poparam.allowed.indexOf('Uint8Array') >= 0){ - return true; - } - return gpgme_error('PARAM_WRONG'); - } else { - return gpgme_error('PARAM_WRONG'); - } - break; - default: - return gpgme_error('PARAM_WRONG'); + /** + * 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. + */ + this.setChunksize = function (value){ + if ( + Number.isInteger(value) && + value > 10 * 1024 && + value <= 1024 * 1024 + ){ + _msg.chunksize = value; } }; - let typechecked = checktype(value); - if (typechecked !== true){ - return typechecked; - } - if (poparam.hasOwnProperty('allowed_data')){ - if (poparam.allowed_data.indexOf(value) < 0){ + + this.getMsg = function(){ + return _msg; + }; + + this.getChunksize= function() { + return _msg.chunksize; + }; + + /** + * Sets a parameter for the message. It validates with + * {@link permittedOperations} + * @param {String} param Parameter to set + * @param {any} value Value to set + * @returns {Boolean} If the parameter was set successfully + */ + this.setParameter = function ( param,value ){ + if (!param || typeof(param) !== 'string'){ return gpgme_error('PARAM_WRONG'); } - } - _msg[param] = value; - return true; - }; + let po = permittedOperations[_msg.op]; + if (!po){ + return gpgme_error('MSG_WRONG_OP'); + } + let poparam = null; + if (po.required.hasOwnProperty(param)){ + poparam = po.required[param]; + } else if (po.optional.hasOwnProperty(param)){ + poparam = po.optional[param]; + } else { + return gpgme_error('PARAM_WRONG'); + } + // check incoming value for correctness + let checktype = function(val){ + switch(typeof(val)){ + case 'string': + if (poparam.allowed.indexOf(typeof(val)) >= 0 + && val.length > 0) { + return true; + } + return gpgme_error('PARAM_WRONG'); + case 'number': + if ( + poparam.allowed.indexOf('number') >= 0 + && isNaN(value) === false){ + return true; + } + return gpgme_error('PARAM_WRONG'); + + case 'boolean': + if (poparam.allowed.indexOf('boolean') >= 0){ + return true; + } + return gpgme_error('PARAM_WRONG'); + case 'object': + if (Array.isArray(val)){ + if (poparam.array_allowed !== true){ + return gpgme_error('PARAM_WRONG'); + } + for (let i=0; i < val.length; i++){ + let res = checktype(val[i]); + if (res !== true){ + return res; + } + } + if (val.length > 0) { + return true; + } + } else if (val instanceof Uint8Array){ + if (poparam.allowed.indexOf('Uint8Array') >= 0){ + return true; + } + return gpgme_error('PARAM_WRONG'); + } else { + return gpgme_error('PARAM_WRONG'); + } + break; + default: + return gpgme_error('PARAM_WRONG'); + } + }; + let typechecked = checktype(value); + if (typechecked !== true){ + return typechecked; + } + if (poparam.hasOwnProperty('allowed_data')){ + if (poparam.allowed_data.indexOf(value) < 0){ + return gpgme_error('PARAM_WRONG'); + } + } + _msg[param] = value; + return true; + }; - /** - * 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. - */ - this.isComplete = function(){ - if (!_msg.op){ - return false; - } - let reqParams = Object.keys( - permittedOperations[_msg.op].required); - let msg_params = Object.keys(_msg); - for (let i=0; i < reqParams.length; i++){ - if (msg_params.indexOf(reqParams[i]) < 0){ + /** + * 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. + */ + this.isComplete = function(){ + if (!_msg.op){ return false; } - } - return true; - }; - /** - * Sends the Message via nativeMessaging and resolves with the answer. - * @returns {Promise} - * @async - */ - this.post = function(){ - let me = this; - return new Promise(function(resolve, reject) { - if (me.isComplete() === true) { + let reqParams = Object.keys( + permittedOperations[_msg.op].required); + let msg_params = Object.keys(_msg); + for (let i=0; i < reqParams.length; i++){ + if (msg_params.indexOf(reqParams[i]) < 0){ + return false; + } + } + return true; + }; + /** + * Sends the Message via nativeMessaging and resolves with the answer. + * @returns {Promise} + * @async + */ + this.post = function(){ + let me = this; + return new Promise(function(resolve, reject) { + if (me.isComplete() === true) { - let conn = new Connection; - conn.post(me).then(function(response) { - resolve(response); - }, function(reason) { - reject(reason); - }); - } - else { - reject(gpgme_error('MSG_INCOMPLETE')); - } - }); - }; -} + let conn = new Connection; + conn.post(me).then(function(response) { + resolve(response); + }, function(reason) { + reject(reason); + }); + } + else { + reject(gpgme_error('MSG_INCOMPLETE')); + } + }); + }; + } /** * Returns the prepared message with parameters and completeness checked @@ -231,11 +231,10 @@ export class GPGME_Message { return null; } } - -get operation(){ - return this.getOperation(); -} -get chunksize(){ - return this.getChunksize(); -} + get operation(){ + return this.getOperation(); + } + get chunksize(){ + return this.getChunksize(); + } } diff --git a/lang/js/src/Signature.js b/lang/js/src/Signature.js index ff4278ad..0ee58e94 100644 --- a/lang/js/src/Signature.js +++ b/lang/js/src/Signature.js @@ -84,65 +84,65 @@ class GPGME_Signature { constructor(sigObject){ let _rawSigObject = sigObject; - this.getFingerprint = function(){ - if (!_rawSigObject.fingerprint){ - return gpgme_error('SIG_WRONG'); - } else { - return _rawSigObject.fingerprint; - } - }; - - /** - * The expiration of this Signature as Javascript date, or null if - * signature does not expire - * @returns {Date | null} - */ - this.getExpiration = function(){ - if (!_rawSigObject.exp_timestamp){ - return null; - } - return new Date(_rawSigObject.exp_timestamp* 1000); - }; - - /** - * The creation date of this Signature in Javascript Date - * @returns {Date} - */ - this.getTimestamp= function (){ - return new Date(_rawSigObject.timestamp * 1000); - }; - - /** - * The overall validity of the key. If false, errorDetails may contain - * additional information - */ - this.getValid= function() { - if (_rawSigObject.summary.valid === true){ - return true; - } else { - return false; - } - }; - - /** - * gives more information on non-valid signatures. Refer to the gpgme docs - * https://www.gnupg.org/documentation/manuals/gpgme/Verify.html for - * details on the values - * @returns {Object} Object with boolean properties - */ - this.getErrorDetails = function (){ - let properties = ['revoked', 'key-expired', 'sig-expired', - 'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy', - 'sys-error']; - let result = {}; - for (let i=0; i< properties.length; i++){ - if ( _rawSigObject.hasOwnProperty(properties[i]) ){ - result[properties[i]] = _rawSigObject[properties[i]]; + this.getFingerprint = function(){ + if (!_rawSigObject.fingerprint){ + return gpgme_error('SIG_WRONG'); + } else { + return _rawSigObject.fingerprint; } - } - return result; - }; -} + }; + + /** + * The expiration of this Signature as Javascript date, or null if + * signature does not expire + * @returns {Date | null} + */ + this.getExpiration = function(){ + if (!_rawSigObject.exp_timestamp){ + return null; + } + return new Date(_rawSigObject.exp_timestamp* 1000); + }; + + /** + * The creation date of this Signature in Javascript Date + * @returns {Date} + */ + this.getTimestamp= function (){ + return new Date(_rawSigObject.timestamp * 1000); + }; + + /** + * The overall validity of the key. If false, errorDetails may contain + * additional information. + */ + this.getValid= function() { + if (_rawSigObject.summary.valid === true){ + return true; + } else { + return false; + } + }; + + /** + * gives more information on non-valid signatures. Refer to the gpgme + * docs https://www.gnupg.org/documentation/manuals/gpgme/Verify.html + * for details on the values. + * @returns {Object} Object with boolean properties + */ + this.getErrorDetails = function (){ + let properties = ['revoked', 'key-expired', 'sig-expired', + 'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy', + 'sys-error']; + let result = {}; + for (let i=0; i< properties.length; i++){ + if ( _rawSigObject.hasOwnProperty(properties[i]) ){ + result[properties[i]] = _rawSigObject[properties[i]]; + } + } + return result; + }; + } /** * Convenience getter for {@link getFingerprint} diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index f587e854..720490d6 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -107,204 +107,207 @@ export class GpgME { return _Keyring; }; - /** - * Encrypt (and optionally sign) data - * @param {String|Object} data text/data to be encrypted as String. Also - * accepts Objects with a getText method - * @param {inputKeys} publicKeys - * Keys used to encrypt 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. - * @param {Boolean} wildcard (optional) If true, recipient information will - * not be added to the message. - * @param {Object} additional use additional valid gpg options as defined - * in {@link permittedOperations} - * @returns {Promise} Object containing the encrypted - * message and additional info. - * @async - */ - this.encrypt = function (data, publicKeys, secretKeys, base64=false, - armor=true, wildcard=false, additional = {} - ){ - let msg = createMessage('encrypt'); - if (msg instanceof Error){ - return Promise.reject(msg); - } - msg.setParameter('armor', armor); - msg.setParameter('always-trust', true); - if (base64 === true) { - msg.setParameter('base64', true); - } - let pubkeys = toKeyIdArray(publicKeys); - msg.setParameter('keys', pubkeys); - let sigkeys = toKeyIdArray(secretKeys); - if (sigkeys.length > 0) { - msg.setParameter('signing_keys', sigkeys); - } - putData(msg, data); - if (wildcard === true){ - msg.setParameter('throw-keyids', true); - } - if (additional){ - let additional_Keys = Object.keys(additional); - for (let k = 0; k < additional_Keys.length; k++) { - msg.setParameter(additional_Keys[k], - additional[additional_Keys[k]]); + /** + * Encrypt (and optionally sign) data + * @param {String|Object} data text/data to be encrypted as String. Also + * accepts Objects with a getText method + * @param {inputKeys} publicKeys + * Keys used to encrypt 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. + * @param {Boolean} wildcard (optional) If true, recipient information + * will not be added to the message. + * @param {Object} additional use additional valid gpg options as + * defined in {@link permittedOperations} + * @returns {Promise} Object containing the encrypted + * message and additional info. + * @async + */ + this.encrypt = function (data, publicKeys, secretKeys, base64=false, + armor=true, wildcard=false, additional = {} + ){ + let msg = createMessage('encrypt'); + if (msg instanceof Error){ + return Promise.reject(msg); } - } - if (msg.isComplete() === true){ - return msg.post(); - } else { - return Promise.reject(gpgme_error('MSG_INCOMPLETE')); - } - }; - - /** - * 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} Decrypted Message and information - * @async - */ - this.decrypt = function (data, base64=false){ - if (data === undefined){ - return Promise.reject(gpgme_error('MSG_EMPTY')); - } - let msg = createMessage('decrypt'); - - if (msg instanceof Error){ - return Promise.reject(msg); - } - if (base64 === true){ - msg.setParameter('base64', true); - } - putData(msg, data); - if (base64 === true){ - msg.setParameter('base64', true); - } - return new Promise(function(resolve, reject){ - msg.post().then(function(result){ - let _result = {data: result.data}; - _result.base64 = result.base64 ? true: false; - _result.is_mime = result.mime ? true: false; - if (result.file_name){ - _result.file_name = result.file_name; - } - if ( - result.hasOwnProperty('signatures') && - Array.isArray(result.signatures) - ) { - _result.signatures = collectSignatures(result.signatures); - } - resolve(_result); - }, function(error){ - reject(error); - }); - }); - }; - - /** - * Sign a Message - * @param {String|Object} data text/data to be signed. Accepts Strings - * 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} - * @async - */ - this.sign = function (data, keys, mode='clearsign', base64=false) { - if (data === undefined){ - return Promise.reject(gpgme_error('MSG_EMPTY')); - } - let key_arr = toKeyIdArray(keys); - if (key_arr.length === 0){ - return Promise.reject(gpgme_error('MSG_NO_KEYS')); - } - let msg = createMessage('sign'); - - msg.setParameter('keys', key_arr); - if (base64 === true){ - msg.setParameter('base64', true); - } - msg.setParameter('mode', mode); - putData(msg, data); - return new Promise(function(resolve,reject) { - if (mode ==='detached'){ - msg.expect= 'base64'; + msg.setParameter('armor', armor); + msg.setParameter('always-trust', true); + if (base64 === true) { + msg.setParameter('base64', true); } - msg.post().then( function(message) { - if (mode === 'clearsign'){ - resolve({ - data: message.data} - ); - } else if (mode === 'detached') { - resolve({ - data: data, - signature: message.data - }); + let pubkeys = toKeyIdArray(publicKeys); + msg.setParameter('keys', pubkeys); + let sigkeys = toKeyIdArray(secretKeys); + if (sigkeys.length > 0) { + msg.setParameter('signing_keys', sigkeys); + } + putData(msg, data); + if (wildcard === true){ + msg.setParameter('throw-keyids', true); + } + if (additional){ + let additional_Keys = Object.keys(additional); + for (let k = 0; k < additional_Keys.length; k++) { + msg.setParameter(additional_Keys[k], + additional[additional_Keys[k]]); } - }, function(error){ - reject(error); - }); - }); - }; - - /** - * Verifies data. - * @param {String|Object} data text/data to be verified. Accepts Strings - * 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 - * @returns {Promise} - *@async - */ - this.verify= function (data, signature, base64 = false){ - let msg = createMessage('verify'); - let dt = putData(msg, data); - if (dt instanceof Error){ - return Promise.reject(dt); - } - if (signature){ - if (typeof(signature)!== 'string'){ - return Promise.reject(gpgme_error('PARAM_WRONG')); + } + if (msg.isComplete() === true){ + return msg.post(); } else { - msg.setParameter('signature', signature); + return Promise.reject(gpgme_error('MSG_INCOMPLETE')); } - } - if (base64 === true){ - msg.setParameter('base64', true); - } - return new Promise(function(resolve, reject){ - msg.post().then(function (message){ - if (!message.info || !message.info.signatures){ - reject(gpgme_error('SIG_NO_SIGS')); - } else { - let _result = collectSignatures(message.info.signatures); - _result.is_mime = message.info.is_mime? true: false; - if (message.info.filename){ - _result.file_name = message.info.filename; - } - _result.data = message.data; - resolve(_result); - } - }, function(error){ - reject(error); - }); - }); - }; -} + }; /** + * 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} Decrypted Message and information + * @async + */ + this.decrypt = function (data, base64=false){ + if (data === undefined){ + return Promise.reject(gpgme_error('MSG_EMPTY')); + } + let msg = createMessage('decrypt'); + + if (msg instanceof Error){ + return Promise.reject(msg); + } + if (base64 === true){ + msg.setParameter('base64', true); + } + putData(msg, data); + if (base64 === true){ + msg.setParameter('base64', true); + } + return new Promise(function(resolve, reject){ + msg.post().then(function(result){ + let _result = {data: result.data}; + _result.base64 = result.base64 ? true: false; + _result.is_mime = result.mime ? true: false; + if (result.file_name){ + _result.file_name = result.file_name; + } + if ( + result.hasOwnProperty('signatures') && + Array.isArray(result.signatures) + ) { + _result.signatures = collectSignatures( + result.signatures); + } + resolve(_result); + }, function(error){ + reject(error); + }); + }); + }; + + /** + * Sign a Message + * @param {String|Object} data text/data to be signed. Accepts Strings + * 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} + * @async + */ + this.sign = function (data, keys, mode='clearsign', base64=false) { + if (data === undefined){ + return Promise.reject(gpgme_error('MSG_EMPTY')); + } + let key_arr = toKeyIdArray(keys); + if (key_arr.length === 0){ + return Promise.reject(gpgme_error('MSG_NO_KEYS')); + } + let msg = createMessage('sign'); + + msg.setParameter('keys', key_arr); + if (base64 === true){ + msg.setParameter('base64', true); + } + msg.setParameter('mode', mode); + putData(msg, data); + return new Promise(function(resolve,reject) { + if (mode ==='detached'){ + msg.expect= 'base64'; + } + msg.post().then( function(message) { + if (mode === 'clearsign'){ + resolve({ + data: message.data} + ); + } else if (mode === 'detached') { + resolve({ + data: data, + signature: message.data + }); + } + }, function(error){ + reject(error); + }); + }); + }; + + /** + * Verifies data. + * @param {String|Object} data text/data to be verified. Accepts Strings + * 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 + * @returns {Promise} + *@async + */ + this.verify= function (data, signature, base64 = false){ + let msg = createMessage('verify'); + let dt = putData(msg, data); + if (dt instanceof Error){ + return Promise.reject(dt); + } + if (signature){ + if (typeof(signature)!== 'string'){ + return Promise.reject(gpgme_error('PARAM_WRONG')); + } else { + msg.setParameter('signature', signature); + } + } + if (base64 === true){ + msg.setParameter('base64', true); + } + return new Promise(function(resolve, reject){ + msg.post().then(function (message){ + if (!message.info || !message.info.signatures){ + reject(gpgme_error('SIG_NO_SIGS')); + } else { + let _result = collectSignatures( + message.info.signatures); + _result.is_mime = message.info.is_mime? true: false; + if (message.info.filename){ + _result.file_name = message.info.filename; + } + _result.data = message.data; + resolve(_result); + } + }, function(error){ + reject(error); + }); + }); + }; + } + + /** * setter for {@link setKeyring}. * @param {GPGME_Keyring} keyring A Keyring to use */ @@ -332,7 +335,7 @@ export class GpgME { * @private */ function putData(message, data){ - if (!message || !message instanceof GPGME_Message) { + if (!message || !(message instanceof GPGME_Message)) { return gpgme_error('PARAM_WRONG'); } if (!data){