diff options
| author | Maximilian Krambach <[email protected]> | 2018-08-20 10:12:43 +0000 | 
|---|---|---|
| committer | Maximilian Krambach <[email protected]> | 2018-08-20 10:12:43 +0000 | 
| commit | 1954d27be86b8e4eb801ca6ddcb670f8cfb149f5 (patch) | |
| tree | 90183ad8fbc37e440a3f2949e3dc5c1fbc12bacb /lang/js | |
| parent | js: decode arriving gpg message strings (diff) | |
| download | gpgme-1954d27be86b8e4eb801ca6ddcb670f8cfb149f5.tar.gz gpgme-1954d27be86b8e4eb801ca6ddcb670f8cfb149f5.zip | |
js: revert changes to class read/write restriction
--
* undoes 94ee0988d4eaac27785de6efb7c19ca9976e1e9c and
  e16a87e83910ebb6bfdc4148369165f121f0997e.
  I do not fully understand why my approach was bad, but I am not in
  a position to argue. This revert was requested to me after a review,
  and I'm doing it in the assumption that more experienced people know
  better than me.
* unittests: Also changed some outdated tests that stopped working
  since 754e799d35fd62d7a979452f44342934659908c7 (as GPGME_Key is not
  exported, one cannot check for instanceof in the tests anymore)
