js: transfer encoding changes
authorMaximilian Krambach <maximilian.krambach@intevation.de>
Tue, 22 May 2018 12:24:16 +0000 (14:24 +0200)
committerMaximilian Krambach <maximilian.krambach@intevation.de>
Tue, 22 May 2018 12:24:16 +0000 (14:24 +0200)
--

* Uint8Arrays are not supported for now there are unsolved issues in
  conversion, and they are lower priority

* encrypt gains a new option to indicate that input values are base64
  encoded
* as decrypted values are always base64 encoded, the option base64 will
  not try to decode the result into utf, but leave it as it is

lang/js/BrowserTestExtension/tests/encryptDecryptTest.js
lang/js/BrowserTestExtension/tests/longRunningTests.js
lang/js/BrowserTestExtension/tests/openpgpModeTest.js
lang/js/src/Connection.js
lang/js/src/Message.js
lang/js/src/gpgmejs.js
lang/js/src/permittedOperations.js

index 5c53403..2fe955e 100644 (file)
@@ -1,3 +1,4 @@
+
 /* gpgme.js - Javascript integration for gpgme
  * Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
  *
@@ -39,6 +40,22 @@ describe('Encryption and Decryption', function () {
                 });
         });
     });
+
+    it('Decrypt simple non-ascii', function (done) {
+        let prm = Gpgmejs.init();
+        prm.then(function (context) {
+            let data = encryptedData;
+            context.decrypt(data).then(
+                function (result) {
+                    expect(result).to.not.be.empty;
+                    expect(result.data).to.be.a('string');
+                    expect(result.data).to.equal(
+                        '¡Äußerste µ€ før ñoquis@hóme! Добрый день\n');
+                    done();
+            });
+        });
+    }).timeout(3000);
+
     it('Roundtrip does not destroy trailing whitespace',
         function (done) {
             let prm = Gpgmejs.init();
@@ -64,7 +81,7 @@ describe('Encryption and Decryption', function () {
                             });
                     });
             });
-        }).timeout(5000);
+    }).timeout(5000);
 
     for (let j = 0; j < inputvalues.encrypt.good.data_nonascii_32.length; j++){
         it('Roundtrip with >1MB non-ascii input meeting default chunksize (' + (j + 1) + '/' + inputvalues.encrypt.good.data_nonascii_32.length + ')',
@@ -96,21 +113,113 @@ describe('Encryption and Decryption', function () {
                                 });
                         });
                 });
-        }).timeout(5000);
+        }).timeout(3000);
     };
 
-    it('Decrypt simple non-ascii', function (done) {
+    it('Random data, as string', function (done) {
+        let data = bigString(1000);
         let prm = Gpgmejs.init();
         prm.then(function (context) {
-            data = encryptedData;
-            context.decrypt(data).then(
-                function (result) {
-                    expect(result).to.not.be.empty;
-                    expect(result.data).to.be.a('string');
-                    expect(result.data).to.equal(
-                        '¡Äußerste µ€ før ñoquis@hóme! Добрый день\n');
-                    done();
-            });
+            context.encrypt(data,
+                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).then(
+                        function (result) {
+                            expect(result).to.not.be.empty;
+                            expect(result.data).to.be.a('string');
+                            expect(result.data).to.equal(data);
+                            context.connection.disconnect();
+                            done();
+                        });
+                });
         });
     }).timeout(3000);
+
+    it('Data, input as base64', function (done) {
+        let data = inputvalues.encrypt.good.data;
+        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).then(
+                        function (result) {
+                            expect(result).to.not.be.empty;
+                            expect(result.data).to.be.a('string');
+                            expect(data).to.equal(data);
+                            context.connection.disconnect();
+                            done();
+                        });
+                });
+        });
+    }).timeout(3000);
+
+    it('Random data, input as base64', function (done) {
+        //TODO fails. The result is
+        let data = bigBoringString(0.001);
+        let b64data = btoa(data);
+        let prm = Gpgmejs.init();
+        prm.then(function (context) {
+            context.encrypt(b64data,
+                inputvalues.encrypt.good.fingerprint, true).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).then(
+                        function (result) {
+                            expect(result).to.not.be.empty;
+                            expect(result.data).to.be.a('string');
+                            expect(result.data).to.equal(data);
+                            context.connection.disconnect();
+                            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(
+                        function (result) {
+                            expect(result).to.not.be.empty;
+                            expect(result.data).to.be.a('string');
+                            expect(result.data).to.equal(b64data);
+                            context.connection.disconnect();
+                            done();
+                        });
+                });
+        });
+    }).timeout(3000);
+
+
 });
index c95bebd..4e55fd2 100644 (file)
@@ -37,29 +37,4 @@ describe('Long running Encryption/Decryption', function () {
         }).timeout(8000);
     };
 
-    it('Successful encrypt 1 MB Uint8Array', function (done) {
-        //TODO: this succeeds, but result may be bogus (String with byte values as numbers)
-        let prm = Gpgmejs.init();
-        let data = bigUint8(1);
-        prm.then(function (context) {
-                context.encrypt(data,
-                    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).then(
-                                function(result){
-                                    expect(result).to.not.be.empty;
-                                    expect(result.data).to.be.a('string');
-                                    expect(result.data).to.equal(data);
-                                    done();
-                            });
-                    });
-            });
-    }).timeout(5000);
-
 });
index 98b6e1d..cccaf60 100644 (file)
@@ -41,33 +41,6 @@ describe('Encrypting-Decrypting in openpgp mode, using a Message object', functi
                 });
         });
     });
-    it('Encrypt-Decrypt, sending Uint8Array as data', function (done) {
-        //TODO! fails. Reason is that atob<->btoa destroys the uint8Array,
-        // resulting in a string of constituyent numbers
-        // (error already occurs in encryption)
-
-        let prm = Gpgmejs.init({api_style: 'gpgme_openpgpjs'});
-        prm.then(function (context) {
-            let input = bigUint8(0.3);
-            expect(input).to.be.an.instanceof(Uint8Array);
-            context.encrypt({
-                data: input,
-                publicKeys: 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({message:answer.data}).then(function (result) {
-                    expect(result).to.not.be.empty;
-                    expect(result.data).to.be.an.instanceof(Uint8Array);
-                    expect(result.data).to.equal(input);
-                    context._GpgME.connection.disconnect();
-                    done();
-                });
-            });
-        });
-    });
     it('Keys as Fingerprints', function(done){
         let prm = Gpgmejs.init({api_style: 'gpgme_openpgpjs'});
             let input = inputvalues.encrypt.good.data_nonascii;
index 1931a55..9c2a642 100644 (file)
@@ -93,7 +93,7 @@ export class Connection{
         }
         let me = this;
         return new Promise(function(resolve, reject){
-            let answer = new Answer(message.operation);
+            let answer = new Answer(message);
             let listener = function(msg) {
                 if (!msg){
                     me._connection.onMessage.removeListener(listener)
@@ -147,8 +147,9 @@ export class Connection{
  */
 class Answer{
 
-    constructor(operation){
-        this.operation = operation;
+    constructor(message){
+        this.operation = message.operation;
+        this.expected = message.expected;
     }
 
     /**
@@ -210,26 +211,31 @@ class Answer{
     }
 
     /**
-     * @returns {Object} the assembled message.
-     * TODO: does not care yet if completed.
+     * @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){
-                if (this._response.base64 == true){
-                    let respatob =  atob(this._response[keys[i]]);
-
-                    let result = decodeURIComponent(
-                        respatob.split('').map(function(c) {
+            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(function(c) {
                             return '%' +
-                            ('00' + c.charCodeAt(0).toString(16)).slice(-2);
+                                ('00' + c.charCodeAt(0).toString(16)).slice(-2);
                         }).join(''));
-                    this._response[keys[i]] = result;
                 }
+            } else {
+                msg[keys[i]] = this._response[keys[i]];
             }
         }
-        return this._response;
+        return msg;
     }
 }
index 6a59c3e..932212a 100644 (file)
@@ -41,6 +41,7 @@ export class GPGME_Message {
 
     constructor(operation){
         this.operation = operation;
+        this._expected = 'string';
     }
 
     set operation (op){
@@ -58,6 +59,19 @@ export class GPGME_Message {
         return this._msg.op;
     }
 
+    set expected(string){
+        if (string === 'base64'){
+            this._expected = 'base64';
+        }
+    }
+
+    get expected() {
+       if (this._expected === "base64"){
+           return this._expected;
+       }
+       return "string";
+    }
+
     /**
      * 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
index d106f4f..01cb92c 100644 (file)
@@ -64,11 +64,11 @@ export class GpgME {
     }
 
     /**
-     * @param {String|Uint8Array} data text/data to be encrypted as String/Uint8Array
+     * @param {String} data text/data to be encrypted as String
      * @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){
+    encrypt(data, publicKeys, base64=false, wildcard=false){
 
         let msg = createMessage('encrypt');
         if (msg instanceof Error){
@@ -77,11 +77,14 @@ export class GpgME {
         // TODO temporary
         msg.setParameter('armor', true);
         msg.setParameter('always-trust', true);
-
+        if (base64 === true) {
+            msg.setParameter('base64', true);
+        }
         let pubkeys = toKeyIdArray(publicKeys);
         msg.setParameter('keys', pubkeys);
         putData(msg, data);
-        if (wildcard === true){msg.setParameter('throw-keyids', true);
+        if (wildcard === true){
+            msg.setParameter('throw-keyids', true);
         };
         if (msg.isComplete === true){
             return this.connection.post(msg);
@@ -91,7 +94,8 @@ export class GpgME {
     }
 
     /**
-    * @param  {String} data TODO Format: base64? String? Message with the encrypted data
+    * @param  {String} data TODO base64? Message with the encrypted data
+    * @param {Boolean} base64 (optional) Response should stay base64
     * @returns {Promise<Object>} decrypted message:
         data:   The decrypted data.  This may be base64 encoded.
         base64: Boolean indicating whether data is base64 encoded.
@@ -100,11 +104,14 @@ export class GpgME {
     * @async
     */
 
-    decrypt(data){
+    decrypt(data, base64=false){
         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);
         }
@@ -156,11 +163,9 @@ export class GpgME {
 }
 
 /**
- * Sets the data of the message, converting Uint8Array to base64 and setting
- * the base64 flag
+ * Sets the data of the message
  * @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 putData(message, data){
     if (!message || !message instanceof GPGME_Message ) {
@@ -168,30 +173,15 @@ function putData(message, data){
     }
     if (!data){
         return gpgme_error('PARAM_WRONG');
-    } else if (data instanceof Uint8Array){
-        message.setParameter('base64', true);
-        // TODO: btoa turns the array into a string
-        // of comma separated  of numbers
-        // atob(data).split(',') would result in a "normal" array of numbers
-        // atob(btoa(data)).split(',') would result in a "normal" array of numbers
-        // would result in a "normal" array of numbers
-        message.setParameter ('data', btoa(data));
-
     } else if (typeof(data) === 'string') {
-        message.setParameter('base64', false);
         message.setParameter('data', data);
     } else if (
         typeof(data) === 'object' &&
         typeof(data.getText) === 'function'
     ){
         let txt = data.getText();
-        if (txt instanceof Uint8Array){
-            message.setParameter('base64', true);
-            message.setParameter ('data', btoa(txt));
-        }
-        else if (typeof(txt) === 'string'){
-            message.setParameter('base64', false);
-            message.setParameter ('data', txt);
+        if (typeof(txt) === 'string'){
+            message.setParameter('data', txt);
         } else {
             return gpgme_error('PARAM_WRONG');
         }
index 59597aa..da46a1f 100644 (file)
@@ -51,7 +51,7 @@ export const permittedOperations = {
                 array_allowed: true
             },
             'data': {
-                allowed: ['string', 'Uint8Array']
+                allowed: ['string']
             }
         },
         optional: {
@@ -103,7 +103,7 @@ export const permittedOperations = {
         pinentry: true,
         required: {
             'data': {
-                allowed: ['string', 'Uint8Array']
+                allowed: ['string']
             }
         },
         optional: {