js: transfer encoding changes
-- * 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
This commit is contained in:
parent
6b4caee039
commit
ecad772635
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/* gpgme.js - Javascript integration for gpgme
|
/* gpgme.js - Javascript integration for gpgme
|
||||||
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
|
* 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',
|
it('Roundtrip does not destroy trailing whitespace',
|
||||||
function (done) {
|
function (done) {
|
||||||
let prm = Gpgmejs.init();
|
let prm = Gpgmejs.init();
|
||||||
@ -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();
|
let prm = Gpgmejs.init();
|
||||||
prm.then(function (context) {
|
prm.then(function (context) {
|
||||||
data = encryptedData;
|
context.encrypt(data,
|
||||||
context.decrypt(data).then(
|
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) {
|
function (result) {
|
||||||
expect(result).to.not.be.empty;
|
expect(result).to.not.be.empty;
|
||||||
expect(result.data).to.be.a('string');
|
expect(result.data).to.be.a('string');
|
||||||
expect(result.data).to.equal(
|
expect(result.data).to.equal(data);
|
||||||
'¡Äußerste µ€ før ñoquis@hóme! Добрый день\n');
|
context.connection.disconnect();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).timeout(3000);
|
});
|
||||||
|
}).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);
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -37,29 +37,4 @@ describe('Long running Encryption/Decryption', function () {
|
|||||||
}).timeout(8000);
|
}).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);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -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){
|
it('Keys as Fingerprints', function(done){
|
||||||
let prm = Gpgmejs.init({api_style: 'gpgme_openpgpjs'});
|
let prm = Gpgmejs.init({api_style: 'gpgme_openpgpjs'});
|
||||||
let input = inputvalues.encrypt.good.data_nonascii;
|
let input = inputvalues.encrypt.good.data_nonascii;
|
||||||
|
@ -93,7 +93,7 @@ export class Connection{
|
|||||||
}
|
}
|
||||||
let me = this;
|
let me = this;
|
||||||
return new Promise(function(resolve, reject){
|
return new Promise(function(resolve, reject){
|
||||||
let answer = new Answer(message.operation);
|
let answer = new Answer(message);
|
||||||
let listener = function(msg) {
|
let listener = function(msg) {
|
||||||
if (!msg){
|
if (!msg){
|
||||||
me._connection.onMessage.removeListener(listener)
|
me._connection.onMessage.removeListener(listener)
|
||||||
@ -147,8 +147,9 @@ export class Connection{
|
|||||||
*/
|
*/
|
||||||
class Answer{
|
class Answer{
|
||||||
|
|
||||||
constructor(operation){
|
constructor(message){
|
||||||
this.operation = operation;
|
this.operation = message.operation;
|
||||||
|
this.expected = message.expected;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -210,26 +211,31 @@ class Answer{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {Object} the assembled message.
|
* @returns {Object} the assembled message, original data assumed to be
|
||||||
* TODO: does not care yet if completed.
|
* (javascript-) strings
|
||||||
*/
|
*/
|
||||||
get message(){
|
get message(){
|
||||||
let keys = Object.keys(this._response);
|
let keys = Object.keys(this._response);
|
||||||
|
let msg = {};
|
||||||
let poa = permittedOperations[this.operation].answer;
|
let poa = permittedOperations[this.operation].answer;
|
||||||
for (let i=0; i < keys.length; i++) {
|
for (let i=0; i < keys.length; i++) {
|
||||||
if (poa.data.indexOf(keys[i]) >= 0){
|
if (poa.data.indexOf(keys[i]) >= 0
|
||||||
if (this._response.base64 == true){
|
&& this._response.base64 === true
|
||||||
let respatob = atob(this._response[keys[i]]);
|
) {
|
||||||
|
msg[keys[i]] = atob(this._response[keys[i]]);
|
||||||
let result = decodeURIComponent(
|
if (this.expected === 'base64'){
|
||||||
respatob.split('').map(function(c) {
|
msg[keys[i]] = this._response[keys[i]];
|
||||||
|
} else {
|
||||||
|
msg[keys[i]] = decodeURIComponent(
|
||||||
|
atob(this._response[keys[i]]).split('').map(function(c) {
|
||||||
return '%' +
|
return '%' +
|
||||||
('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
}).join(''));
|
}).join(''));
|
||||||
this._response[keys[i]] = result;
|
}
|
||||||
|
} else {
|
||||||
|
msg[keys[i]] = this._response[keys[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return msg;
|
||||||
return this._response;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ export class GPGME_Message {
|
|||||||
|
|
||||||
constructor(operation){
|
constructor(operation){
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
|
this._expected = 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
set operation (op){
|
set operation (op){
|
||||||
@ -58,6 +59,19 @@ export class GPGME_Message {
|
|||||||
return this._msg.op;
|
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
|
* 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
|
* first, to be able to check if the parameter is permittted
|
||||||
|
@ -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 {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
|
* @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');
|
let msg = createMessage('encrypt');
|
||||||
if (msg instanceof Error){
|
if (msg instanceof Error){
|
||||||
@ -77,11 +77,14 @@ export class GpgME {
|
|||||||
// TODO temporary
|
// TODO temporary
|
||||||
msg.setParameter('armor', true);
|
msg.setParameter('armor', true);
|
||||||
msg.setParameter('always-trust', true);
|
msg.setParameter('always-trust', true);
|
||||||
|
if (base64 === true) {
|
||||||
|
msg.setParameter('base64', true);
|
||||||
|
}
|
||||||
let pubkeys = toKeyIdArray(publicKeys);
|
let pubkeys = toKeyIdArray(publicKeys);
|
||||||
msg.setParameter('keys', pubkeys);
|
msg.setParameter('keys', pubkeys);
|
||||||
putData(msg, data);
|
putData(msg, data);
|
||||||
if (wildcard === true){msg.setParameter('throw-keyids', true);
|
if (wildcard === true){
|
||||||
|
msg.setParameter('throw-keyids', true);
|
||||||
};
|
};
|
||||||
if (msg.isComplete === true){
|
if (msg.isComplete === true){
|
||||||
return this.connection.post(msg);
|
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:
|
* @returns {Promise<Object>} decrypted message:
|
||||||
data: The decrypted data. This may be base64 encoded.
|
data: The decrypted data. This may be base64 encoded.
|
||||||
base64: Boolean indicating whether data is base64 encoded.
|
base64: Boolean indicating whether data is base64 encoded.
|
||||||
@ -100,11 +104,14 @@ export class GpgME {
|
|||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
|
|
||||||
decrypt(data){
|
decrypt(data, base64=false){
|
||||||
if (data === undefined){
|
if (data === undefined){
|
||||||
return Promise.reject(gpgme_error('MSG_EMPTY'));
|
return Promise.reject(gpgme_error('MSG_EMPTY'));
|
||||||
}
|
}
|
||||||
let msg = createMessage('decrypt');
|
let msg = createMessage('decrypt');
|
||||||
|
if (base64 === true){
|
||||||
|
msg.expected = 'base64';
|
||||||
|
}
|
||||||
if (msg instanceof Error){
|
if (msg instanceof Error){
|
||||||
return Promise.reject(msg);
|
return Promise.reject(msg);
|
||||||
}
|
}
|
||||||
@ -156,11 +163,9 @@ export class GpgME {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the data of the message, converting Uint8Array to base64 and setting
|
* Sets the data of the message
|
||||||
* the base64 flag
|
|
||||||
* @param {GPGME_Message} message The message where this data will be set
|
* @param {GPGME_Message} message The message where this data will be set
|
||||||
* @param {*} data The data to enter
|
* @param {*} data The data to enter
|
||||||
* @param {String} propertyname // TODO unchecked still
|
|
||||||
*/
|
*/
|
||||||
function putData(message, data){
|
function putData(message, data){
|
||||||
if (!message || !message instanceof GPGME_Message ) {
|
if (!message || !message instanceof GPGME_Message ) {
|
||||||
@ -168,29 +173,14 @@ function putData(message, data){
|
|||||||
}
|
}
|
||||||
if (!data){
|
if (!data){
|
||||||
return gpgme_error('PARAM_WRONG');
|
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') {
|
} else if (typeof(data) === 'string') {
|
||||||
message.setParameter('base64', false);
|
|
||||||
message.setParameter('data', data);
|
message.setParameter('data', data);
|
||||||
} else if (
|
} else if (
|
||||||
typeof(data) === 'object' &&
|
typeof(data) === 'object' &&
|
||||||
typeof(data.getText) === 'function'
|
typeof(data.getText) === 'function'
|
||||||
){
|
){
|
||||||
let txt = data.getText();
|
let txt = data.getText();
|
||||||
if (txt instanceof Uint8Array){
|
if (typeof(txt) === 'string'){
|
||||||
message.setParameter('base64', true);
|
|
||||||
message.setParameter ('data', btoa(txt));
|
|
||||||
}
|
|
||||||
else if (typeof(txt) === 'string'){
|
|
||||||
message.setParameter('base64', false);
|
|
||||||
message.setParameter('data', txt);
|
message.setParameter('data', txt);
|
||||||
} else {
|
} else {
|
||||||
return gpgme_error('PARAM_WRONG');
|
return gpgme_error('PARAM_WRONG');
|
||||||
|
@ -51,7 +51,7 @@ export const permittedOperations = {
|
|||||||
array_allowed: true
|
array_allowed: true
|
||||||
},
|
},
|
||||||
'data': {
|
'data': {
|
||||||
allowed: ['string', 'Uint8Array']
|
allowed: ['string']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
@ -103,7 +103,7 @@ export const permittedOperations = {
|
|||||||
pinentry: true,
|
pinentry: true,
|
||||||
required: {
|
required: {
|
||||||
'data': {
|
'data': {
|
||||||
allowed: ['string', 'Uint8Array']
|
allowed: ['string']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
optional: {
|
optional: {
|
||||||
|
Loading…
Reference in New Issue
Block a user