diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js index 0fd14994..accc2af5 100644 --- a/lang/js/src/Helpers.js +++ b/lang/js/src/Helpers.js @@ -1,111 +1,110 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ * * Author(s): * Maximilian Krambach */ import { gpgme_error } from './Errors'; -import { GPGME_Key } from './Key'; /** * Tries to return an array of fingerprints, either from input fingerprints or * from Key objects (openpgp Keys or GPGME_Keys are both accepted). * * @param {Object | Array | String | Array} input * @returns {Array} Array of fingerprints, or an empty array */ export function toKeyIdArray(input){ if (!input){ return []; } if (!Array.isArray(input)){ input = [input]; } let result = []; for (let i=0; i < input.length; i++){ if (typeof(input[i]) === 'string'){ if (isFingerprint(input[i]) === true){ result.push(input[i]); } else { // MSG_NOT_A_FPR is just a console warning if warning enabled // in src/Errors.js gpgme_error('MSG_NOT_A_FPR'); } } else if (typeof(input[i]) === 'object'){ let fpr = ''; - if (input[i] instanceof GPGME_Key){ + if (input[i].hasOwnProperty('fingerprint')){ fpr = input[i].fingerprint; } else if (input[i].hasOwnProperty('primaryKey') && input[i].primaryKey.hasOwnProperty('getFingerprint')){ fpr = input[i].primaryKey.getFingerprint(); } if (isFingerprint(fpr) === true){ result.push(fpr); } else { gpgme_error('MSG_NOT_A_FPR'); } } else { return gpgme_error('PARAM_WRONG'); } } if (result.length === 0){ return []; } else { return result; } } /** * Check if values are valid hexadecimal values of a specified length * @param {String} key input value. * @param {int} len the expected length of the value * @returns {Boolean} true if value passes test * @private */ function hextest(key, len){ if (!key || typeof(key) !== 'string'){ return false; } if (key.length !== len){ return false; } let regexp= /^[0-9a-fA-F]*$/i; return regexp.test(key); } /** * check if the input is a valid Fingerprint * (Hex string with a length of 40 characters) * @param {String} value to check * @returns {Boolean} true if value passes test */ export function isFingerprint(value){ return hextest(value, 40); } /** * check if the input is a valid gnupg long ID (Hex string with a length of 16 * characters) * @param {String} value to check * @returns {Boolean} true if value passes test */ export function isLongId(value){ return hextest(value, 16); } diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js index aa419053..8d7fd948 100644 --- a/lang/js/src/Key.js +++ b/lang/js/src/Key.js @@ -1,604 +1,626 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ * * Author(s): * Maximilian Krambach */ import { isFingerprint, isLongId } from './Helpers'; import { gpgme_error } from './Errors'; import { createMessage } from './Message'; /** * Validates the given fingerprint and creates a new {@link GPGME_Key} * @param {String} fingerprint * @param {Boolean} async If True, Key properties (except fingerprint) will be * queried from gnupg on each call, making the operation up-to-date, the * answers will be Promises, and the performance will likely suffer - * @returns {GPGME_Key|GPGME_Error} + * @param {Object} data additional initial properties this Key will have. Needs + * a full object as delivered by gpgme-json + * @returns {Object|GPGME_Error} The verified and updated data */ -export function createKey(fingerprint, async = false){ +export function createKey(fingerprint, async = false, data){ if (!isFingerprint(fingerprint) || typeof(async) !== 'boolean'){ return gpgme_error('PARAM_WRONG'); } - else return Object.freeze(new GPGME_Key(fingerprint, async)); + if (data !== undefined){ + data = validateKeyData(data); + } + if (data instanceof Error){ + return gpgme_error('KEY_INVALID'); + } else { + return Object.freeze(new GPGME_Key(fingerprint, async, data)); + } } /** * Represents the Keys as stored in the gnupg backend * It allows to query almost all information defined in gpgme Key Objects * Refer to {@link validKeyProperties} for available information, and the gpgme * documentation on their meaning * (https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html) * * @class */ -export class GPGME_Key { +class GPGME_Key { - constructor(fingerprint, async){ + constructor(fingerprint, async, data){ /** * @property {Boolean} If true, most answers will be asynchronous */ this.isAsync = async; let _data = {fingerprint: fingerprint.toUpperCase()}; + if (data !== undefined + && data.fingerprint.toUpperCase() === _data.fingerprint + ) { + _data = data; + } this.getFingerprint = function(){ if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){ return gpgme_error('KEY_INVALID'); } return _data.fingerprint; }; /** * Property indicating if the Key possesses a private/secret part. If * this information is not yet cached, it returns an * {@link GPGME_Error} with code 'KEY_NO_INIT'. Running * {@link refreshKey} may help in this case. * @returns {Boolean} If the Key has a secret subkey. */ this.hasSecret= function (){ return this.get('hasSecret'); }; - /** - * @param {Object} data Bulk set the data for this key, with an Object - * sent by gpgme-json. - * @returns {GPGME_Key|GPGME_Error} Itself after values have been set, - * an error if something went wrong. - * @private - */ - this.setKeyData = function (data){ - if (typeof(data) !== 'object') { - return gpgme_error('KEY_INVALID'); - } - if (!data.fingerprint || - data.fingerprint.toUpperCase() !== _data.fingerprint){ - return gpgme_error('KEY_INVALID'); - } - let keys = Object.keys(data); - for (let i=0; i< keys.length; i++){ - if (!validKeyProperties.hasOwnProperty(keys[i])){ - return gpgme_error('KEY_INVALID'); - } - //running the defined validation function - if (validKeyProperties[keys[i]](data[keys[i]]) !== true ){ - return gpgme_error('KEY_INVALID'); - } - switch (keys[i]){ - case 'subkeys': - _data.subkeys = []; - for (let i=0; i< data.subkeys.length; i++) { - _data.subkeys.push(Object.freeze( - new GPGME_Subkey(data.subkeys[i]))); - } - break; - case 'userids': - _data.userids = []; - for (let i=0; i< data.userids.length; i++) { - _data.userids.push(Object.freeze( - new GPGME_UserId(data.userids[i]))); - } - break; - case 'last_update': - _data[keys[i]] = new Date( data[keys[i]] * 1000 ); - break; - default: - _data[keys[i]] = data[keys[i]]; - } - } - return this; - }; /** * Query any property of the Key listed in {@link validKeyProperties} * @param {String} property property to be retreived * @returns {*|Promise<*>} the value (Boolean, String, Array, Object). * If 'cached' is false, the value will be resolved as a Promise. */ this.get = function(property) { if (this.isAsync === true) { let me = this; return new Promise(function(resolve, reject) { if (property === 'armored'){ resolve(me.getArmor()); } else if (property === 'hasSecret'){ resolve(me.getHasSecret()); } else if (validKeyProperties.hasOwnProperty(property)){ let msg = createMessage('keylist'); msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ if (result.keys && result.keys.length === 1 && result.keys[0].hasOwnProperty(property)){ resolve(result.keys[0][property]); } else { reject(gpgme_error('CONN_UNEXPECTED_ANSWER')); } }, function(error){ reject(gpgme_error(error)); }); } else { reject(gpgme_error('PARAM_WRONG')); } }); } else { if (!validKeyProperties.hasOwnProperty(property)){ return gpgme_error('PARAM_WRONG'); } if (!_data.hasOwnProperty(property)){ return gpgme_error('KEY_NO_INIT'); } else { return (_data[property]); } } }; /** * Reloads the Key information from gnupg. This is only useful if you * use the GPGME_Keys cached. Note that this is a performance hungry * operation. If you desire more than a few refreshs, it may be * advisable to run {@link Keyring.getKeys} instead. * @returns {Promise} * @async */ this.refreshKey = function() { let me = this; return new Promise(function(resolve, reject) { if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('sigs', true); msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ if (result.keys.length === 1){ - me.setKeyData(result.keys[0]); - me.getHasSecret().then(function(){ - me.getArmor().then(function(){ - resolve(me); + const newdata = validateKeyData( + _data.fingerprint, result.keys[0]); + if (newdata instanceof Error){ + reject(gpgme_error('KEY_INVALID')); + } else { + _data = newdata; + me.getHasSecret().then(function(){ + me.getArmor().then(function(){ + resolve(me); + }, function(error){ + reject(error); + }); }, function(error){ reject(error); }); - }, function(error){ - reject(error); - }); + } } else { reject(gpgme_error('KEY_NOKEY')); } }, function (error) { reject(gpgme_error('GNUPG_ERROR'), error); }); }); }; /** * Query the armored block of the Key directly from gnupg. Please note * that this will not get you any export of the secret/private parts of * a Key * @returns {Promise} * @async */ this.getArmor = function(){ return new Promise(function(resolve, reject) { if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('export'); msg.setParameter('armor', true); msg.setParameter('keys', _data.fingerprint); msg.post().then(function(result){ _data.armored = result.data; resolve(result.data); }, function(error){ reject(error); }); }); }; /** * Find out if the Key includes a secret part. Note that this is a * rather nonperformant operation, as it needs to query gnupg twice. * If you want this inforrmation about more than a few Keys, it may be * advisable to run {@link Keyring.getKeys} instead. * @returns {Promise} True if a secret/private Key * is available in the gnupg Keyring * @async */ this.getHasSecret = function (){ return new Promise(function(resolve, reject) { if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('keylist'); msg.setParameter('keys', _data.fingerprint); msg.setParameter('secret', true); msg.post().then(function(result){ _data.hasSecret = null; if ( result.keys && result.keys.length === 1 && result.keys[0].secret === true ) { _data.hasSecret = true; resolve(true); } else { _data.hasSecret = false; resolve(false); } }, function(error){ reject(error); }); }); }; /** * Deletes the (public) Key from the GPG Keyring. Note that a deletion * of a secret key is not supported by the native backend. * @returns {Promise} Success if key was deleted, * rejects with a GPG error otherwise. */ this.delete= function (){ return new Promise(function(resolve, reject){ if (!_data.fingerprint){ reject(gpgme_error('KEY_INVALID')); } let msg = createMessage('delete'); msg.setParameter('key', _data.fingerprint); msg.post().then(function(result){ resolve(result.success); }, function(error){ reject(error); }); }); }; } /** * @returns {String} The fingerprint defining this Key */ get fingerprint(){ return this.getFingerprint(); } /** * Property for the export of armored Key. If the armored Key is not * cached, it returns an {@link GPGME_Error} with code 'KEY_NO_INIT'. * Running {@link refreshKey} may help in this case. * @returns {String|GPGME_Error} The armored public Key block. */ get armored(){ if (this.isAsync === true){ return gpgme_error('KEY_NO_INIT'); } else { return this.get('armored'); } } } /** * Representing a subkey of a Key. * @class * @protected */ class GPGME_Subkey { /** * Initializes with the json data sent by gpgme-json * @param {Object} data * @private */ constructor(data){ let _data = {}; let keys = Object.keys(data); /** * Validates a subkey property against {@link validSubKeyProperties} and * sets it if validation is successful * @param {String} property * @param {*} value * @param private */ const setProperty = function (property, value){ if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties[property](value) === true) { if (property === 'timestamp' || property === 'expires'){ _data[property] = new Date(value * 1000); } else { _data[property] = value; } } } }; 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]); } }; } } /** * Representing user attributes associated with a Key or subkey * @class * @protected */ class GPGME_UserId { /** * Initializes with the json data sent by gpgme-json * @param {Object} data * @private */ constructor(data){ let _data = {}; 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); } else { _data[property] = value; } } } }; 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]); } }; } } /** * Validation definition for userIds. Each valid userId property is represented * as a key- Value pair, with their value being a validation function to check * against * @protected * @const */ const validUserIdProperties = { 'revoked': function(value){ return typeof(value) === 'boolean'; }, 'invalid': function(value){ return typeof(value) === 'boolean'; }, 'uid': function(value){ if (typeof(value) === 'string' || value === ''){ return true; } return false; }, 'validity': function(value){ if (typeof(value) === 'string'){ return true; } return false; }, 'name': function(value){ if (typeof(value) === 'string' || value === ''){ return true; } return false; }, 'email': function(value){ if (typeof(value) === 'string' || value === ''){ return true; } return false; }, 'address': function(value){ if (typeof(value) === 'string' || value === ''){ return true; } return false; }, 'comment': function(value){ if (typeof(value) === 'string' || value === ''){ return true; } return false; }, 'origin': function(value){ return Number.isInteger(value); }, 'last_update': function(value){ return Number.isInteger(value); } }; /** * Validation definition for subKeys. Each valid userId property is represented * as a key-value pair, with the value being a validation function * @protected * @const */ const validSubKeyProperties = { 'invalid': function(value){ return typeof(value) === 'boolean'; }, 'can_encrypt': function(value){ return typeof(value) === 'boolean'; }, 'can_sign': function(value){ return typeof(value) === 'boolean'; }, 'can_certify': function(value){ return typeof(value) === 'boolean'; }, 'can_authenticate': function(value){ return typeof(value) === 'boolean'; }, 'secret': function(value){ return typeof(value) === 'boolean'; }, 'is_qualified': function(value){ return typeof(value) === 'boolean'; }, 'is_cardkey': function(value){ return typeof(value) === 'boolean'; }, 'is_de_vs': function(value){ return typeof(value) === 'boolean'; }, 'pubkey_algo_name': function(value){ return typeof(value) === 'string'; // TODO: check against list of known?[''] }, 'pubkey_algo_string': function(value){ return typeof(value) === 'string'; // TODO: check against list of known?[''] }, 'keyid': function(value){ return isLongId(value); }, 'pubkey_algo': function(value) { return (Number.isInteger(value) && value >= 0); }, 'length': function(value){ return (Number.isInteger(value) && value > 0); }, 'timestamp': function(value){ return (Number.isInteger(value) && value > 0); }, 'expires': function(value){ return (Number.isInteger(value) && value > 0); } }; /** * Validation definition for Keys. Each valid Key property is represented * as a key-value pair, with their value being a validation function * @protected * @const */ const validKeyProperties = { 'fingerprint': function(value){ return isFingerprint(value); }, 'armored': function(value){ return typeof(value === 'string'); }, 'revoked': function(value){ return typeof(value) === 'boolean'; }, 'expired': function(value){ return typeof(value) === 'boolean'; }, 'disabled': function(value){ return typeof(value) === 'boolean'; }, 'invalid': function(value){ return typeof(value) === 'boolean'; }, 'can_encrypt': function(value){ return typeof(value) === 'boolean'; }, 'can_sign': function(value){ return typeof(value) === 'boolean'; }, 'can_certify': function(value){ return typeof(value) === 'boolean'; }, 'can_authenticate': function(value){ return typeof(value) === 'boolean'; }, 'secret': function(value){ return typeof(value) === 'boolean'; }, 'is_qualified': function(value){ return typeof(value) === 'boolean'; }, 'protocol': function(value){ return typeof(value) === 'string'; //TODO check for implemented ones }, 'issuer_serial': function(value){ return typeof(value) === 'string'; }, 'issuer_name': function(value){ return typeof(value) === 'string'; }, 'chain_id': function(value){ return typeof(value) === 'string'; }, 'owner_trust': function(value){ return typeof(value) === 'string'; }, 'last_update': function(value){ return (Number.isInteger(value)); //TODO undefined/null possible? }, 'origin': function(value){ return (Number.isInteger(value)); }, 'subkeys': function(value){ return (Array.isArray(value)); }, 'userids': function(value){ return (Array.isArray(value)); }, 'tofu': function(value){ return (Array.isArray(value)); }, 'hasSecret': function(value){ return typeof(value) === 'boolean'; } }; + +/** +* sets the Key data in bulk. It can only be used from inside a Key, either +* during construction or on a refresh callback. +* @param {Object} key the original internal key data. +* @param {Object} data Bulk set the data for this key, with an Object structure +* as sent by gpgme-json. +* @returns {Object|GPGME_Error} the changed data after values have been set, +* an error if something went wrong. +* @private +*/ +function validateKeyData(data){ + const key = {}; + if ( typeof(data) !== 'object' + || !data.fingerprint){ + return gpgme_error('KEY_INVALID'); + } + let props = Object.keys(data); + for (let i=0; i< props.length; i++){ + if (!validKeyProperties.hasOwnProperty(props[i])){ + return gpgme_error('KEY_INVALID'); + } + // running the defined validation function + if (validKeyProperties[props[i]](data[props[i]]) !== true ){ + return gpgme_error('KEY_INVALID'); + } + switch (props[i]){ + case 'subkeys': + key.subkeys = []; + for (let i=0; i< data.subkeys.length; i++) { + key.subkeys.push(Object.freeze( + 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]))); + } + break; + case 'last_update': + key[props[i]] = new Date( data[props[i]] * 1000 ); + break; + default: + key[props[i]] = data[props[i]]; + } + } + return key; +} \ No newline at end of file diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js index a6787986..43ab96c8 100644 --- a/lang/js/src/Keyring.js +++ b/lang/js/src/Keyring.js @@ -1,424 +1,425 @@ /* gpgme.js - Javascript integration for gpgme * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik * * This file is part of GPGME. * * GPGME is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * GPGME is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ * * Author(s): * Maximilian Krambach */ import {createMessage} from './Message'; import {createKey} from './Key'; import { isFingerprint } from './Helpers'; import { gpgme_error } from './Errors'; /** * This class offers access to the gnupg keyring */ export class GPGME_Keyring { constructor(){ /** * Queries Keys (all Keys or a subset) from gnupg. * * @param {String | Array} pattern (optional) A pattern to * search for in userIds or KeyIds. * @param {Boolean} prepare_sync (optional) if set to true, the * 'hasSecret' and 'armored' properties will be fetched for the Keys as * well. These require additional calls to gnupg, resulting in a * performance hungry operation. Calling them here enables direct, * synchronous use of these properties for all keys, without having to * resort to a refresh() first. * @param {Boolean} search (optional) retrieve Keys from external * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup) * @returns {Promise>} * @static * @async */ this.getKeys = function(pattern, prepare_sync=false, search=false){ return new Promise(function(resolve, reject) { let msg = createMessage('keylist'); if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); } msg.setParameter('sigs', true); if (search === true){ msg.setParameter('locate', true); } msg.post().then(function(result){ let resultset = []; if (result.keys.length === 0){ resolve([]); } else { let secondrequest; if (prepare_sync === true) { secondrequest = function() { let msg2 = createMessage('keylist'); msg2.setParameter('keys', pattern); msg2.setParameter('secret', true); return msg2.post(); }; } 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; } } // TODO getArmor() to be used in sync } } - let k = createKey(result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); + 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} secret_fprs (optional) list of * fingerprints for those Keys that also have a secret Key available in * gnupg. The secret key will not be exported, but the fingerprint can * be used in operations needing a secret key. */ /** * Fetches the armored public Key blocks for all Keys matching the * pattern (if no pattern is given, fetches all keys known to gnupg). * @param {String|Array} pattern (optional) The Pattern to * search for * @param {Boolean} with_secret_fpr (optional) also return a list of * fingerprints for the keys that have a secret key available * @returns {Promise} Object containing the * armored Key(s) and additional information. * @static * @async */ this.getKeysArmored = function(pattern, with_secret_fpr) { return new Promise(function(resolve, reject) { let msg = createMessage('export'); msg.setParameter('armor', true); if (with_secret_fpr === true) { msg.setParameter('with-sec-fprs', true); } if (pattern !== undefined && pattern !== null){ msg.setParameter('keys', pattern); } msg.post().then(function(answer){ const result = {armored: answer.data}; if (with_secret_fpr === true && answer.hasOwnProperty('sec-fprs') ) { result.secret_fprs = answer['sec-fprs']; } resolve(result); }, function(error){ reject(error); }); }); }; /** * Returns the Key used by default in gnupg. * (a.k.a. 'primary Key or 'main key'). * It looks up the gpg configuration if set, or the first key that * contains a secret key. * * @returns {Promise} * @async * @static */ - this.getDefaultKey = function() { + 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')); } else { for (let i=0; i< result.keys.length; i++ ) { if (result.keys[i].invalid === false) { let k = createKey( - result.keys[i].fingerprint); - k.setKeyData(result.keys[i]); + 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} Keys Array of Object containing * GPGME_Keys with additional import information * */ /** * @typedef {Object} importedKeyResult * @property {GPGME_Key} key The resulting key * @property {String} status: * 'nochange' if the Key was not changed, * 'newkey' if the Key was imported in gpg, and did not exist * previously, * 'change' if the key existed, but details were updated. For details, * Key.changes is available. * @property {Boolean} changes.userId Changes in userIds * @property {Boolean} changes.signature Changes in signatures * @property {Boolean} changes.subkey Changes in subkeys */ /** * Import an armored Key block into gnupg. Note that this currently * will not succeed on private Key blocks. * @param {String} armored Armored Key block of the Key(s) to be * imported into gnupg * @param {Boolean} prepare_sync prepare the keys for synched use * (see {@link getKeys}). * @returns {Promise} A summary and Keys considered. * @async * @static */ this.importKey = function (armored, prepare_sync) { let feedbackValues = ['considered', 'no_user_id', 'imported', 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', 'new_signatures', 'new_revocations', 'secret_read', 'secret_imported', 'secret_unchanged', 'skipped_new_keys', 'not_imported', 'skipped_v3_keys']; if (!armored || typeof(armored) !== 'string'){ return Promise.reject(gpgme_error('PARAM_WRONG')); } let me = this; return new Promise(function(resolve, reject){ let msg = createMessage('import'); msg.setParameter('data', armored); msg.post().then(function(response){ let infos = {}; let fprs = []; 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} * @async * @static */ this.deleteKey = function(fingerprint){ if (isFingerprint(fingerprint) === true) { let key = createKey(fingerprint); return key.delete(); } else { return Promise.reject(gpgme_error('KEY_INVALID')); } }; /** * Generates a new Key pair directly in gpg, and returns a GPGME_Key * representing that Key. Please note that due to security concerns, * secret Keys can not be deleted or exported from inside gpgme.js. * * @param {String} userId The user Id, e.g. 'Foo Bar ' * @param {String} algo (optional) algorithm (and optionally key size) * to be used. See {@link supportedKeyAlgos} below for supported * values. * @param {Date} expires (optional) Expiration date. If not set, * expiration will be set to 'never' * * @return {Promise} * @async */ this.generateKey = function (userId, algo = 'default', expires){ if ( typeof(userId) !== 'string' || supportedKeyAlgos.indexOf(algo) < 0 || (expires && !(expires instanceof Date)) ){ return Promise.reject(gpgme_error('PARAM_WRONG')); } let me = this; return new Promise(function(resolve, reject){ let msg = createMessage('createkey'); msg.setParameter('userid', userId); msg.setParameter('algo', algo ); if (expires){ msg.setParameter('expires', Math.floor(expires.valueOf()/1000)); } 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 */ const supportedKeyAlgos = [ 'default', 'rsa', 'rsa2048', 'rsa3072', 'rsa4096', 'dsa', 'dsa2048', 'dsa3072', 'dsa4096', 'elg', 'elg2048', 'elg3072', 'elg4096', 'ed25519', 'cv25519', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'NIST P-256', 'NIST P-384', 'NIST P-521' ]; \ No newline at end of file