Diffstat (limited to '')
| -rw-r--r-- | lang/js/src/Connection.js | 415 | ||||
| -rw-r--r-- | lang/js/src/Errors.js | 13 | ||||
| -rw-r--r-- | lang/js/src/Key.js | 386 | ||||
| -rw-r--r-- | lang/js/src/Keyring.js | 675 | ||||
| -rw-r--r-- | lang/js/src/Message.js | 321 | ||||
| -rw-r--r-- | lang/js/src/Signature.js | 116 | ||||
| -rw-r--r-- | lang/js/src/gpgmejs.js | 425 | ||||
| -rw-r--r-- | lang/js/src/index.js | 6 | ||||
| -rw-r--r-- | lang/js/unittests.js | 62 | 
9 files changed, 1164 insertions, 1255 deletions
| diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index 8d381f15..a60fd215 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -39,144 +39,146 @@ import { decode } from './Helpers';  export class Connection{      constructor(){ -        let _connection = chrome.runtime.connectNative('gpgmejson'); - +        this._connection = chrome.runtime.connectNative('gpgmejson'); +    } -        /** -         * Immediately closes an open port. -         */ -        this.disconnect = function () { -            if (_connection){ -                _connection.disconnect(); -                _connection = null; -            } -        }; +    /** +     * Immediately closes an open port. +     */ +    disconnect() { +        if (this._connection){ +            this._connection.disconnect(); +            this._connection = null; +        } +    } -        /** -        * @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" -        * } -        */ +    /** +    * @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 -         */ -        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<backEndDetails>|Promise<Boolean>} Details from the +     * backend +     * @async +     */ +    checkConnection (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<Object>} 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 = Object.freeze(new Answer(message)); -                let listener = function(msg) { -                    if (!msg){ -                        _connection.onMessage.removeListener(listener); -                        _connection.disconnect(); -                        reject(gpgme_error('CONN_EMPTY_GPG_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 collected answer +     * @async +     */ +    post(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; +        const me = this; +        return new Promise(function(resolve, reject){ +            let answer = new Answer(message); +            let listener = function(msg) { +                if (!msg){ +                    me._connection.onMessage.removeListener(listener); +                    me._connection.disconnect(); +                    reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); +                } else { +                    let answer_result = answer.collect(msg); +                    if (answer_result !== true){ +                        me._connection.onMessage.removeListener(listener); +                        me._connection.disconnect(); +                        reject(answer_result);                      } else { -                        let answer_result = answer.collect(msg); -                        if (answer_result !== true){ -                            _connection.onMessage.removeListener(listener); -                            _connection.disconnect(); -                            reject(answer_result); +                        if (msg.more === true){ +                            me._connection.postMessage({ +                                'op': 'getmore', +                                'chunksize': chunksize +                            });                          } else { -                            if (msg.more === true){ -                                _connection.postMessage({ -                                    'op': 'getmore', -                                    'chunksize': chunksize -                                }); +                            me._connection.onMessage.removeListener(listener); +                            me._connection.disconnect(); +                            const message = answer.getMessage(); +                            if (message instanceof Error){ +                                reject(message);                              } else { -                                _connection.onMessage.removeListener(listener); -                                _connection.disconnect(); -                                const message = answer.getMessage(); -                                if (message instanceof Error){ -                                    reject(message); -                                } else { -                                    resolve(message); -                                } +                                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.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; -                        } -                    });                  } -            }); -        }; +            }; +            me._connection.onMessage.addListener(listener); +            if (permittedOperations[message.operation].pinentry){ +                return me._connection.postMessage(message.message); +            } else { +                return Promise.race([ +                    me._connection.postMessage(message.message), +                    function(resolve, reject){ +                        setTimeout(function(){ +                            me._connection.disconnect(); +                            reject(gpgme_error('CONN_TIMEOUT')); +                        }, 5000); +                    } +                ]).then(function(result){ +                    return result; +                }, function(reject){ +                    if(!(reject instanceof Error)) { +                        me._connection.disconnect(); +                        return gpgme_error('GNUPG_ERROR', reject); +                    } else { +                        return reject; +                    } +                }); +            } +        });      }  } +  /**   * A class for answer objects, checking and processing the return messages of   * the nativeMessaging communication. @@ -188,95 +190,94 @@ class Answer{       * @param {GPGME_Message} message       */      constructor(message){ -        const operation = message.operation; -        const expected = message.getExpect(); -        let response_b64 = null; +        this._operation = message.operation; +        this._expected = message.expected; +        this._response_b64 = null; +    } -        this.getOperation = function(){ -            return operation; -        }; +    get operation (){ +        return this._operation; +    } -        this.getExpect = function(){ -            return expected; -        }; +    get expected (){ +        return this._expected; +    } -        /** -         * 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', -                            decode(_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() !== 'base64' -                    ){ -                        _response[key] = decodeURIComponent( -                            atob(_decodedResponse[key]).split('').map( -                                function(c) { -                                    return '%' + -                                ('00' + c.charCodeAt(0).toString(16)).slice(-2); -                                }).join('')); -                    } else { -                        _response[key] = decode(_decodedResponse[key]); -                    } -                    break; +    /** +     * 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'); +        } +        if (!this._response_b64){ +            this._response_b64 = msg.response; +            return true; +        } else { +            this._response_b64 += msg.response; +            return true; +        } +    } +    /** +     * Returns the base64 encoded answer data with the content verified +     * against {@link permittedOperations}. +     */ +    getMessage(){ +        if (this._response_b64 === null){ +            return gpgme_error('CONN_UNEXPECTED_ANSWER'); +        } +        let _decodedResponse = JSON.parse(atob(this._response_b64)); +        let _response = {}; +        let messageKeys = Object.keys(_decodedResponse); +        let poa = permittedOperations[this.operation].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', +                        decode(_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.expected !== 'base64' +                ){ +                    _response[key] = decodeURIComponent( +                        atob(_decodedResponse[key]).split('').map( +                            function(c) { +                                return '%' + +                            ('00' + c.charCodeAt(0).toString(16)).slice(-2); +                            }).join('')); +                } else { +                    _response[key] = decode(_decodedResponse[key]);                  } +                break;              } -            return _response; -        }; +        } +        return _response;      }  } diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js index b22eca73..2a35bc5e 100644 --- a/lang/js/src/Errors.js +++ b/lang/js/src/Errors.js @@ -123,7 +123,7 @@ const err_list = {  export function gpgme_error(code = 'GENERIC_ERROR', info){      if (err_list.hasOwnProperty(code)){          if (err_list[code].type === 'error'){ -            return Object.freeze(new GPGME_Error(code)); +            return new GPGME_Error(code);          }          if (err_list[code].type === 'warning'){              // eslint-disable-next-line no-console @@ -131,10 +131,10 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){          }          return null;      } else if (code === 'GNUPG_ERROR'){ -        return Object.freeze(new GPGME_Error(code, info)); +        return new GPGME_Error(code, info);      }      else { -        return Object.freeze(new GPGME_Error('GENERIC_ERROR')); +        return new GPGME_Error('GENERIC_ERROR');      }  } @@ -148,6 +148,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){   */  class GPGME_Error extends Error{      constructor(code = 'GENERIC_ERROR', msg=''){ +          if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){              super(msg);          } else if (err_list.hasOwnProperty(code)){ @@ -159,12 +160,10 @@ class GPGME_Error extends Error{          } else {              super(err_list['GENERIC_ERROR'].msg);          } -        this.getCode = function(){ -            return code; -        }; +        this._code = code;      }      get code(){ -        return this.getCode(); +        return this._code;      }  }
\ No newline at end of file diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index ea6fd883..37ec7f9d 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -40,12 +40,12 @@ export function createKey(fingerprint, async = false, data){          return gpgme_error('PARAM_WRONG');      }      if (data !== undefined){ -        data = validateKeyData(data); +        data = validateKeyData(fingerprint, data);      }      if (data instanceof Error){          return gpgme_error('KEY_INVALID');      } else { -        return Object.freeze(new GPGME_Key(fingerprint, async, data)); +        return new GPGME_Key(fingerprint, async, data);      }  } @@ -65,184 +65,181 @@ class GPGME_Key {          /**           * @property {Boolean} If true, most answers will be asynchronous           */ -        this.isAsync = async; +        this._async = async; -        let _data = {fingerprint: fingerprint.toUpperCase()}; +        this._data = {fingerprint: fingerprint.toUpperCase()};          if (data !== undefined -            && data.fingerprint.toUpperCase() === _data.fingerprint +            && data.fingerprint.toUpperCase() === this._data.fingerprint          ) { -            _data = data; +            this._data = data;          } -        this.getFingerprint = function(){ -            if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ -                return gpgme_error('KEY_INVALID'); -            } -            return _data.fingerprint; -        }; +    } -        /** -         * Query any property of the Key listed in {@link validKeyProperties} -         * @param {String} property property to be retreived -         * @returns {Boolean| String | Date | Array | Object |GPGME_Error} -         * the value of the property. If the Key is set to Async, the value -         * will be fetched from gnupg and resolved as a Promise. If Key is not -         * async, the armored property is not available (it can still be -         * retrieved asynchronously by {@link Key.getArmor}) -         */ -        this.get = function(property) { -            if (this.isAsync === true) { -                switch (property){ -                case 'armored': -                    return this.getArmor(); -                case 'hasSecret': -                    return this.getGnupgSecretState(); -                default: -                    return getGnupgState(property); -                } +    /** +     * Query any property of the Key listed in {@link validKeyProperties} +     * @param {String} property property to be retreived +     * @returns {Boolean| String | Date | Array | Object |GPGME_Error} +     * the value of the property. If the Key is set to Async, the value +     * will be fetched from gnupg and resolved as a Promise. If Key is not +     * async, the armored property is not available (it can still be +     * retrieved asynchronously by {@link Key.getArmor}) +     */ +    get(property) { +        if (this._async === true) { +            switch (property){ +            case 'armored': +                return this.getArmor(); +            case 'hasSecret': +                return this.getGnupgSecretState(); +            default: +                return getGnupgState(this.fingerprint, property); +            } +        } else { +            if (property === 'armored') { +                return gpgme_error('KEY_ASYNC_ONLY'); +            } +            if (!validKeyProperties.hasOwnProperty(property)){ +                return gpgme_error('PARAM_WRONG');              } else { -                if (property === 'armored') { -                    return gpgme_error('KEY_ASYNC_ONLY'); -                } -                if (!validKeyProperties.hasOwnProperty(property)){ -                    return gpgme_error('PARAM_WRONG'); -                } else { -                    return (_data[property]); -                } +                return (this._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<GPGME_Key|GPGME_Error>} -         * @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){ -                        const newdata = validateKeyData( -                            _data.fingerprint, result.keys[0]); -                        if (newdata instanceof Error){ -                            reject(gpgme_error('KEY_INVALID')); -                        } else { -                            _data = newdata; -                            me.getGnupgSecretState().then(function(){ -                                me.getArmor().then(function(){ -                                    resolve(me); -                                }, function(error){ -                                    reject(error); -                                }); +    /** +     * 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; +        return new Promise(function(resolve, reject) { +            if (!me._data.fingerprint){ +                reject(gpgme_error('KEY_INVALID')); +            } +            let msg = createMessage('keylist'); +            msg.setParameter('sigs', true); +            msg.setParameter('keys', me._data.fingerprint); +            msg.post().then(function(result){ +                if (result.keys.length === 1){ +                    const newdata = validateKeyData( +                        me._data.fingerprint, result.keys[0]); +                    if (newdata instanceof Error){ +                        reject(gpgme_error('KEY_INVALID')); +                    } else { +                        me._data = newdata; +                        me.getGnupgSecretState().then(function(){ +                            me.getArmor().then(function(){ +                                resolve(me);                              }, function(error){                                  reject(error);                              }); -                        } -                    } else { -                        reject(gpgme_error('KEY_NOKEY')); +                        }, function(error){ +                            reject(error); +                        });                      } -                }, 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<String|GPGME_Error>} -         * @async -         */ -        this.getArmor = function(){ -            return new Promise(function(resolve, reject) { -                if (!_data.fingerprint){ -                    reject(gpgme_error('KEY_INVALID')); +                } else { +                    reject(gpgme_error('KEY_NOKEY'));                  } -                let msg = createMessage('export'); -                msg.setParameter('armor', true); -                msg.setParameter('keys', _data.fingerprint); -                msg.post().then(function(result){ -                    resolve(result.data); -                }, function(error){ -                    reject(error); -                }); +            }, function (error) { +                reject(gpgme_error('GNUPG_ERROR'), error);              }); -        }; +        }); +    } -        /** -         * Find out if the Key is part of a Key pair including public and -         * private key(s). If you want this information about more than a few -         * Keys in synchronous mode, it may be advisable to run -         * {@link Keyring.getKeys} instead, as it performs faster in bulk -         * querying this state. -         * @returns {Promise<Boolean|GPGME_Error>} True if a private Key is -         * available in the gnupg Keyring. -         * @async -         */ -        this.getGnupgSecretState = function (){ -            return new Promise(function(resolve, reject) { -                if (!_data.fingerprint){ -                    reject(gpgme_error('KEY_INVALID')); -                } else { -                    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); -                    }); -                } +    /** +     * 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() { +        const me = this; +        return new Promise(function(resolve, reject) { +            if (!me._data.fingerprint){ +                reject(gpgme_error('KEY_INVALID')); +            } +            let msg = createMessage('export'); +            msg.setParameter('armor', true); +            msg.setParameter('keys', me._data.fingerprint); +            msg.post().then(function(result){ +                resolve(result.data); +            }, 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<Boolean|GPGME_Error>} 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); +    /** +     * Find out if the Key is part of a Key pair including public and +     * private key(s). If you want this information about more than a few +     * Keys in synchronous mode, it may be advisable to run +     * {@link Keyring.getKeys} instead, as it performs faster in bulk +     * querying this state. +     * @returns {Promise<Boolean|GPGME_Error>} True if a private Key is +     * available in the gnupg Keyring. +     * @async +     */ +    getGnupgSecretState (){ +        const me = this; +        return new Promise(function(resolve, reject) { +            if (!me._data.fingerprint){ +                reject(gpgme_error('KEY_INVALID')); +            } else { +                let msg = createMessage('keylist'); +                msg.setParameter('keys', me._data.fingerprint); +                msg.setParameter('secret', true);                  msg.post().then(function(result){ -                    resolve(result.success); +                    me._data.hasSecret = null; +                    if ( +                        result.keys && +                        result.keys.length === 1 && +                        result.keys[0].secret === true +                    ) { +                        me._data.hasSecret = true; +                        resolve(true); +                    } else { +                        me._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<Boolean|GPGME_Error>} Success if key was deleted, +     * rejects with a GPG error otherwise. +     */ +    delete(){ +        const me = this; +        return new Promise(function(resolve, reject){ +            if (!me._data.fingerprint){ +                reject(gpgme_error('KEY_INVALID')); +            } +            let msg = createMessage('delete'); +            msg.setParameter('key', me._data.fingerprint); +            msg.post().then(function(result){ +                resolve(result.success); +            }, function(error){ +                reject(error);              }); -        }; +        });      }      /**       * @returns {String} The fingerprint defining this Key. Convenience getter       */      get fingerprint(){ -        return this.getFingerprint(); +        return this._data.fingerprint;      }  } @@ -259,8 +256,9 @@ class GPGME_Subkey {       * @private       */      constructor(data){ -        let _data = {}; +        this._data = {};          let keys = Object.keys(data); +        const me = this;          /**           * Validates a subkey property against {@link validSubKeyProperties} and @@ -273,9 +271,9 @@ class GPGME_Subkey {              if (validSubKeyProperties.hasOwnProperty(property)){                  if (validSubKeyProperties[property](value) === true) {                      if (property === 'timestamp' || property === 'expires'){ -                        _data[property] = new Date(value * 1000); +                        me._data[property] = new Date(value * 1000);                      } else { -                        _data[property] = value; +                        me._data[property] = value;                      }                  }              } @@ -283,18 +281,19 @@ class GPGME_Subkey {          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} +     */ +    get(property) { +        if (this._data.hasOwnProperty(property)){ +            return (this._data[property]); +        }      } +  }  /** @@ -310,15 +309,16 @@ class GPGME_UserId {       * @private       */      constructor(data){ -        let _data = {}; +        this._data = {}; +        const me = this;          let keys = Object.keys(data);          const setProperty = function(property, value){              if (validUserIdProperties.hasOwnProperty(property)){                  if (validUserIdProperties[property](value) === true) {                      if (property === 'last_update'){ -                        _data[property] = new Date(value*1000); +                        me._data[property] = new Date(value*1000);                      } else { -                        _data[property] = value; +                        me._data[property] = value;                      }                  }              } @@ -326,18 +326,19 @@ class GPGME_UserId {          for (let i=0; i< keys.length; i++) {              setProperty(keys[i], data[keys[i]]);          } +    } -        /** -         * 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} +     */ +    get(property) { +        if (this._data.hasOwnProperty(property)){ +            return (this._data[property]); +        }      } +  }  /** @@ -569,10 +570,11 @@ const validKeyProperties = {  * an error if something went wrong.  * @private  */ -function validateKeyData(data){ +function validateKeyData(fingerprint, data){      const key = {}; -    if ( typeof(data) !== 'object' -    || !data.fingerprint){ +    if (!fingerprint || typeof(data) !== 'object' || !data.fingerprint +     || fingerprint !== data.fingerprint.toUpperCase() +    ){          return gpgme_error('KEY_INVALID');      }      let props = Object.keys(data); @@ -588,15 +590,15 @@ function validateKeyData(data){          case 'subkeys':              key.subkeys = [];              for (let i=0; i< data.subkeys.length; i++) { -                key.subkeys.push(Object.freeze( -                    new GPGME_Subkey(data.subkeys[i]))); +                key.subkeys.push( +                    new GPGME_Subkey(data.subkeys[i]));              }              break;          case 'userids':              key.userids = [];              for (let i=0; i< data.userids.length; i++) { -                key.userids.push(Object.freeze( -                    new GPGME_UserId(data.userids[i]))); +                key.userids.push( +                    new GPGME_UserId(data.userids[i]));              }              break;          case 'last_update': @@ -623,19 +625,19 @@ function getGnupgState (fingerprint, property){          } else {              let msg = createMessage('keylist');              msg.setParameter('keys', fingerprint); -            msg.post().then(function(result){ -                if (!result.keys || result.keys.length !== 1){ +            msg.post().then(function(res){ +                if (!res.keys || res.keys.length !== 1){                      reject(gpgme_error('KEY_INVALID'));                  } else { -                    const key = result.keys[0]; +                    const key = res.keys[0];                      let result;                      switch (property){                      case 'subkeys':                          result = [];                          if (key.subkeys.length){                              for (let i=0; i < key.subkeys.length; i++) { -                                result.push(Object.freeze( -                                    new GPGME_Subkey(key.subkeys[i]))); +                                result.push( +                                    new GPGME_Subkey(key.subkeys[i]));                              }                          }                          resolve(result); @@ -644,8 +646,8 @@ function getGnupgState (fingerprint, property){                          result = [];                          if (key.userids.length){                              for (let i=0; i< key.userids.length; i++) { -                                result.push(Object.freeze( -                                    new GPGME_UserId(key.userids[i]))); +                                result.push( +                                    new GPGME_UserId(key.userids[i]));                              }                          }                          resolve(result); diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index d18fb649..de21736e 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -32,383 +32,384 @@ import { gpgme_error } from './Errors';   */  export class GPGME_Keyring {      constructor(){ +    } -        /** -         * 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, most data -         * (with the exception of armored Key blocks) will be cached for the -         * Keys. This enables direct, synchronous use of these properties for -         * all keys. It does not check for changes on the backend. The cached -         * information can be updated with the {@link Key.refresh} method. -         * @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>>} -         * @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([]); +    /** +     * 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, most data +     * (with the exception of armored Key blocks) will be cached for the +     * Keys. This enables direct, synchronous use of these properties for +     * all keys. It does not check for changes on the backend. The cached +     * information can be updated with the {@link Key.refresh} method. +     * @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>>} +     * @static +     * @async +     */ +    getKeys (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(); +                        };                      } else { -                        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){ -                                    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){ -                                                    b.hasSecret = true; -                                                } else { -                                                    b.hasSecret = false; -                                                } -                                                break; +                        secondrequest = function() { +                            return Promise.resolve(true); +                        }; +                    } +                    secondrequest().then(function(answer) { +                        for (let i=0; i < result.keys.length; i++){ +                            if (prepare_sync === true){ +                                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){ +                                                b.hasSecret = true; +                                            } else { +                                                b.hasSecret = false;                                              } +                                            break;                                          }                                      }                                  } -                                let k = createKey(result.keys[i].fingerprint, -                                    !prepare_sync, result.keys[i]); -                                resultset.push(k);                              } -                            resolve(resultset); -                        }, function(error){ -                            reject(error); -                        }); -                    } -                }); +                            let k = createKey(result.keys[i].fingerprint, +                                !prepare_sync, 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<String>} 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. -         */ +    /** +     * @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<String>} 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<String>} 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<exportResult|GPGME_Error>} 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); +    /** +     * 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<String>} 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<exportResult|GPGME_Error>} Object containing the +     * armored Key(s) and additional information. +     * @static +     * @async +     */ +    getKeysArmored (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'];                  } -                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); -                }); +                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<GPGME_Key|GPGME_Error>} -         * @async -         * @static -         */ -        this.getDefaultKey = function(prepare_sync = false) { -            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(resp){ -                    if (resp.option !== undefined -                        && resp.option.hasOwnProperty('value') -                        && resp.option.value.length === 1 -                        && resp.option.value[0].hasOwnProperty('string') -                        && typeof(resp.option.value[0].string) === 'string'){ -                        me.getKeys(resp.option.value[0].string, true).then( -                            function(keys){ -                                if(keys.length === 1){ -                                    resolve(keys[0]); -                                } else { -                                    reject(gpgme_error('KEY_NO_DEFAULT')); -                                } -                            }, function(error){ -                                reject(error); -                            }); -                    } else { -                        let msg = createMessage('keylist'); -                        msg.setParameter('secret', true); -                        msg.post().then(function(result){ -                            if (result.keys.length === 0){ -                                reject(gpgme_error('KEY_NO_DEFAULT')); +    /** +     * 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<GPGME_Key|GPGME_Error>} +     * @async +     * @static +     */ +    getDefaultKey(prepare_sync = false) { +        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(resp){ +                if (resp.option !== undefined +                    && resp.option.hasOwnProperty('value') +                    && resp.option.value.length === 1 +                    && resp.option.value[0].hasOwnProperty('string') +                    && typeof(resp.option.value[0].string) === 'string'){ +                    me.getKeys(resp.option.value[0].string, true).then( +                        function(keys){ +                            if(keys.length === 1){ +                                resolve(keys[0]);                              } else { -                                for (let i=0; i< result.keys.length; i++ ) { -                                    if (result.keys[i].invalid === false) { -                                        let k = createKey( -                                            result.keys[i].fingerprint, -                                            !prepare_sync, -                                            result.keys[i]); -                                        resolve(k); -                                        break; -                                    } else if (i === result.keys.length - 1){ -                                        reject(gpgme_error('KEY_NO_DEFAULT')); -                                    } -                                } +                                reject(gpgme_error('KEY_NO_DEFAULT'));                              }                          }, function(error){                              reject(error);                          }); -                    } -                }, function(error){ -                    reject(error); -                }); +                } else { +                    let msg = createMessage('keylist'); +                    msg.setParameter('secret', true); +                    msg.post().then(function(result){ +                        if (result.keys.length === 0){ +                            reject(gpgme_error('KEY_NO_DEFAULT')); +                        } else { +                            for (let i=0; i< result.keys.length; i++ ) { +                                if (result.keys[i].invalid === false) { +                                    let k = createKey( +                                        result.keys[i].fingerprint, +                                        !prepare_sync, +                                        result.keys[i]); +                                    resolve(k); +                                    break; +                                } else if (i === result.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<importedKeyResult>} Keys Array of Object containing -         * GPGME_Keys with additional import information -         * -         */ +    /** +     * @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 -         */ +    /** +     * @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<importResult>} 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 = []; -                    let summary = {}; -                    for (let i=0; i < feedbackValues.length; i++ ){ -                        summary[feedbackValues[i]] = -                            response.result[feedbackValues[i]]; -                    } -                    if (!response.result.hasOwnProperty('imports') || -                        response.result.imports.length === 0 -                    ){ -                        resolve({Keys:[],summary: summary}); -                        return; +    /** +     * 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<importResult>} A summary and Keys considered. +     * @async +     * @static +     */ +    importKey (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 = []; +                let summary = {}; +                for (let i=0; i < feedbackValues.length; i++ ){ +                    summary[feedbackValues[i]] = +                        response.result[feedbackValues[i]]; +                } +                if (!response.result.hasOwnProperty('imports') || +                    response.result.imports.length === 0 +                ){ +                    resolve({Keys:[],summary: summary}); +                    return; +                } +                for (let res=0; res<response.result.imports.length; res++){ +                    let result = response.result.imports[res]; +                    let status = ''; +                    if (result.status === 0){ +                        status = 'nochange'; +                    } else if ((result.status & 1) === 1){ +                        status = 'newkey'; +                    } else { +                        status = 'change';                      } -                    for (let res=0; res<response.result.imports.length; res++){ -                        let result = response.result.imports[res]; -                        let status = ''; -                        if (result.status === 0){ -                            status = 'nochange'; -                        } else if ((result.status & 1) === 1){ -                            status = 'newkey'; -                        } else { -                            status = 'change'; -                        } -                        let changes = {}; -                        changes.userId = (result.status & 2) === 2; -                        changes.signature = (result.status & 4) === 4; -                        changes.subkey = (result.status & 8) === 8; -                        //16 new secret key: not implemented +                    let changes = {}; +                    changes.userId = (result.status & 2) === 2; +                    changes.signature = (result.status & 4) === 4; +                    changes.subkey = (result.status & 8) === 8; +                    //16 new secret key: not implemented -                        fprs.push(result.fingerprint); -                        infos[result.fingerprint] = { -                            changes: changes, -                            status: status -                        }; -                    } -                    let resultset = []; -                    if (prepare_sync === true){ -                        me.getKeys(fprs, true).then(function(result){ -                            for (let i=0; i < result.length; i++) { -                                resultset.push({ -                                    key: result[i], -                                    changes: -                                        infos[result[i].fingerprint].changes, -                                    status: infos[result[i].fingerprint].status -                                }); -                            } -                            resolve({Keys:resultset,summary: summary}); -                        }, function(error){ -                            reject(error); -                        }); -                    } else { -                        for (let i=0; i < fprs.length; i++) { +                    fprs.push(result.fingerprint); +                    infos[result.fingerprint] = { +                        changes: changes, +                        status: status +                    }; +                } +                let resultset = []; +                if (prepare_sync === true){ +                    me.getKeys(fprs, true).then(function(result){ +                        for (let i=0; i < result.length; i++) {                              resultset.push({ -                                key: createKey(fprs[i]), -                                changes: infos[fprs[i]].changes, -                                status: infos[fprs[i]].status +                                key: result[i], +                                changes: +                                    infos[result[i].fingerprint].changes, +                                status: infos[result[i].fingerprint].status                              });                          } -                        resolve({Keys:resultset,summary:summary}); +                        resolve({Keys:resultset,summary: summary}); +                    }, function(error){ +                        reject(error); +                    }); +                } else { +                    for (let i=0; i < fprs.length; i++) { +                        resultset.push({ +                            key: createKey(fprs[i]), +                            changes: infos[fprs[i]].changes, +                            status: infos[fprs[i]].status +                        });                      } +                    resolve({Keys:resultset,summary:summary}); +                } -                }, function(error){ -                    reject(error); -                }); +            }, function(error){ +                reject(error); +            }); -            }); +        }); -        }; +    } -        /** -         * 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 -         */ -        this.deleteKey = function(fingerprint){ -            if (isFingerprint(fingerprint) === true) { -                let key = createKey(fingerprint); -                return key.delete(); -            } else { -                return Promise.reject(gpgme_error('KEY_INVALID')); -            } -        }; +    /** +     * 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); +            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 <[email protected]>' -         * @param {String} algo (optional) algorithm (and optionally key size) -         * to be used. See {@link supportedKeyAlgos} below for supported -         * values. -         * @param {Date} expires (optional) Expiration date. If not set, -         * expiration will be set to 'never' -         * -         * @return {Promise<Key|GPGME_Error>} -         * @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')); +    /** +     * 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 <[email protected]>' +     * @param {String} algo (optional) algorithm (and optionally key size) +     * to be used. See {@link supportedKeyAlgos} below for supported +     * values. +     * @param {Date} expires (optional) Expiration date. If not set, +     * expiration will be set to 'never' +     * +     * @return {Promise<Key|GPGME_Error>} +     * @async +     */ +    generateKey(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)); +            } else { +                msg.setParameter('expires', 0);              } -            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)); -                } else { -                    msg.setParameter('expires', 0); -                } -                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); -                }); +            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);              }); -        }; +        });      }  } +  /**   * List of algorithms supported for key generation. Please refer to the gnupg   * documentation for details diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js index e2c07344..2134fe99 100644 --- a/lang/js/src/Message.js +++ b/lang/js/src/Message.js @@ -36,7 +36,7 @@ export function createMessage(operation){          return gpgme_error('PARAM_WRONG');      }      if (permittedOperations.hasOwnProperty(operation)){ -        return Object.freeze(new GPGME_Message(operation)); +        return new GPGME_Message(operation);      } else {          return gpgme_error('MSG_WRONG_OP');      } @@ -52,199 +52,188 @@ export function createMessage(operation){  export class GPGME_Message {      constructor(operation){ -        let _msg = { +        this._msg = {              op: operation,              chunksize: 1023* 1024          }; -        let expected = null; +        this._expected = null; +    } -        this.getOperation = function(){ -            return _msg.op; -        }; +    get operation(){ +        return this._msg.op; +    } -        this.setExpect = function(value){ -            if (value === 'base64'){ -                expected = value; -            } -        }; -        this.getExpect = function(){ -            return expected; -        }; +    set expected (value){ +        if (value === 'base64'){ +            this._expected = value; +        } +    } -        /** -         * 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; -            } -        }; +    get expected() { +        return this._expected; +    } +    /** +     * 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 ( +            Number.isInteger(value) && +            value > 10 * 1024 && +            value <= 1024 * 1024 +        ){ +            this._msg.chunksize = value; +        } +    } -        this.getMsg = function(){ -            return _msg; -        }; +    get chunksize(){ +        return this._msg.chunksize; +    } -        this.getChunksize= function() { -            return _msg.chunksize; -        }; +    /** +     * Returns the prepared message with parameters and completeness checked +     * @returns {Object|null} Object to be posted to gnupg, or null if +     * incomplete +     */ +    get message() { +        if (this.isComplete() === true){ +            return this._msg; +        } else { +            return null; +        } +    } -        /** -         * 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'){ +    /** +     * 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 +     */ +    setParameter ( param,value ){ +        if (!param || typeof(param) !== 'string'){ +            return gpgme_error('PARAM_WRONG'); +        } +        let po = permittedOperations[this._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'); -            } -            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 { +            case 'number': +                if ( +                    poparam.allowed.indexOf('number') >= 0 +                        && isNaN(value) === false){ +                    return true; +                }                  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; + +            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');                      } -                    return gpgme_error('PARAM_WRONG'); -                case 'number': -                    if ( -                        poparam.allowed.indexOf('number') >= 0 -                            && isNaN(value) === false){ +                    for (let i=0; i < val.length; i++){ +                        let res = checktype(val[i]); +                        if (res !== true){ +                            return res; +                        } +                    } +                    if (val.length > 0) {                          return true;                      } -                    return gpgme_error('PARAM_WRONG'); - -                case 'boolean': -                    if (poparam.allowed.indexOf('boolean') >= 0){ +                } else if (val instanceof Uint8Array){ +                    if (poparam.allowed.indexOf('Uint8Array') >= 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){ +                } else {                      return gpgme_error('PARAM_WRONG');                  } +                break; +            default: +                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){ -                    return false; -                } +        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');              } -            return true; -        }; -        /** -         * Sends the Message via nativeMessaging and resolves with the answer. -         * @returns {Promise<Object|GPGME_Error>} -         * @async -         */ -        this.post = function(){ -            let me = this; -            return new Promise(function(resolve, reject) { -                if (me.isComplete() === true) { - -                    let conn  = Object.freeze(new Connection); -                    conn.post(me).then(function(response) { -                        resolve(response); -                    }, function(reason) { -                        reject(reason); -                    }); -                } -                else { -                    reject(gpgme_error('MSG_INCOMPLETE')); -                } -            }); -        }; +        } +        this._msg[param] = value; +        return true;      } +      /** -     * Returns the prepared message with parameters and completeness checked -     * @returns {Object|null} Object to be posted to gnupg, or null if -     * incomplete +     * 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 message(){ -        if (this.isComplete() === true){ -            return this.getMsg(); +    isComplete(){ +        if (!this._msg.op){ +            return false;          } -        else { -            return null; +        let reqParams = Object.keys( +            permittedOperations[this._msg.op].required); +        let msg_params = Object.keys(this._msg); +        for (let i=0; i < reqParams.length; i++){ +            if (msg_params.indexOf(reqParams[i]) < 0){ +                return false; +            }          } +        return true;      } -    get operation(){ -        return this.getOperation(); -    } -    get chunksize(){ -        return this.getChunksize(); +    /** +     * 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; +                conn.post(me).then(function(response) { +                    resolve(response); +                }, function(reason) { +                    reject(reason); +                }); +            } +            else { +                reject(gpgme_error('MSG_INCOMPLETE')); +            } +        });      } +  } diff --git a/lang/js/src/Signature.js b/lang/js/src/Signature.js index 55131b01..65365772 100644 --- a/lang/js/src/Signature.js +++ b/lang/js/src/Signature.js @@ -66,7 +66,7 @@ export function createSignature(sigObject){              }          }      } -    return Object.freeze(new GPGME_Signature(sigObject)); +    return new GPGME_Signature(sigObject);  } @@ -82,101 +82,65 @@ export function createSignature(sigObject){  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]]; -                } -            } -            return result; -        }; +        this._rawSigObject = sigObject;      } - -    /** -     * Convenience getter for {@link getFingerprint} -     */      get fingerprint(){ -        return this.getFingerprint(); +        if (!this._rawSigObject.fingerprint){ +            return gpgme_error('SIG_WRONG'); +        } else { +            return this._rawSigObject.fingerprint; +        }      }      /** -     * Convenience getter for {@link getExpiration} +     * The expiration of this Signature as Javascript date, or null if +     * signature does not expire +     * @returns {Date | null}       */      get expiration(){ -        return this.getExpiration(); +        if (!this._rawSigObject.exp_timestamp){ +            return null; +        } +        return new Date(this._rawSigObject.exp_timestamp* 1000);      }      /** -     * Convenience getter for {@link getTimeStamp} +     * The creation date of this Signature in Javascript Date +     * @returns {Date}       */ -    get timestamp(){ -        return this.getTimestamp(); +    get timestamp (){ +        return new Date(this._rawSigObject.timestamp * 1000);      }      /** -     * Convenience getter for {@link getValid} +     * The overall validity of the key. If false, errorDetails may contain +     * additional information.       */ -    get valid(){ -        return this.getValid(); +    get valid () { +        if (this._rawSigObject.summary.valid === true){ +            return true; +        } else { +            return false; +        }      }      /** -     * Convenience getter for {@link getErrorDetails} +     * 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       */      get errorDetails(){ -        return this.getErrorDetails(); +        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 ( this._rawSigObject.hasOwnProperty(properties[i]) ){ +                result[properties[i]] = this._rawSigObject[properties[i]]; +            } +        } +        return result;      }  } diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index 18164366..4aa51759 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -85,243 +85,226 @@ import { createSignature } from './Signature';  export class GpgME {      constructor(){ -        let _Keyring = null; - -        /** -         * Sets a new Keyring to be used -         * @param {GPGME_Keyring} keyring -         */ -        this.setKeyring = function(keyring){ -            if (keyring && keyring instanceof GPGME_Keyring){ -                _Keyring = keyring; -            } -        }; +        this._Keyring = null; +    } -        /** -         * Accesses the {@link GPGME_Keyring}. -         */ -        this.getKeyring = function(){ -            if (!_Keyring){ -                _Keyring = Object.freeze(new GPGME_Keyring); -            } -            return _Keyring; -        }; +    /** +     * setter for {@link setKeyring}. +     * @param {GPGME_Keyring} keyring A Keyring to use +     */ +    set Keyring(keyring){ +        if (keyring && keyring instanceof GPGME_Keyring){ +            this._Keyring = keyring; +        } +    } +    /** +     * Accesses the {@link GPGME_Keyring}. +     */ +    get Keyring(){ +        if (!this._Keyring){ +            this._Keyring = new GPGME_Keyring; +        } +        return this._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<encrypt_result>} 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]]); -                } -            } -            if (msg.isComplete() === true){ -                return msg.post(); -            } else { -                return Promise.reject(gpgme_error('MSG_INCOMPLETE')); +    /** +     * 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<encrypt_result>} Object containing the encrypted +     * message and additional info. +     * @async +     */ +    encrypt (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]]);              } -        }; +        } +        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<decrypt_result>} 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'); +    /** +    * 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<decrypt_result>} Decrypted Message and information +    * @async +    */ +    decrypt (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.is_mime ? true: false; -                    if (result.file_name){ -                        _result.file_name = result.file_name; -                    } else { -                        _result.file_name = null; -                    } -                    if ( -                        result.hasOwnProperty('signatures') && -                        Array.isArray(result.signatures) -                    ) { -                        _result.signatures = collectSignatures( -                            result.signatures); -                    } -                    resolve(_result); -                }, function(error){ -                    reject(error); -                }); +        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.is_mime ? true: false; +                if (result.file_name){ +                    _result.file_name = result.file_name; +                } else { +                    _result.file_name = null; +                } +                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<signResult>} -         * @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'); +    /** +     * 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<signResult>} +     * @async +     */ +    sign (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('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.expected ='base64';              } -            msg.setParameter('mode', mode); -            putData(msg, data); -            return new Promise(function(resolve,reject) { -                if (mode ==='detached'){ -                    msg.setExpect('base64'); +            msg.post().then( function(message) { +                if (mode === 'clearsign'){ +                    resolve({ +                        data: message.data} +                    ); +                } else if (mode === 'detached') { +                    resolve({ +                        data: data, +                        signature: message.data +                    });                  } -                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); -                }); +            }, 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<verifyResult>} -         *@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); +    /** +     * 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<verifyResult>} +     *@async +    */ +    verify (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 (signature){ -                if (typeof(signature)!== 'string'){ -                    return Promise.reject(gpgme_error('PARAM_WRONG')); +        } +        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 { -                    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); +                    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;                      } -                }, function(error){ -                    reject(error); -                }); +                    _result.data = message.data; +                    resolve(_result); +                } +            }, function(error){ +                reject(error);              }); -        }; -    } - -    /** -     * setter for {@link setKeyring}. -     * @param {GPGME_Keyring} keyring A Keyring to use -     */ -    set Keyring(keyring){ -        this.setKeyring(keyring); -    } - -    /** -     * Accesses the {@link GPGME_Keyring}. -     */ -    get Keyring(){ -        return this.getKeyring(); +        });      }  } diff --git a/lang/js/src/index.js b/lang/js/src/index.js index 51f07538..ad4b05b0 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -34,11 +34,11 @@ import { Connection } from './Connection';   */  function init(){      return new Promise(function(resolve, reject){ -        const connection = Object.freeze(new Connection); +        const connection = new Connection;          connection.checkConnection(false).then(              function(result){                  if (result === true) { -                    resolve(Object.freeze(new GpgME())); +                    resolve(new GpgME());                  } else {                      reject(gpgme_error('CONN_NO_CONNECT'));                  } @@ -48,5 +48,5 @@ function init(){      });  } -const exportvalue = Object.freeze({init:init}); +const exportvalue = {init:init};  export default exportvalue;
\ No newline at end of file diff --git a/lang/js/unittests.js b/lang/js/unittests.js index 25023bcb..47eeabf2 100644 --- a/lang/js/unittests.js +++ b/lang/js/unittests.js @@ -27,7 +27,7 @@ import { key_params as kp } from './unittest_inputvalues';  import { Connection } from './src/Connection';  import { gpgme_error } from './src/Errors';  import { toKeyIdArray , isFingerprint } from './src/Helpers'; -import { GPGME_Key , createKey } from './src/Key'; +import { createKey } from './src/Key';  import { GPGME_Keyring } from './src/Keyring';  import {GPGME_Message, createMessage} from './src/Message'; @@ -116,14 +116,6 @@ function unittests (){              expect(test0).to.include(hp.validFingerprint);          }); -        it('correct GPGME_Key', function(){ -            expect(hp.validGPGME_Key).to.be.an.instanceof(GPGME_Key); -            let test0 = toKeyIdArray(hp.validGPGME_Key); - -            expect(test0).to.be.an('array'); -            expect(test0).to.include(hp.validGPGME_Key.fingerprint); -        }); -          it('openpgpjs-like object', function(){              let test0 = toKeyIdArray(hp.valid_openpgplike); @@ -169,15 +161,9 @@ function unittests (){      });      describe('GPGME_Key', function(){ - -        it('correct Key initialization', function(){ -            let key = createKey(kp.validKeyFingerprint); -            expect(key).to.be.an.instanceof(GPGME_Key); -        });          it('Key has data after a first refresh', function(done) {              let key = createKey(kp.validKeyFingerprint);              key.refreshKey().then(function(key2){ -                expect(key2).to.be.an.instanceof(GPGME_Key);                  expect(key2.get).to.be.a('function');                  for (let i=0; i < kp.validKeyProperties.length; i++) {                      let prop = key2.get(kp.validKeyProperties[i]); @@ -220,7 +206,6 @@ function unittests (){          it('Non-cached key async hasSecret (no secret in Key)', function (done){              let key = createKey(kp.validFingerprintNoSecret, true); -            expect(key).to.be.an.instanceof(GPGME_Key);              key.get('hasSecret').then(function(result){                  expect(result).to.be.a('boolean');                  expect(result).to.equal(false); @@ -246,32 +231,21 @@ function unittests (){              }          }); -        it('malformed GPGME_Key cannot be used', function(){ -            for (let i=0; i < 4; i++){ -                let key = new GPGME_Key(wp.four_invalid_params[i]); -                expect(key.fingerprint).to.be.an.instanceof(Error); -                expect(key.fingerprint.code).to.equal('KEY_INVALID'); -            } -        }); - -        it('Overwriting getFingerprint does not work', function(){ -            const evilFunction = function(){ -                return 'bad Data'; -            }; -            let key = createKey(kp.validKeyFingerprint, true); -            expect(key.fingerprint).to.equal(kp.validKeyFingerprint); -            try { -                key.getFingerprint = evilFunction; -            } -            catch(e) { -                expect(e).to.be.an.instanceof(TypeError); -            } -            expect(key.fingerprint).to.equal(kp.validKeyFingerprint); -            expect(key.getFingerprint).to.not.equal(evilFunction); -        }); -        // TODO: tests for subkeys -        // TODO: tests for userids -        // TODO: some invalid tests for key/keyring +    //     it('Overwriting getFingerprint does not work', function(){ +    //         const evilFunction = function(){ +    //             return 'bad Data'; +    //         }; +    //         let key = createKey(kp.validKeyFingerprint, true); +    //         expect(key.fingerprint).to.equal(kp.validKeyFingerprint); +    //         try { +    //             key.getFingerprint = evilFunction; +    //         } +    //         catch(e) { +    //             expect(e).to.be.an.instanceof(TypeError); +    //         } +    //         expect(key.fingerprint).to.equal(kp.validKeyFingerprint); +    //         expect(key.getFingerprint).to.not.equal(evilFunction); +    //     });      });      describe('GPGME_Keyring', function(){ @@ -287,10 +261,7 @@ function unittests (){                  let keyring = new GPGME_Keyring;                  keyring.getKeys(null, true).then(function(result){                      expect(result).to.be.an('array'); -                    expect(result[0]).to.be.an.instanceof(GPGME_Key);                      expect(result[0].get('hasSecret')).to.be.a('boolean'); -                    // expect(result[0].get('armored')).to.include( -                    //     '-----END PGP PUBLIC KEY BLOCK-----');                      done();                  });              } @@ -302,7 +273,6 @@ function unittests (){                  keyring.getKeys(kp.validKeyFingerprint, true).then(                      function(result){                          expect(result).to.be.an('array'); -                        expect(result[0]).to.be.an.instanceof(GPGME_Key);                          expect(result[0].get('hasSecret')).to.be.a('boolean');                          done();                      } | 
