diff options
Diffstat (limited to 'lang/js/src')
-rw-r--r-- | lang/js/src/Connection.js | 208 | ||||
-rw-r--r-- | lang/js/src/Helpers.js | 84 | ||||
-rw-r--r-- | lang/js/src/Message.js | 109 | ||||
-rw-r--r-- | lang/js/src/gpgmejs.js | 282 | ||||
-rw-r--r-- | lang/js/src/gpgmejs_openpgpjs.js | 156 | ||||
-rw-r--r-- | lang/js/src/index.js | 33 | ||||
-rw-r--r-- | lang/js/src/permittedOperations.js | 75 |
7 files changed, 714 insertions, 233 deletions
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js index e8fea542..784929e9 100644 --- a/lang/js/src/Connection.js +++ b/lang/js/src/Connection.js @@ -1,76 +1,180 @@ +import { GPGME_Message } from "./Message"; + +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + /** * A connection port will be opened for each communication between gpgmejs and * gnupg. It should be alive as long as there are additional messages to be * expected. */ +import { permittedOperations } from './permittedOperations' -export function Connection(){ - if (!this.connection){ - this.connection = connect(); - this._msg = { - 'always-trust': true, - // 'no-encrypt-to': false, - // 'no-compress': true, - // 'throw-keyids': false, - // 'wrap': false, - 'armor': true, - 'base64': false - }; - }; +export class Connection{ - this.disconnect = function () { - if (this.connection){ - this.connection.disconnect(); + /** + * Opens and closes a port. Thus, it is made sure that the connection can + * be used. + * THIS BEHAVIOUR MAY CHANGE! + * discussion is to keep a port alive as long as the context stays the same + * + * TODO returns nothing, but triggers exceptions if not successfull + */ + constructor(){ + this._connection = chrome.runtime.connectNative('gpgmejson'); + if (!this._connection){ + if (chrome.runtime.lastError){ + throw('NO_CONNECT_RLE'); + } else { + throw('NO_CONNECT'); + } } - }; + this._flags = {}; // TODO general config + } /** - * Sends a message and resolves with the answer. - * @param {*} operation The interaction requested from gpgme - * @param {*} message A json-capable object to pass the operation details. - * TODO: _msg should contain configurable parameters + * Immediately closes the open port */ - this.post = function(operation, message){ - let timeout = 5000; - let me = this; - if (!message || !operation){ - return Promise.reject('no message'); // TBD + disconnect() { + if (this._connection){ + this._connection.disconnect(); } + } - let keys = Object.keys(message); - for (let i=0; i < keys.length; i++){ - let property = keys[i]; - me._msg[property] = message[property]; + /** + * Sends a message and resolves with the answer. + * @param {GPGME_Message} message + * @returns {Promise<Object>} the gnupg answer, or rejection with error + * information + * TODO: better/more consistent error information + */ + post(message){ + if (!message || !message instanceof GPGME_Message){ + return Promise.reject('ERR_NO_MSG'); } - me._msg['op'] = operation; - // TODO fancier checks if what we want is consistent with submitted content + // let timeout = 5000; //TODO config + let me = this; return new Promise(function(resolve, reject){ - me.connection.onMessage.addListener(function(msg) { + let answer = new Answer(message.op); + let listener = function(msg) { if (!msg){ - reject('empty answer.'); - } - if (msg.type === "error"){ + me._connection.onMessage.removeListener(listener) + reject('EMPTY_ANSWER'); + } else if (msg.type === "error"){ + me._connection.onMessage.removeListener(listener) reject(msg.msg); + } else { + answer.add(msg); + if (msg.more === true){ + me._connection.postMessage({'op': 'getmore'}); + } else { + me._connection.onMessage.removeListener(listener) + resolve(answer.message); + } } - resolve(msg); - }); + }; - me.connection.postMessage(me._msg); - setTimeout( - function(){ - me.disconnect(); - reject('Timeout'); - }, timeout); + me._connection.onMessage.addListener(listener); + me._connection.postMessage(message); + //TBD: needs to be aware if there is a pinentry pending + // setTimeout( + // function(){ + // me.disconnect(); + // reject('TIMEOUT'); + // }, timeout); }); - }; + } }; +/** + * A class for answer objects, checking and processing the return messages of + * the nativeMessaging communication + * @param {String} operation The operation, to look up validity of return keys + */ +class Answer{ -function connect(){ - let connection = chrome.runtime.connectNative('gpgmejson'); - if (!connection){ - let msg = chrome.runtime.lastError || 'no message'; //TBD - throw(msg); + constructor(operation){ + this.operation = operation; } - return connection; -}; + + /** + * + * @param {Object} msg The message as received with nativeMessaging + * TODO: "error" and "more" handling are not in here, but in post() + */ + add(msg){ + if (this._response === undefined){ + this._response = {}; + } + let messageKeys = Object.keys(msg); + let poa = permittedOperations[this.operation].answer; + for (let i= 0; i < messageKeys.length; i++){ + let key = messageKeys[i]; + switch (key) { + case 'type': + if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){ + console.log( 'unexpected answer type: ' + msg.type); + throw('UNEXPECTED_TYPE'); + + } + break; + case 'more': + break; + default: + //data should be concatenated + if (poa.data.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = ''; + } + this._response[key] = this._response[key].concat(msg[key]); + } + //params should not change through the message + else if (poa.params.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = msg[key]; + } + else if (this._response[key] !== msg[key]){ + throw('UNEXPECTED_TYPE'); + } + } + //infos may be json objects etc. Not yet defined. + // Pushing them into arrays for now + else if (poa.infos.indexOf(key) >= 0){ + if (!this._response.hasOwnProperty(key)){ + this._response[key] = []; + } + this._response.push(msg[key]); + } + else { + console.log('unexpected answer parameter: ' + key); + throw('UNEXPECTED_PARAM'); + } + break; + } + } + } + + /** + * Returns the assembled message. TODO: does not care yet if completed. + */ + get message(){ + return this._response; + } +} diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js new file mode 100644 index 00000000..eeb7a3c4 --- /dev/null +++ b/lang/js/src/Helpers.js @@ -0,0 +1,84 @@ +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * Tries to return an array of fingerprints, either from input fingerprints or + * from Key objects + * @param {String|Array<String>} input Input value. + * @returns {Array<String>} Array of fingerprints. + */ +export function toKeyIdArray(input){ + if (!input){ + return []; + // TODO: Warning or error here? Did we expect something or is "nothing" okay? + } + if (input instanceof Array){ + let result = []; + for (let i=0; i < input.length; i++){ + if (isFingerprint(input[i]) === true){ + result.push(input[i]); + } else { + //TODO error? + console.log('gpgmejs/Helpers.js Warning: '+ + input[i] + + ' is not a valid key fingerprint and will not be used'); + } + } + return result; + } else if (isFingerprint(input) === true) { + return [input]; + } + console.log('gpgmejs/Helpers.js Warning: ' + input + + ' is not a valid key fingerprint and will not be used'); + return []; +}; + +/** + * check if values are valid hexadecimal values of a specified length + * @param {*} key input value. + * @param {int} len the expected length of the value + */ +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 Hex string with a length of 40 + */ +export function isFingerprint(string){ + return hextest(string, 40); +}; + +//TODO needed anywhere? +function isLongId(string){ + return hextest(string, 16); +}; + +//TODO needed anywhere? +function isShortId(string){ + return hextest(string, 8); +}; diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js new file mode 100644 index 00000000..90b554a1 --- /dev/null +++ b/lang/js/src/Message.js @@ -0,0 +1,109 @@ +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ +import { permittedOperations } from './permittedOperations' + +export class GPGME_Message { + //TODO getter + + constructor(){ + } + + /** + * Defines the operation this message will have + * @param {String} operation Mus be defined in permittedOperations + * TODO: move to constructor? + */ + set operation (operation){ + if (!operation || typeof(operation) !== 'string'){ + throw('ERR_WRONG_PARAM'); + } + if (operation in permittedOperations){ + if (!this._msg){ + this._msg = {}; + } + this._msg.op = operation; + } else { + throw('ERR_NOT_IMPLEMENTED'); + } + } + + /** + * Sets a parameter for the message. Note that the operation has to be set + * first, to be able to check if the parameter is permittted + * @param {String} param Parameter to set + * @param {any} value Value to set //TODO: Some type checking + * @returns {Boolean} If the parameter was set successfully + */ + setParameter(param,value){ + if (!param || typeof(param) !== 'string'){ + throw('ERR_WRONG_PARAM'); + } + if (!this._msg || !this._msg.op){ + console.log('There is no operation specified yet. '+ + 'The parameter cannot be set'); + return false; + } + let po = permittedOperations[this._msg.op]; + if (!po){ + throw('LAZY_PROGRAMMER'); + //TODO + return false; + } + if (po.required.indexOf(param) >= 0 || po.optional.indexOf(param) >= 0){ + this._msg[param] = value; + return true; + } + console.log('' + param + ' is invalid and could not be set'); + return false; + } + + /** + * Check if the message has the minimum requirements to be sent, according + * to the definitions in permittedOperations + * @returns {Boolean} + */ + get isComplete(){ + if (!this._msg.op){ + return false; + } + let reqParams = permittedOperations[this._msg.op].required; + for (let i=0; i < reqParams.length; i++){ + if (!reqParams[i] in this._msg){ + return false; + } + } + return true; + } + + /** + * 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; + } + + } +}
\ No newline at end of file diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js index dedbf809..8323ac3b 100644 --- a/lang/js/src/gpgmejs.js +++ b/lang/js/src/gpgmejs.js @@ -1,187 +1,131 @@ -import {Connection} from "./Connection" - -export function encrypt(data, publicKeys, privateKeys, passwords=null, - sessionKey, filename, compression, armor=true, detached=false, - signature=null, returnSessionKey=false, wildcard=false, date=new Date()){ - // gpgme_op_encrypt ( <-gpgme doc on this operation - // gpgme_ctx_t ctx, - // gpgme_key_t recp[], - // gpgme_encrypt_flags_t flags, - // gpgme_data_t plain, - // gpgme_data_t cipher) - // flags: - // GPGME_ENCRYPT_ALWAYS_TRUST - // GPGME_ENCRYPT_NO_ENCRYPT_TO - // GPGME_ENCRYPT_NO_COMPRESS - // GPGME_ENCRYPT_PREPARE - // GPGME_ENCRYPT_EXPECT_SIGN - // GPGME_ENCRYPT_SYMMETRIC - // GPGME_ENCRYPT_THROW_KEYIDS - // GPGME_ENCRYPT_WRAP - if (passwords !== null){ - throw('Password!'); // TBD - } - - let pubkeys = toKeyIdArray(publicKeys); - let privkeys = toKeyIdArray(privateKeys); - - // TODO filename: data is supposed to be empty, file is provided - // TODO config compression detached signature - // TODO signature to add to the encrypted message (?) || privateKeys: signature is desired - // gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) - - // TODO sign date overwriting implemented in gnupg? - - let conn = new Connection(); - if (wildcard){ - // Connection.set('throw-keyids', true); TODO Connection.set not yet existant - } - return conn.post('encrypt', { - 'data': data, - 'keys': publicKeys, - 'armor': armor}); -}; +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ -export function decrypt(message, privateKeys, passwords, sessionKeys, publicKeys, - format='utf8', signature=null, date=new Date()) { - if (passwords !== null){ - throw('Password!'); // TBD - } - if (format === 'binary'){ - // Connection.set('base64', true); - } - if (publicKeys || signature){ - // Connection.set('signature', signature); - // request verification, too +import {Connection} from "./Connection" +import {GPGME_Message} from './Message' +import {toKeyIdArray} from "./Helpers" + +export class GpgME { + /** + * initial check if connection si successfull. Will throw ERR_NO_CONNECT or + * ERR_NO_CONNECT_RLE (if chrome.runtime.lastError is available) if the + * connection fails. + * TODO The connection to the nativeMessaging host will, for now, be closed + * after each interaction. Session management with gpg_agent is TBD. + * TODO: add configuration + */ + constructor(){ + let conn = new Connection(); + // this.keyring = new Keyring(); TBD + // TODO config, e.g. + this.configuration = { + null_expire_is_never: true + }; + conn.disconnect(); } - //privateKeys optionally if keyId was thrown? - // gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) - // response is gpgme_op_decrypt_result (gpgme_ctx_t ctx) (next available?) - return conn.post('decrypt', { - 'data': message - }); -} -// BIG TODO. -export function generateKey({userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve="", date=new Date()}){ - throw('not implemented here'); - // gpgme_op_createkey (gpgme_ctx_t ctx, const char *userid, const char *algo, unsigned long reserved, unsigned long expires, gpgme_key_t extrakey, unsigned int flags); - return false; -} + /** + * @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array + * @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message + * @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message + */ + encrypt (data, publicKeys, wildcard=false){ -export function sign({ data, privateKeys, armor=true, detached=false, date=new Date() }) { - //TODO detached GPGME_SIG_MODE_DETACH | GPGME_SIG_MODE_NORMAL - // gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode) - // TODO date not supported + let msg = new GPGME_Message; + msg.operation = 'encrypt'; - let conn = new Connection(); - let privkeys = toKeyIdArray(privateKeys); - return conn.post('sign', { - 'data': data, - 'keys': privkeys, - 'armor': armor}); -}; + // TODO temporary + msg.setParameter('armor', true); + msg.setParameter('always-trust', true); -export function verify({ message, publicKeys, signature=null, date=new Date() }) { - //TODO extra signature: sig, signed_text, plain: null - // inline sig: signed_text:null, plain as writable (?) - // date not supported - //gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plain) - let conn = new Connection(); - let privkeys = toKeyIdArray(privateKeys); - return conn.post('sign', { - 'data': data, - 'keys': privkeys, - 'armor': armor}); -} + let pubkeys = toKeyIdArray(publicKeys); + msg.setParameter('keys', pubkeys); + putData(msg, data); + if (wildcard === true){msg.setParameter('throw-keyids', true); + }; -export function reformatKey(privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0){ - let privKey = toKeyIdArray(privateKey); - if (privKey.length !== 1){ - return false; //TODO some error handling. There is not exactly ONE key we are editing + if (msg.isComplete === true) { + let conn = new Connection(); + return (conn.post(msg.message)); + } + else { + return Promise.reject('NO_CONNECT'); + //TODO + } } - let conn = new Connection(); - // TODO key management needs to be changed somewhat - return conn.post('TODO', { - 'key': privKey[0], - 'keyExpirationTime': keyExpirationTime, //TODO check if this is 0 or a positive and plausible number - 'userIds': userIds //TODO check if empty or plausible strings - }); - // unlocked will be ignored -} -export function decryptKey({ privateKey, passphrase }) { - throw('not implemented here'); - return false; -}; + /** + * @param {String} data TODO Format: base64? String? Message with the encrypted data + * @returns {Promise<Object>} decrypted message: + data: The decrypted data. This may be base64 encoded. + base64: Boolean indicating whether data is base64 encoded. + mime: A Boolean indicating whether the data is a MIME object. + info: An optional object with extra information. + * @async + */ -export function encryptKey({ privateKey, passphrase }) { - throw('not implemented here'); - return false; -}; + decrypt(data){ -export function encryptSessionKey({data, algorithm, publicKeys, passwords, wildcard=false }) { - //openpgpjs: - // Encrypt a symmetric session key with public keys, passwords, or both at - // once. At least either public keys or passwords must be specified. - throw('not implemented here'); - return false; -}; - -export function decryptSessionKeys({ message, privateKeys, passwords }) { - throw('not implemented here'); - return false; -}; - -// //TODO worker handling - -// //TODO key representation -// //TODO: keyring handling - - -/** - * Helper functions and checks - */ - -/** - * Checks if the submitted value is a keyID. - * TODO: should accept all strings that are accepted as keyID by gnupg - * TODO: See if Key becomes an object later on - * @param {*} key input value. Is expected to be a string of 8,16 or 40 chars - * representing hex values. Will return false if that expectation is not met - */ -function isKeyId(key){ - if (!key || typeof(key) !== "string"){ - return false; - } - if ([8,16,40].indexOf(key.length) < 0){ - return false; + if (data === undefined){ + throw('ERR_EMPTY_MSG'); + } + let msg = new GPGME_Message; + msg.operation = 'decrypt'; + putData(msg, data); + // TODO: needs proper EOL to be decrypted. + + if (msg.isComplete === true){ + let conn = new Connection(); + return conn.post(msg.message); + } + else { + return Promise.reject('NO_CONNECT'); + //TODO + } } - let regexp= /^[0-9a-fA-F]*$/i; - return regexp.test(key); -}; +} /** - * Tries to return an array of keyID values, either from a string or an array. - * Filters out those that do not meet the criteria. (TODO: silently for now) - * @param {*} array Input value. + * Sets the data of the message, converting Uint8Array to base64 and setting + * the base64 flag + * @param {GPGME_Message} message The message where this data will be set + * @param {*} data The data to enter + * @param {String} propertyname // TODO unchecked still */ -function toKeyIdArray(array){ - let result = []; - if (!array){ - return result; - } - if (!Array.isArray(array)){ - if (isKeyId(array) === true){ - return [keyId]; - } - return result; +function putData(message, data){ + if (!message || !message instanceof GPGME_Message ) { + throw('NO_MESSAGE_OBJECT'); } - for (let i=0; i < array.length; i++){ - if (isKeyId(array[i]) === true){ - result.push(array[i]); - } + if (!data){ + //TODO Debug only! No data is legitimate + console.log('Warning. no data in message'); + message.setParameter('data', ''); + } else if (data instanceof Uint8Array){ + let decoder = new TextDecoder('utf8'); + message.setParameter('base64', true); + message.setParameter ('data', decoder.decode(data)); + } else if (typeof(data) === 'string') { + message.setParameter('base64', false); + message.setParameter('data', data); + } else { + throw('ERR_WRONG_TYPE'); } - return result; -}; +}
\ No newline at end of file diff --git a/lang/js/src/gpgmejs_openpgpjs.js b/lang/js/src/gpgmejs_openpgpjs.js new file mode 100644 index 00000000..1eec4da4 --- /dev/null +++ b/lang/js/src/gpgmejs_openpgpjs.js @@ -0,0 +1,156 @@ +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/** + * This is a compatibility API to be used as openpgpjs syntax. + * Non-implemented options will throw an error if set (not null or undefined) + * TODO Some info about differences + */ + + import { GpgME } from "./gpgmejs"; +// import {Keyring} from "./Keyring" TODO + + +export class GpgME_openPGPCompatibility { + + constructor(){ + this._gpgme = new GpgME; + } + + /** + * Encrypt Message + * Supported: + * @param {String|Uint8Array} data + * @param {Key|Array<Key>} publicKeys + * @param {Boolean} wildcard + * TODO: + * @param {Key|Array<Key>} privateKeys + * @param {String} filename + * @param {module:enums.compression} compression + * @param {Boolean} armor + * @param {Boolean} detached + * unsupported: + * @param {String|Array<String>} passwords + * @param {Object} sessionKey + * @param {Signature} signature + * @param {Boolean} returnSessionKey + * + * @returns {Promise<Object>} + * {data: ASCII armored message, + * signature: detached signature if 'detached' is true + * } + * @async + * @static + */ + encrypt({data = '', publicKeys = '', privateKeys, passwords, sessionKey, + filename, compression, armor=true, detached=false, signature=null, + returnSessionKey=null, wildcard=false, date=null}) { + if (passwords !== undefined + || sessionKey !== undefined + || signature !== null + || returnSessionKey !== null + || date !== null){ + throw('NOT_IMPLEMENTED'); + } + if ( privateKeys + || filename + || compression + || armor === false + || detached == true){ + console.log('may be implemented later'); + throw('NOT_YET_IMPLEMENTED'); + } + return this.GpgME.encrypt(data, translateKeyInput(publicKeys), wildcard); + } + + /** Decrypt Message + * supported + * TODO: @param {Message} message TODO: for now it accepts an armored string only + * Unsupported: + * @param {String|Array<String>} passwords + * @param {Object|Array<Object>} sessionKeys + * @param {Date} date + + * TODO + * @param {Key|Array<Key>} privateKey + * @param {Key|Array<Key>} publicKeys + * @param {String} format (optional) return data format either as 'utf8' or 'binary' + * @param {Signature} signature (optional) detached signature for verification + + * @returns {Promise<Object>} decrypted and verified message in the form: + * { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] } + * @async + * @static + */ + decrypt({ message, privateKeys, passwords, sessionKeys, publicKeys, format='utf8', signature=null, date}) { + if (passwords !== undefined + || sessionKeys + || date){ + + throw('NOT_IMPLEMENTED'); + } + if ( privateKeys + || publicKeys + || format !== 'utf8' + || signature + ){ + console.log('may be implemented later'); + throw('NOT_YET_IMPLEMENTED'); + } + return this.GpgME.decrypt(message); + // TODO: translate between: + // openpgp: + // { data:Uint8Array|String, + // filename:String, + // signatures:[{ keyid:String, valid:Boolean }] } + // and gnupg: + // data: The decrypted data. This may be base64 encoded. + // base64: Boolean indicating whether data is base64 encoded. + // mime: A Boolean indicating whether the data is a MIME object. + // info: An optional object with extra information. + } +} + +/** + * + * @param {Object | String} Key Either a (presumably openpgp Key) Object with a + * primaryKeyproperty and a method getFingerprint, or a string. + * @returns {String} Unchecked string value claiming to be a fingerprint + * TODO: gpgmejs checks again, so it's okay here. + */ +function translateKeyInput(Key){ + if (!Key){ + return []; + } + if (!Array.isArray(Key)){ + Key = [Key]; + } + let resultslist = []; + for (let i=0; i < Key.length; i++){ + if (typeof(Key[i]) === 'string'){ + resultslist.push(Key); + } else if ( + Key[i].hasOwnProperty(primaryKey) && + Key[i].primaryKey.hasOwnProperty(getFingerprint)){ + resultslist.push(Key[i].primaryKey.getFingerprint()); + } + } + return resultslist; +}
\ No newline at end of file diff --git a/lang/js/src/index.js b/lang/js/src/index.js index 02dc919d..f70bd2d8 100644 --- a/lang/js/src/index.js +++ b/lang/js/src/index.js @@ -1,14 +1,23 @@ -import * as gpgmejs from'./gpgmejs' -export default gpgmejs; - -/** - * Export each high level api function separately. - * Usage: +/* 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. * - * import { encryptMessage } from 'gpgme.js' - * encryptMessage(keys, text) + * 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ */ -export { - encrypt, decrypt, sign, verify, - generateKey, reformatKey - } from './gpgmejs'; + +import { GpgME as gpgmejs } from "./gpgmejs"; +// import { GpgME_openPGPCompatibility as gpgmejs } from "./gpgmejs_openpgpjs"; +export default gpgmejs; diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js new file mode 100644 index 00000000..3c11b8e0 --- /dev/null +++ b/lang/js/src/permittedOperations.js @@ -0,0 +1,75 @@ +/* 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + + /** + * Definition of the possible interactions with gpgme-json. + * operation: <Object> + required: Array<String> + optional: Array<String> + answer: <Object> + type: <String< The content type of answer expected + data: Array<String> The payload property of the answer. May be + partial and in need of concatenation + params: Array<String> Information that do not change throughout + the message + infos: Array<String> arbitrary information that may change + } + } + */ + +export const permittedOperations = { + encrypt: { + required: ['keys', 'data'], + optional: [ + 'protocol', + 'chunksize', + 'base64', + 'mime', + 'armor', + 'always-trust', + 'no-encrypt-to', + 'no-compress', + 'throw-keyids', + 'want-address', + 'wrap' + ], + answer: { + type: ['ciphertext'], + data: ['data'], + params: ['base64'], + infos: [] + } + }, + + decrypt: { + required: ['data'], + optional: [ + 'protocol', + 'chunksize', + 'base64' + ], + answer: { + type: ['plaintext'], + data: ['data'], + params: ['base64', 'mime'], + infos: ['info'] + } + } +} |