Blame lang/js/src/Keyring.js

Packit Service 30b792
/* gpgme.js - Javascript integration for gpgme
Packit Service 30b792
 * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1+
Packit Service 30b792
 *
Packit Service 30b792
 * Author(s):
Packit Service 30b792
 *     Maximilian Krambach <mkrambach@intevation.de>
Packit Service 30b792
 */
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
import { createMessage } from './Message';
Packit Service 30b792
import { createKey } from './Key';
Packit Service 30b792
import { isFingerprint } from './Helpers';
Packit Service 30b792
import { gpgme_error } from './Errors';
Packit Service 30b792
Packit Service 30b792
/**
Packit Service 30b792
 * This class offers access to the gnupg keyring
Packit Service 30b792
 */
Packit Service 30b792
export class GPGME_Keyring {
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Queries Keys (all Keys or a subset) from gnupg.
Packit Service 30b792
     *
Packit Service 30b792
     * @param {Object} options:
Packit Service 30b792
     * @param {String | Array<String>} options.pattern (optional) A pattern to
Packit Service 30b792
     * search for in userIds or KeyIds.
Packit Service 30b792
     * @param {Boolean} options.prepare_sync (optional) if set to true, most
Packit Service 30b792
     * data (with the exception of armored Key blocks) will be cached for the
Packit Service 30b792
     * Keys. This enables direct, synchronous use of these properties for
Packit Service 30b792
     * all keys. It does not check for changes on the backend. The cached
Packit Service 30b792
     * information can be updated with the {@link Key.refresh} method.
Packit Service 30b792
     * @param {Boolean} options.search (optional) retrieve Keys from external
Packit Service 30b792
     * servers with the method(s) defined in gnupg (e.g. WKD/HKP lookup)
Packit Service 30b792
     * @returns {Promise<GPGME_Key[]>}
Packit Service 30b792
     * @static
Packit Service 30b792
     * @async
Packit Service 30b792
     */
Packit Service 30b792
    getKeys ({ pattern, prepare_sync = false, search = false } = {}){
Packit Service 30b792
        if (typeof arguments[0] !== 'object') {
Packit Service 30b792
            return Promise.reject(gpgme_error('PARAM_WRONG'));
Packit Service 30b792
        }
Packit Service 30b792
        if (arguments.length && typeof arguments[0] !== 'object') {
Packit Service 30b792
            return Promise.reject(gpgme_error('PARAM_WRONG'));
Packit Service 30b792
        }
Packit Service 30b792
        return new Promise(function (resolve, reject) {
Packit Service 30b792
            let msg = createMessage('keylist');
Packit Service 30b792
            if (pattern) {
Packit Service 30b792
                msg.setParameter('keys', pattern);
Packit Service 30b792
            }
Packit Service 30b792
            msg.setParameter('sigs', true);
Packit Service 30b792
            if (search === true){
Packit Service 30b792
                msg.setParameter('locate', true);
Packit Service 30b792
            }
Packit Service 30b792
            msg.post().then(function (result){
Packit Service 30b792
                let resultset = [];
Packit Service 30b792
                if (result.keys.length === 0){
Packit Service 30b792
                    resolve([]);
Packit Service 30b792
                } else {
Packit Service 30b792
                    let secondrequest;
Packit Service 30b792
                    if (prepare_sync === true) {
Packit Service 30b792
                        secondrequest = function () {
Packit Service 30b792
                            let msg2 = createMessage('keylist');
Packit Service 30b792
                            if (pattern){
Packit Service 30b792
                                msg2.setParameter('keys', pattern);
Packit Service 30b792
                            }
Packit Service 30b792
                            msg2.setParameter('secret', true);
Packit Service 30b792
                            return msg2.post();
Packit Service 30b792
                        };
Packit Service 30b792
                    } else {
Packit Service 30b792
                        secondrequest = function () {
Packit Service 30b792
                            return Promise.resolve(true);
Packit Service 30b792
                        };
Packit Service 30b792
                    }
Packit Service 30b792
                    secondrequest().then(function (answer) {
Packit Service 30b792
                        for (let i=0; i < result.keys.length; i++){
Packit Service 30b792
                            if (prepare_sync === true){
Packit Service 30b792
                                if (answer && answer.keys) {
Packit Service 30b792
                                    for (let j=0;
Packit Service 30b792
                                        j < answer.keys.length; j++ ){
Packit Service 30b792
                                        const a = answer.keys[j];
Packit Service 30b792
                                        const b = result.keys[i];
Packit Service 30b792
                                        if (
Packit Service 30b792
                                            a.fingerprint === b.fingerprint
Packit Service 30b792
                                        ) {
Packit Service 30b792
                                            if (a.secret === true){
Packit Service 30b792
                                                b.hasSecret = true;
Packit Service 30b792
                                            } else {
Packit Service 30b792
                                                b.hasSecret = false;
Packit Service 30b792
                                            }
Packit Service 30b792
                                            break;
Packit Service 30b792
                                        }
Packit Service 30b792
                                    }
Packit Service 30b792
                                }
Packit Service 30b792
                            }
Packit Service 30b792
                            let k = createKey(result.keys[i].fingerprint,
Packit Service 30b792
                                !prepare_sync, result.keys[i]);
Packit Service 30b792
                            resultset.push(k);
Packit Service 30b792
                        }
Packit Service 30b792
                        resolve(resultset);
Packit Service 30b792
                    }, function (error){
Packit Service 30b792
                        reject(error);
Packit Service 30b792
                    });
Packit Service 30b792
                }
Packit Service 30b792
            });
Packit Service 30b792
        });
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * @typedef {Object} exportResult The result of a getKeysArmored
Packit Service 30b792
     * operation.
Packit Service 30b792
     * @property {String} armored The public Key(s) as armored block. Note
Packit Service 30b792
     * that the result is one armored block, and not a block per key.
Packit Service 30b792
     * @property {Array<String>} secret_fprs (optional) list of
Packit Service 30b792
     * fingerprints for those Keys that also have a secret Key available in
Packit Service 30b792
     * gnupg. The secret key will not be exported, but the fingerprint can
Packit Service 30b792
     * be used in operations needing a secret key.
Packit Service 30b792
     */
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Fetches the armored public Key blocks for all Keys matching the
Packit Service 30b792
     * pattern (if no pattern is given, fetches all keys known to gnupg).
Packit Service 30b792
     * @param {Object} options (optional)
Packit Service 30b792
     * @param {String|Array<String>} options.pattern The Pattern to
Packit Service 30b792
     * search for
Packit Service 30b792
     * @param {Boolean} options.with_secret_fpr also return a list of
Packit Service 30b792
     * fingerprints for the keys that have a secret key available
Packit Service 30b792
     * @returns {Promise<exportResult>} Object containing the
Packit Service 30b792
     * armored Key(s) and additional information.
Packit Service 30b792
     * @static
Packit Service 30b792
     * @async
Packit Service 30b792
     */
Packit Service 30b792
    getKeysArmored ({ pattern, with_secret_fpr }) {
Packit Service 30b792
        return new Promise(function (resolve, reject) {
Packit Service 30b792
            let msg = createMessage('export');
Packit Service 30b792
            msg.setParameter('armor', true);
Packit Service 30b792
            if (with_secret_fpr === true) {
Packit Service 30b792
                msg.setParameter('with-sec-fprs', true);
Packit Service 30b792
            }
Packit Service 30b792
            if (pattern){
Packit Service 30b792
                msg.setParameter('keys', pattern);
Packit Service 30b792
            }
Packit Service 30b792
            msg.post().then(function (answer){
Packit Service 30b792
                const result = { armored: answer.data };
Packit Service 30b792
                if (with_secret_fpr === true){
Packit Service 30b792
                    if (answer.hasOwnProperty('sec-fprs')){
Packit Service 30b792
                        result.secret_fprs = answer['sec-fprs'];
Packit Service 30b792
                    } else {
Packit Service 30b792
                        result.secret_fprs = [];
Packit Service 30b792
                    }
Packit Service 30b792
                }
Packit Service 30b792
                resolve(result);
Packit Service 30b792
            }, function (error){
Packit Service 30b792
                reject(error);
Packit Service 30b792
            });
Packit Service 30b792
        });
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Returns the Key used by default in gnupg.
Packit Service 30b792
     * (a.k.a. 'primary Key or 'main key').
Packit Service 30b792
     * It looks up the gpg configuration if set, or the first key that
Packit Service 30b792
     * contains a secret key.
Packit Service 30b792
     *
Packit Service 30b792
     * @returns {Promise<GPGME_Key>}
Packit Service 30b792
     * @async
Packit Service 30b792
     * @static
Packit Service 30b792
     */
Packit Service 30b792
    getDefaultKey (prepare_sync = false) {
Packit Service 30b792
        let me = this;
Packit Service 30b792
        return new Promise(function (resolve, reject){
Packit Service 30b792
            let msg = createMessage('config_opt');
Packit Service 30b792
            msg.setParameter('component', 'gpg');
Packit Service 30b792
            msg.setParameter('option', 'default-key');
Packit Service 30b792
            msg.post().then(function (resp){
Packit Service 30b792
                if (resp.option !== undefined
Packit Service 30b792
                    && resp.option.hasOwnProperty('value')
Packit Service 30b792
                    && resp.option.value.length === 1
Packit Service 30b792
                    && resp.option.value[0].hasOwnProperty('string')
Packit Service 30b792
                    && typeof (resp.option.value[0].string) === 'string'){
Packit Service 30b792
                    me.getKeys({ pattern: resp.option.value[0].string,
Packit Service 30b792
                        prepare_sync: true }).then(
Packit Service 30b792
                        function (keys){
Packit Service 30b792
                            if (keys.length === 1){
Packit Service 30b792
                                resolve(keys[0]);
Packit Service 30b792
                            } else {
Packit Service 30b792
                                reject(gpgme_error('KEY_NO_DEFAULT'));
Packit Service 30b792
                            }
Packit Service 30b792
                        }, function (error){
Packit Service 30b792
                            reject(error);
Packit Service 30b792
                        });
Packit Service 30b792
                } else {
Packit Service 30b792
                    let msg = createMessage('keylist');
Packit Service 30b792
                    msg.setParameter('secret', true);
Packit Service 30b792
                    msg.post().then(function (result){
Packit Service 30b792
                        if (result.keys.length === 0){
Packit Service 30b792
                            reject(gpgme_error('KEY_NO_DEFAULT'));
Packit Service 30b792
                        } else {
Packit Service 30b792
                            for (let i=0; i< result.keys.length; i++ ) {
Packit Service 30b792
                                if (
Packit Service 30b792
                                    result.keys[i].invalid === false &&
Packit Service 30b792
                                    result.keys[i].expired === false &&
Packit Service 30b792
                                    result.keys[i].revoked === false &&
Packit Service 30b792
                                    result.keys[i].can_sign === true
Packit Service 30b792
                                ) {
Packit Service 30b792
                                    let k = createKey(
Packit Service 30b792
                                        result.keys[i].fingerprint,
Packit Service 30b792
                                        !prepare_sync,
Packit Service 30b792
                                        result.keys[i]);
Packit Service 30b792
                                    resolve(k);
Packit Service 30b792
                                    break;
Packit Service 30b792
                                } else if (i === result.keys.length - 1){
Packit Service 30b792
                                    reject(gpgme_error('KEY_NO_DEFAULT'));
Packit Service 30b792
                                }
Packit Service 30b792
                            }
Packit Service 30b792
                        }
Packit Service 30b792
                    }, function (error){
Packit Service 30b792
                        reject(error);
Packit Service 30b792
                    });
Packit Service 30b792
                }
Packit Service 30b792
            }, function (error){
Packit Service 30b792
                reject(error);
Packit Service 30b792
            });
Packit Service 30b792
        });
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * @typedef {Object} importResult The result of a Key update
Packit Service 30b792
     * @property {Object} summary Numerical summary of the result. See the
Packit Service 30b792
     * feedbackValues variable for available Keys values and the gnupg
Packit Service 30b792
     * documentation.
Packit Service 30b792
     * https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html
Packit Service 30b792
     * for details on their meaning.
Packit Service 30b792
     * @property {Array<importedKeyResult>} Keys Array of Object containing
Packit Service 30b792
     * GPGME_Keys with additional import information
Packit Service 30b792
     *
Packit Service 30b792
     */
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * @typedef {Object} importedKeyResult
Packit Service 30b792
     * @property {GPGME_Key} key The resulting key
Packit Service 30b792
     * @property {String} status:
Packit Service 30b792
     *  'nochange' if the Key was not changed,
Packit Service 30b792
     *  'newkey' if the Key was imported in gpg, and did not exist
Packit Service 30b792
     *    previously,
Packit Service 30b792
     *  'change' if the key existed, but details were updated. For details,
Packit Service 30b792
     *    Key.changes is available.
Packit Service 30b792
     * @property {Boolean} changes.userId Changes in userIds
Packit Service 30b792
     * @property {Boolean} changes.signature Changes in signatures
Packit Service 30b792
     * @property {Boolean} changes.subkey Changes in subkeys
Packit Service 30b792
     */
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Import an armored Key block into gnupg. Note that this currently
Packit Service 30b792
     * will not succeed on private Key blocks.
Packit Service 30b792
     * @param {String} armored Armored Key block of the Key(s) to be
Packit Service 30b792
     * imported into gnupg
Packit Service 30b792
     * @param {Boolean} prepare_sync prepare the keys for synched use
Packit Service 30b792
     * (see {@link getKeys}).
Packit Service 30b792
     * @returns {Promise<importResult>} A summary and Keys considered.
Packit Service 30b792
     * @async
Packit Service 30b792
     * @static
Packit Service 30b792
     */
Packit Service 30b792
    importKey (armored, prepare_sync) {
Packit Service 30b792
        let feedbackValues = ['considered', 'no_user_id', 'imported',
Packit Service 30b792
            'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
Packit Service 30b792
            'new_signatures', 'new_revocations', 'secret_read',
Packit Service 30b792
            'secret_imported', 'secret_unchanged', 'skipped_new_keys',
Packit Service 30b792
            'not_imported', 'skipped_v3_keys'];
Packit Service 30b792
        if (!armored || typeof (armored) !== 'string'){
Packit Service 30b792
            return Promise.reject(gpgme_error('PARAM_WRONG'));
Packit Service 30b792
        }
Packit Service 30b792
        let me = this;
Packit Service 30b792
        return new Promise(function (resolve, reject){
Packit Service 30b792
            let msg = createMessage('import');
Packit Service 30b792
            msg.setParameter('data', armored);
Packit Service 30b792
            msg.post().then(function (response){
Packit Service 30b792
                let infos = {};
Packit Service 30b792
                let fprs = [];
Packit Service 30b792
                let summary = {};
Packit Service 30b792
                for (let i=0; i < feedbackValues.length; i++ ){
Packit Service 30b792
                    summary[feedbackValues[i]] =
Packit Service 30b792
                        response.result[feedbackValues[i]];
Packit Service 30b792
                }
Packit Service 30b792
                if (!response.result.hasOwnProperty('imports') ||
Packit Service 30b792
                    response.result.imports.length === 0
Packit Service 30b792
                ){
Packit Service 30b792
                    resolve({ Keys:[],summary: summary });
Packit Service 30b792
                    return;
Packit Service 30b792
                }
Packit Service 30b792
                for (let res=0; res
Packit Service 30b792
                    let result = response.result.imports[res];
Packit Service 30b792
                    let status = '';
Packit Service 30b792
                    if (result.status === 0){
Packit Service 30b792
                        status = 'nochange';
Packit Service 30b792
                    } else if ((result.status & 1) === 1){
Packit Service 30b792
                        status = 'newkey';
Packit Service 30b792
                    } else {
Packit Service 30b792
                        status = 'change';
Packit Service 30b792
                    }
Packit Service 30b792
                    let changes = {};
Packit Service 30b792
                    changes.userId = (result.status & 2) === 2;
Packit Service 30b792
                    changes.signature = (result.status & 4) === 4;
Packit Service 30b792
                    changes.subkey = (result.status & 8) === 8;
Packit Service 30b792
                    // 16 new secret key: not implemented
Packit Service 30b792
                    fprs.push(result.fingerprint);
Packit Service 30b792
                    infos[result.fingerprint] = {
Packit Service 30b792
                        changes: changes,
Packit Service 30b792
                        status: status
Packit Service 30b792
                    };
Packit Service 30b792
                }
Packit Service 30b792
                let resultset = [];
Packit Service 30b792
                if (prepare_sync === true){
Packit Service 30b792
                    me.getKeys({ pattern: fprs, prepare_sync: true })
Packit Service 30b792
                        .then(function (result){
Packit Service 30b792
                            for (let i=0; i < result.length; i++) {
Packit Service 30b792
                                resultset.push({
Packit Service 30b792
                                    key: result[i],
Packit Service 30b792
                                    changes:
Packit Service 30b792
                                        infos[result[i].fingerprint].changes,
Packit Service 30b792
                                    status: infos[result[i].fingerprint].status
Packit Service 30b792
                                });
Packit Service 30b792
                            }
Packit Service 30b792
                            resolve({ Keys:resultset,summary: summary });
Packit Service 30b792
                        }, function (error){
Packit Service 30b792
                            reject(error);
Packit Service 30b792
                        });
Packit Service 30b792
                } else {
Packit Service 30b792
                    for (let i=0; i < fprs.length; i++) {
Packit Service 30b792
                        resultset.push({
Packit Service 30b792
                            key: createKey(fprs[i]),
Packit Service 30b792
                            changes: infos[fprs[i]].changes,
Packit Service 30b792
                            status: infos[fprs[i]].status
Packit Service 30b792
                        });
Packit Service 30b792
                    }
Packit Service 30b792
                    resolve({ Keys:resultset,summary:summary });
Packit Service 30b792
                }
Packit Service 30b792
Packit Service 30b792
            }, function (error){
Packit Service 30b792
                reject(error);
Packit Service 30b792
            });
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
        });
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Convenience function for deleting a Key. See {@link Key#delete} for
Packit Service 30b792
     * further information about the return values.
Packit Service 30b792
     * @param {String} fingerprint
Packit Service 30b792
     * @returns {Promise<Boolean>}
Packit Service 30b792
     * @async
Packit Service 30b792
     * @static
Packit Service 30b792
     */
Packit Service 30b792
    deleteKey (fingerprint){
Packit Service 30b792
        if (isFingerprint(fingerprint) === true) {
Packit Service 30b792
            let key = createKey(fingerprint);
Packit Service 30b792
            return key.delete();
Packit Service 30b792
        } else {
Packit Service 30b792
            return Promise.reject(gpgme_error('KEY_INVALID'));
Packit Service 30b792
        }
Packit Service 30b792
    }
Packit Service 30b792
Packit Service 30b792
    /**
Packit Service 30b792
     * Generates a new Key pair directly in gpg, and returns a GPGME_Key
Packit Service 30b792
     * representing that Key. Please note that due to security concerns,
Packit Service 30b792
     * secret Keys can not be deleted or exported from inside gpgme.js.
Packit Service 30b792
     * @param {Object} options
Packit Service 30b792
     * @param {String} option.userId The user Id, e.g. 'Foo Bar <foo@bar.baz>'
Packit Service 30b792
     * @param {String} option.algo (optional) algorithm (and optionally key
Packit Service 30b792
     * size) to be used. See {@link supportedKeyAlgos} below for supported
Packit Service 30b792
     * values. If omitted, 'default' is used.
Packit Service 30b792
     * @param {Number} option.expires (optional) Expiration time in seconds
Packit Service 30b792
     * from now. If not set or set to 0, expiration will be 'never'
Packit Service 30b792
     *
Packit Service 30b792
     * @return {Promise<Key|GPGME_Error>}
Packit Service 30b792
     * @async
Packit Service 30b792
     */
Packit Service 30b792
    generateKey ({ userId, algo = 'default', expires= 0 } = {}){
Packit Service 30b792
        if (typeof userId !== 'string'
Packit Service 30b792
            // eslint-disable-next-line no-use-before-define
Packit Service 30b792
            || (algo && supportedKeyAlgos.indexOf(algo) < 0 )
Packit Service 30b792
            || (!Number.isInteger(expires) || expires < 0 )
Packit Service 30b792
        ){
Packit Service 30b792
            return Promise.reject(gpgme_error('PARAM_WRONG'));
Packit Service 30b792
        }
Packit Service 30b792
        // eslint-disable-next-line no-use-before-define
Packit Service 30b792
        let me = this;
Packit Service 30b792
        return new Promise(function (resolve, reject){
Packit Service 30b792
            let msg = createMessage('createkey');
Packit Service 30b792
            msg.setParameter('userid', userId);
Packit Service 30b792
            msg.setParameter('algo', algo);
Packit Service 30b792
            msg.setParameter('expires', expires);
Packit Service 30b792
            msg.post().then(function (response){
Packit Service 30b792
                me.getKeys({
Packit Service 30b792
                    pattern: response.fingerprint,
Packit Service 30b792
                    prepare_sync: true
Packit Service 30b792
                }).then(function (result){
Packit Service 30b792
                    resolve(result);
Packit Service 30b792
                }, function (error){
Packit Service 30b792
                    reject(error);
Packit Service 30b792
                });
Packit Service 30b792
            }, function (error) {
Packit Service 30b792
                reject(error);
Packit Service 30b792
            });
Packit Service 30b792
        });
Packit Service 30b792
    }
Packit Service 30b792
}
Packit Service 30b792
Packit Service 30b792
Packit Service 30b792
/**
Packit Service 30b792
 * List of algorithms supported for key generation. Please refer to the gnupg
Packit Service 30b792
 * documentation for details
Packit Service 30b792
 */
Packit Service 30b792
const supportedKeyAlgos = [
Packit Service 30b792
    'default', 'future-default',
Packit Service 30b792
    'rsa', 'rsa2048', 'rsa3072', 'rsa4096',
Packit Service 30b792
    'dsa', 'dsa2048', 'dsa3072', 'dsa4096',
Packit Service 30b792
    'elg', 'elg2048', 'elg3072', 'elg4096',
Packit Service 30b792
    'ed25519',
Packit Service 30b792
    'cv25519',
Packit Service 30b792
    'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1',
Packit Service 30b792
    'NIST P-256', 'NIST P-384', 'NIST P-521'
Packit Service 30b792
];