39f6a2f0e8a4692e3c94bc97eb7d565d9486c559
[gpgme.git] / lang / js / src / gpgmejs.js
1 /* gpgme.js - Javascript integration for gpgme
2  * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1+
19  */
20
21 import {GPGME_Message, createMessage} from './Message'
22 import {toKeyIdArray} from "./Helpers"
23 import { gpgme_error } from "./Errors"
24 import { GPGME_Keyring } from "./Keyring";
25
26 export class GpgME {
27     /**
28      * initializes GpgME by opening a nativeMessaging port
29      * TODO: add configuration
30      */
31     constructor(config){ //TODO config not parsed
32         this._config = config;
33     }
34
35    set Keyring(keyring){
36         if (keyring && keyring instanceof GPGME_Keyring){
37             this._Keyring = keyring;
38         }
39     }
40
41     get Keyring(){
42         if (!this._Keyring){
43             this._Keyring = new GPGME_Keyring;
44         }
45         return this._Keyring;
46     }
47
48     /**
49      * Encrypt (and optionally sign) a Message
50      * @param {String|Object} data text/data to be encrypted as String. Also accepts Objects with a getText method
51      * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
52      * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} secretKeys (optional) Keys used to sign the message
53      * @param {Boolean} base64 (optional) The data is already considered to be in base64 encoding
54      * @param {Boolean} armor (optional) Request the output as armored block
55      * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
56      * @param {Object} additional use additional gpg options (refer to src/permittedOperations)
57      * @returns {Promise<Object>} Encrypted message:
58      *   data: The encrypted message
59      *   base64: Boolean indicating whether data is base64 encoded.
60      * @async
61      */
62     encrypt(data, publicKeys, secretKeys, base64=false, armor=true,
63         wildcard=false, additional = {}
64     ){
65         let msg = createMessage('encrypt');
66         if (msg instanceof Error){
67             return Promise.reject(msg)
68         }
69         msg.setParameter('armor', armor);
70         msg.setParameter('always-trust', true);
71         if (base64 === true) {
72             msg.setParameter('base64', true);
73         }
74         let pubkeys = toKeyIdArray(publicKeys);
75         msg.setParameter('keys', pubkeys);
76         let sigkeys = toKeyIdArray(secretKeys);
77         if (sigkeys.length > 0) {
78             msg.setParameter('signing_keys', sigkeys);
79         }
80         putData(msg, data);
81         if (wildcard === true){
82             msg.setParameter('throw-keyids', true);
83         };
84         if (additional){
85             let additional_Keys = Object.keys(additional);
86             for (let k = 0; k < additional_Keys.length; k++) {
87                 msg.setParameter(additional_Keys[k],
88                     additional[additional_Keys[k]]);
89             }
90         }
91         if (msg.isComplete === true){
92             return msg.post();
93         } else {
94             return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
95         }
96     }
97
98     /**
99     * Decrypt a Message
100     * @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a getText method
101     * @param {Boolean} base64 (optional) Response is expected to be base64 encoded
102     * @returns {Promise<Object>} decrypted message:
103         data:   The decrypted data.  This may be base64 encoded.
104         base64: Boolean indicating whether data is base64 encoded.
105         mime:   A Boolean indicating whether the data is a MIME object.
106         signatures: Array of signature Objects TODO not yet implemented.
107             // should be an object that can tell if all signatures are valid etc.
108     * @async
109     */
110     decrypt(data, base64=false){
111         if (data === undefined){
112             return Promise.reject(gpgme_error('MSG_EMPTY'));
113         }
114         let msg = createMessage('decrypt');
115         if (base64 === true){
116             msg.expected = 'base64';
117         }
118         if (msg instanceof Error){
119             return Promise.reject(msg);
120         }
121         putData(msg, data);
122         return msg.post();
123     }
124
125     /**
126      * Sign a Message
127      * @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a gettext methos
128      * @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} keys The key/keys to use for signing
129      * @param {*} mode The signing mode. Currently supported:
130      *      'clearsign': (default) The Message is embedded into the signature
131      *      'detached': The signature is stored separately
132      * @param {*} base64 input is considered base64
133      * @returns {Promise<Object>}
134      *    data: The resulting data. In clearsign mode this includes the signature
135      *    signature: The detached signature (if in detached mode)
136      * @async
137      */
138     sign(data, keys, mode='clearsign', base64=false) {
139         if (data === undefined){
140             return Promise.reject(gpgme_error('MSG_EMPTY'));
141         }
142         let key_arr = toKeyIdArray(keys);
143         if (key_arr.length === 0){
144             return Promise.reject(gpgme_error('MSG_NO_KEYS'));
145         }
146         let msg = createMessage('sign');
147
148         msg.setParameter('keys', key_arr);
149         if (base64 === true){
150             msg.setParameter('base64', true);
151         }
152         msg.setParameter('mode', mode);
153         putData(msg, data);
154         if (mode === 'detached') {
155             msg.expected = 'base64';
156         }
157         let me = this;
158         return new Promise(function(resolve,reject) {
159             msg.post().then( function(message) {
160                 if (mode === 'clearsign'){
161                     resolve({
162                         data: message.data}
163                     );
164                 } else if (mode === 'detached') {
165                     resolve({
166                         data: data,
167                         signature: message.data
168                     });
169                 }
170             }, function(error){
171                 reject(error);
172             })
173         });
174     }
175 }
176
177 /**
178  * Sets the data of the message, setting flags according on the data type
179  * @param {GPGME_Message} message The message where this data will be set
180  * @param {*} data The data to enter
181  */
182 function putData(message, data){
183     if (!message || !message instanceof GPGME_Message ) {
184         return gpgme_error('PARAM_WRONG');
185     }
186     if (!data){
187         return gpgme_error('PARAM_WRONG');
188     } else if (typeof(data) === 'string') {
189         message.setParameter('data', data);
190     } else if (
191         typeof(data) === 'object' &&
192         typeof(data.getText) === 'function'
193     ){
194         let txt = data.getText();
195         if (typeof(txt) === 'string'){
196             message.setParameter('data', txt);
197         } else {
198             return gpgme_error('PARAM_WRONG');
199         }
200
201     } else {
202         return gpgme_error('PARAM_WRONG');
203     }
204 }