[git] GPGME - branch, javascript-binding, updated. gpgme-1.11.1-56-gc072675

[email protected] (by Maximilian Krambach)
Newsgroups gmane.comp.encryption.gpg.cvs
Message-ID <[email protected]>
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".

The branch, javascript-binding has been updated
       via  c072675f3f2d734297a348c6de810148fb1424a2 (commit)
      from  7a072270ac031152ee034df0f5b6ef5e8bf7d394 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c072675f3f2d734297a348c6de810148fb1424a2
Author: Maximilian Krambach <[email protected]>
Date:   Fri Jun 8 17:54:58 2018 +0200

    js: change chunksize handling and decoding
    
    --
    
    * the nativeApp now sends all data in one base64-encoded string, which
      needs reassembly, but in a much easier way now.
    
    * there are some new performance problems now, especially with
      decrypting data

diff --git a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
index a84be27..bd72c1d 100644
--- a/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
+++ b/lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
@@ -152,7 +152,7 @@ describe('Encryption and Decryption', function () {
         let prm = Gpgmejs.init();
         prm.then(function (context) {
             context.encrypt(b64data,
-                inputvalues.encrypt.good.fingerprint).then(
+                inputvalues.encrypt.good.fingerprint, true).then(
                 function (answer) {
                     expect(answer).to.not.be.empty;
                     expect(answer.data).to.be.a('string');
@@ -185,33 +185,7 @@ describe('Encryption and Decryption', function () {
                         'BEGIN PGP MESSAGE');
                     expect(answer.data).to.include(
                         'END PGP MESSAGE');
-                    context.decrypt(answer.data, true).then(
-                        function (result) {
-                            expect(result).to.not.be.empty;
-                            expect(result.data).to.be.a('string');
-                            expect(result.data).to.equal(b64data);
-                            done();
-                        });
-                });
-        });
-    }).timeout(3000);
-
-    it('Random data, input and output as base64', function (done) {
-        let data = bigBoringString(0.0001);
-        let b64data = btoa(data);
-        let prm = Gpgmejs.init();
-        prm.then(function (context) {
-            context.encrypt(b64data,
-                inputvalues.encrypt.good.fingerprint).then(
-                function (answer) {
-                    expect(answer).to.not.be.empty;
-                    expect(answer.data).to.be.a('string');
-
-                    expect(answer.data).to.include(
-                        'BEGIN PGP MESSAGE');
-                    expect(answer.data).to.include(
-                        'END PGP MESSAGE');
-                    context.decrypt(answer.data, true).then(
+                    context.decrypt(answer.data).then(
                         function (result) {
                             expect(result).to.not.be.empty;
                             expect(result.data).to.be.a('string');
@@ -222,5 +196,4 @@ describe('Encryption and Decryption', function () {
         });
     }).timeout(3000);
 
-
 });
diff --git a/lang/js/BrowserTestExtension/tests/longRunningTests.js b/lang/js/BrowserTestExtension/tests/longRunningTests.js
index eefe126..e148d1c 100644
--- a/lang/js/BrowserTestExtension/tests/longRunningTests.js
+++ b/lang/js/BrowserTestExtension/tests/longRunningTests.js
@@ -24,7 +24,7 @@
 /* global bigString, inputvalues */
 
 describe('Long running Encryption/Decryption', function () {
-    for (let i=0; i < 100; i++) {
+    for (let i=0; i < 101; i++) {
         it('Successful encrypt/decrypt completely random data ' +
             (i+1) + '/100', function (done) {
             let prm = Gpgmejs.init();
@@ -43,30 +43,32 @@ describe('Long running Encryption/Decryption', function () {
                             function(result){
                                 expect(result).to.not.be.empty;
                                 expect(result.data).to.be.a('string');
+                                /*
                                 if (result.data.length !== data.length) {
-                                    // console.log('diff: ' +
-                                    // (result.data.length - data.length));
+                                    console.log('diff: ' +
+                                    (result.data.length - data.length));
                                     for (let i=0; i < result.data.length; i++){
                                         if (result.data[i] !== data[i]){
-                                            // console.log('position: ' + i);
-                                            // console.log('result : ' +
-                                            // result.data.charCodeAt(i) +
-                                            // result.data[i-2] +
-                                            // result.data[i-1] +
-                                            // result.data[i] +
-                                            // result.data[i+1] +
-                                            // result.data[i+2]);
-                                            // console.log('original: ' +
-                                            // data.charCodeAt(i) +
-                                            // data[i-2] +
-                                            // data[i-1] +
-                                            // data[i] +
-                                            // data[i+1] +
-                                            // data[i+2]);
+                                            console.log('position: ' + i);
+                                            console.log('result : ' +
+                                            result.data.charCodeAt(i) +
+                                            result.data[i-2] +
+                                            result.data[i-1] +
+                                            result.data[i] +
+                                            result.data[i+1] +
+                                            result.data[i+2]);
+                                            console.log('original: ' +
+                                            data.charCodeAt(i) +
+                                            data[i-2] +
+                                            data[i-1] +
+                                            data[i] +
+                                            data[i+1] +
+                                            data[i+2]);
                                             break;
                                         }
                                     }
                                 }
+                                */
                                 expect(result.data).to.equal(data);
                                 done();
                             });
diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index e9c0b21..f399b22 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -108,6 +108,7 @@ export class Connection{
             return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
         }
         let me = this;
+        let chunksize = message.chunksize;
         return new Promise(function(resolve, reject){
             let answer = new Answer(message);
             let listener = function(msg) {
@@ -115,22 +116,27 @@ export class Connection{
                     me._connection.onMessage.removeListener(listener);
                     me._connection.disconnect();
                     reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
-                } else if (msg.type === 'error'){
-                    me._connection.onMessage.removeListener(listener);
-                    me._connection.disconnect();
-                    reject(gpgme_error('GNUPG_ERROR', msg.msg));
                 } else {
-                    let answer_result = answer.add(msg);
+                    let answer_result = answer.collect(msg);
                     if (answer_result !== true){
                         me._connection.onMessage.removeListener(listener);
                         me._connection.disconnect();
                         reject(answer_result);
-                    } else if (msg.more === true){
-                        me._connection.postMessage({'op': 'getmore'});
                     } else {
-                        me._connection.onMessage.removeListener(listener);
-                        me._connection.disconnect();
-                        resolve(answer.message);
+                        if (msg.more === true){
+                            me._connection.postMessage({
+                                'op': 'getmore',
+                                'chunksize': chunksize
+                            });
+                        } else {
+                            me._connection.onMessage.removeListener(listener);
+                            me._connection.disconnect();
+                            if (answer.message instanceof Error){
+                                reject(answer.message);
+                            } else {
+                                resolve(answer.message);
+                            }
+                        }
                     }
                 }
             };
@@ -170,19 +176,32 @@ class Answer{
 
     constructor(message){
         this.operation = message.operation;
-        this.expected = message.expected;
+        this.expect = message.expect;
     }
 
-    /**
-     * Add the information to the answer
-     * @param {Object} msg The message as received with nativeMessaging
-     * returns true if successfull, gpgme_error otherwise
-     */
-    add(msg){
-        if (this._response === undefined){
-            this._response = {};
+    collect(msg){
+        if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) {
+            return gpgme_error('CONN_UNEXPECTED_ANSWER');
+        }
+        if (this._responseb64 === undefined){
+            //this._responseb64 = [msg.response];
+            this._responseb64 = msg.response;
+            return true;
+        } else {
+            //this._responseb64.push(msg.response);
+            this._responseb64 += msg.response;
+            return true;
         }
-        let messageKeys = Object.keys(msg);
+    }
+
+    get message(){
+        if (this._responseb64 === undefined){
+            return gpgme_error('CONN_UNEXPECTED_ANSWER');
+        }
+        // let _decodedResponse = JSON.parse(atob(this._responseb64.join('')));
+        let _decodedResponse = JSON.parse(atob(this._responseb64));
+        let _response = {};
+        let messageKeys = Object.keys(_decodedResponse);
         let poa = permittedOperations[this.operation].answer;
         if (messageKeys.length === 0){
             return gpgme_error('CONN_UNEXPECTED_ANSWER');
@@ -191,80 +210,42 @@ class Answer{
             let key = messageKeys[i];
             switch (key) {
             case 'type':
-                if ( msg.type !== 'error' && poa.type.indexOf(msg.type) < 0){
+                if (_decodedResponse.type === 'error'){
+                    return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg));
+                } else if (poa.type.indexOf(_decodedResponse.type) < 0){
                     return gpgme_error('CONN_UNEXPECTED_ANSWER');
                 }
                 break;
-            case 'more':
+            case 'base64':
                 break;
-            default:
-                //data should be concatenated
-                if (poa.data.indexOf(key) >= 0){
-                    if (!this._response.hasOwnProperty(key)){
-                        this._response[key] = '';
-                    }
-                    this._response[key] += 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]){
-                        return gpgme_error('CONN_UNEXPECTED_ANSWER',msg[key]);
-                    }
+            case 'msg':
+                if (_decodedResponse.type === 'error'){
+                    return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg));
                 }
-                //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] = [];
-                    }
-
-                    if (Array.isArray(msg[key])) {
-                        for (let i=0; i< msg[key].length; i++) {
-                            this._response[key].push(msg[key][i]);
-                        }
-                    } else {
-                        this._response[key].push(msg[key]);
-                    }
+                break;
+            default:
+                if (!poa.data.hasOwnProperty(key)){
+                    return gpgme_error('CONN_UNEXPECTED_ANSWER');
                 }
-                else {
+                if( typeof(_decodedResponse[key]) !== poa.data[key] ){
                     return gpgme_error('CONN_UNEXPECTED_ANSWER');
                 }
-                break;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * @returns {Object} the assembled message, original data assumed to be
-     * (javascript-) strings
-     */
-    get message(){
-        let keys = Object.keys(this._response);
-        let msg = {};
-        let poa = permittedOperations[this.operation].answer;
-        for (let i=0; i < keys.length; i++) {
-            if (poa.data.indexOf(keys[i]) >= 0
-                && this._response.base64 === true
-            ) {
-                msg[keys[i]] = atob(this._response[keys[i]]);
-                if (this.expected === 'base64'){
-                    msg[keys[i]] = this._response[keys[i]];
-                } else {
-                    msg[keys[i]] = decodeURIComponent(
-                        atob(this._response[keys[i]]).split('').map(
+                if (_decodedResponse.base64 === true
+                    && poa.data[key] === 'string'
+                    && this.expect === undefined
+                ){
+                    _response[key] = decodeURIComponent(
+                        atob(_decodedResponse[key]).split('').map(
                             function(c) {
                                 return '%' +
-                                ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+                            ('00' + c.charCodeAt(0).toString(16)).slice(-2);
                             }).join(''));
+                } else {
+                    _response[key] = _decodedResponse[key];
                 }
-            } else {
-                msg[keys[i]] = this._response[keys[i]];
+                break;
             }
         }
-        return msg;
+        return _response;
     }
 }
diff --git a/lang/js/src/Message.js b/lang/js/src/Message.js
index 0ddda6c..7ccf7ef 100644
--- a/lang/js/src/Message.js
+++ b/lang/js/src/Message.js
@@ -46,7 +46,6 @@ export class GPGME_Message {
 
     constructor(operation){
         this.operation = operation;
-        this._expected = 'string';
     }
 
     set operation (op){
@@ -59,24 +58,50 @@ export class GPGME_Message {
             }
         }
     }
-
     get operation(){
         return this._msg.op;
     }
 
-    set expected(string){
-        if (string === 'base64'){
-            this._expected = 'base64';
+    /**
+     * Set the maximum size of responses from gpgme in bytes. Values allowed
+     * range from 10kB to 1MB. The lower limit is arbitrary, the upper limit
+     * fixed by browsers' nativeMessaging specifications
+     */
+    set chunksize(value){
+        if (
+            Number.isInteger(value) &&
+            value > 10 * 1024 &&
+            value <= 1024 * 1024
+        ){
+            this._chunksize = value;
+        }
+    }
+    get chunksize(){
+        if (this._chunksize === undefined){
+            return 1024 * 1023;
+        } else {
+            return this._chunksize;
         }
     }
 
-    get expected() {
-        if (this._expected === 'base64'){
-            return this._expected;
+    /**
+     * If expect is set to 'base64', the response is expected to be base64
+     * encoded binary
+     */
+    set expect(value){
+        if (value ==='base64'){
+            this._expect = value;
+        }
+    }
+
+    get expect(){
+        if ( this._expect === 'base64'){
+            return this._expect;
         }
-        return 'string';
+        return undefined;
     }
 
+
     /**
      * 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
@@ -188,6 +213,7 @@ export class GPGME_Message {
      */
     get message(){
         if (this.isComplete === true){
+            this._msg.chunksize = this.chunksize;
             return this._msg;
         }
         else {
@@ -201,10 +227,13 @@ export class GPGME_Message {
         return new Promise(function(resolve, reject) {
             if (me.isComplete === true) {
                 let conn  = new Connection;
+                if (me._msg.chunksize === undefined){
+                    me._msg.chunksize = 1023*1024;
+                }
                 conn.post(me).then(function(response) {
                     resolve(response);
                 }, function(reason) {
-                    reject(gpgme_error('GNUPG_ERROR', reason));
+                    reject(reason);
                 });
             }
             else {
diff --git a/lang/js/src/gpgmejs.js b/lang/js/src/gpgmejs.js
index cbad902..09bca7f 100644
--- a/lang/js/src/gpgmejs.js
+++ b/lang/js/src/gpgmejs.js
@@ -57,8 +57,8 @@ export class GpgME {
      * Keys used to encrypt the message
      * @param  {GPGME_Key|String|Array<String>|Array<GPGME_Key>} secretKeys
      * (optional) Keys used to sign the message
-     * @param {Boolean} base64 (optional) The data is already considered to be
-     * in base64 encoding
+     * @param {Boolean} base64 (optional) The data will be interpreted as
+     * base64 encoded data
      * @param {Boolean} armor (optional) Request the output as armored block
      * @param {Boolean} wildcard (optional) If true, recipient information will
      *  not be added to the message
@@ -109,24 +109,20 @@ export class GpgME {
     * Decrypt a Message
     * @param {String|Object} data text/data to be decrypted. Accepts Strings
     *  and Objects with a getText method
-    * @param {Boolean} base64 (optional) Response is expected to be base64
-    * encoded
     * @returns {Promise<Object>} decrypted message:
-        data:   The decrypted data.  This may be base64 encoded.
+        data:   The decrypted data.
         base64: Boolean indicating whether data is base64 encoded.
         mime:   A Boolean indicating whether the data is a MIME object.
         signatures: Array of signature Objects TODO not yet implemented.
-            // should be an object that can tell if all signatures are valid .
+            // should be an object that can tell if all signatures are valid.
     * @async
     */
-    decrypt(data, base64=false){
+    decrypt(data){
         if (data === undefined){
             return Promise.reject(gpgme_error('MSG_EMPTY'));
         }
         let msg = createMessage('decrypt');
-        if (base64 === true){
-            msg.expected = 'base64';
-        }
+
         if (msg instanceof Error){
             return Promise.reject(msg);
         }
@@ -165,10 +161,10 @@ export class GpgME {
         }
         msg.setParameter('mode', mode);
         putData(msg, data);
-        if (mode === 'detached') {
-            msg.expected = 'base64';
-        }
         return new Promise(function(resolve,reject) {
+            if (mode ==='detached'){
+                msg.expect= 'base64';
+            }
             msg.post().then( function(message) {
                 if (mode === 'clearsign'){
                     resolve({
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 445a40c..6ac33af 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -37,11 +37,8 @@
                 5000 ms would be too short
       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<*> arbitrary information that may result in a list
+          data: <Object>
+            the properties expected and their type, eg: {'data':'string'}
       }
   }
 */
@@ -67,9 +64,6 @@ export const permittedOperations = {
                 allowed: ['string'],
                 array_allowed: true
             },
-            'chunksize': {
-                allowed: ['number']
-            },
             'base64': {
                 allowed: ['boolean']
             },
@@ -101,9 +95,10 @@ export const permittedOperations = {
         },
         answer: {
             type: ['ciphertext'],
-            data: ['data'],
-            params: ['base64'],
-            infos: []
+            data: {
+                'data': 'string',
+                'base64':'boolean'
+            }
         }
     },
 
@@ -119,18 +114,18 @@ export const permittedOperations = {
                 allowed: ['string'],
                 allowed_data: ['cms', 'openpgp']
             },
-            'chunksize': {
-                allowed: ['number'],
-            },
             'base64': {
                 allowed: ['boolean']
             }
         },
         answer: {
             type: ['plaintext'],
-            data: ['data'],
-            params: ['base64', 'mime'],
-            infos: ['signatures']
+            data: {
+                'data': 'string',
+                'base64': 'boolean',
+                'mime': 'boolean',
+                'signatures': 'object'
+            }
         }
     },
 
@@ -149,9 +144,6 @@ export const permittedOperations = {
                 allowed: ['string'],
                 allowed_data: ['cms', 'openpgp']
             },
-            'chunksize': {
-                allowed: ['number'],
-            },
             'sender': {
                 allowed: ['string'],
             },
@@ -169,10 +161,11 @@ export const permittedOperations = {
         },
         answer: {
             type: ['signature', 'ciphertext'],
-            data: ['data'], // Unless armor mode is used a Base64 encoded binary
-            // signature.  In armor mode a string with an armored
-            // OpenPGP or a PEM message.
-            params: ['base64']
+            data: {
+                'data': 'string',
+                'base64':'boolean'
+            }
+
         }
     },
 
@@ -186,9 +179,6 @@ export const permittedOperations = {
                 allowed: ['string'],
                 allowed_data: ['cms', 'openpgp']
             },
-            'chunksize': {
-                allowed: ['number'],
-            },
             'secret': {
                 allowed: ['boolean']
             },
@@ -220,9 +210,10 @@ export const permittedOperations = {
         },
         answer: {
             type: ['keys'],
-            data: [],
-            params: ['base64'],
-            infos: ['keys']
+            data: {
+                'base64': 'boolean',
+                'keys': 'object'
+            }
         }
     },
 
@@ -233,9 +224,6 @@ export const permittedOperations = {
                 allowed: ['string'],
                 allowed_data: ['cms', 'openpgp']
             },
-            'chunksize': {
-                allowed: ['number'],
-            },
             'keys': {
                 allowed: ['string'],
                 array_allowed: true
@@ -259,8 +247,10 @@ export const permittedOperations = {
         },
         answer: {
             type: ['keys'],
-            data: ['data'],
-            params: ['base64']
+            data: {
+                'data': 'string',
+                'base64': 'boolean'
+            }
         }
     },
 
@@ -280,10 +270,10 @@ export const permittedOperations = {
             },
         },
         answer: {
-            infos: ['result'],
             type: [],
-            data: [],
-            params: []
+            data: {
+                'result': 'Object'
+            }
         }
     },
 
@@ -299,15 +289,15 @@ export const permittedOperations = {
                 allowed: ['string'],
                 allowed_data: ['cms', 'openpgp']
             },
-            // 'secret': { not yet implemented
+            // 'secret': { not implemented
             //     allowed: ['boolean']
             // }
 
         },
         answer: {
-            data: [],
-            params:['success'],
-            infos: []
+            data: {
+                'success': 'boolean'
+            }
         }
     },
 
@@ -316,9 +306,10 @@ export const permittedOperations = {
         optional: {},
         answer: {
             type:  [''],
-            data: ['gpgme'],
-            infos: ['info'],
-            params:[]
+            data: {
+                'gpgme': 'string',
+                'info': 'object'
+            }
         }
     }
 
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
index ce1dd0c..169e8eb 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -339,7 +339,8 @@ function unittests (){
             test0.setParameter('keys', hp.validFingerprints);
 
             expect(test0.message).to.not.be.null;
-            expect(test0.message).to.have.keys('op', 'data', 'keys');
+            expect(test0.message).to.have.keys('op', 'data', 'keys',
+                'chunksize');
             expect(test0.message.op).to.equal('encrypt');
             expect(test0.message.data).to.equal(
                 mp.valid_encrypt_data);

-----------------------------------------------------------------------

Summary of changes:
 .../tests/encryptDecryptTest.js                    |  31 +----
 .../BrowserTestExtension/tests/longRunningTests.js |  38 +++---
 lang/js/src/Connection.js                          | 143 +++++++++------------
 lang/js/src/Message.js                             |  49 +++++--
 lang/js/src/gpgmejs.js                             |  22 ++--
 lang/js/src/permittedOperations.js                 |  81 ++++++------
 lang/js/unittests.js                               |   3 +-
 7 files changed, 170 insertions(+), 197 deletions(-)


hooks/post-receive
-- 
GnuPG Made Easy
http://git.gnupg.org
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.