diff --git a/lang/js/src/Connection.js b/lang/js/src/Connection.js
index 07df5def..3b442622 100644
--- a/lang/js/src/Connection.js
+++ b/lang/js/src/Connection.js
@@ -1,257 +1,263 @@
/* 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 .
* 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'
import { gpgme_error } from "./Errors"
import { GPGME_Message, createMessage } from "./Message";
/**
* A Connection handles the nativeMessaging interaction.
*/
export class Connection{
constructor(){
this.connect();
}
/**
* Retrieves the information about the backend.
* @param {Boolean} details (optional) If set to false, the promise will
* just return a connection status
* @returns {Promise}
* {String} The property 'gpgme': Version number of gpgme
* {Array} 'info' Further information about the backends.
* Example:
* "protocol": "OpenPGP",
* "fname": "/usr/bin/gpg",
* "version": "2.2.6",
* "req_version": "1.4.0",
* "homedir": "default"
*/
checkConnection(details = true){
if (details === true) {
return this.post(createMessage('version'));
} else {
let me = this;
return new Promise(function(resolve,reject) {
Promise.race([
me.post(createMessage('version')),
new Promise(function(resolve, reject){
setTimeout(function(){
reject(gpgme_error('CONN_TIMEOUT'));
}, 500);
})
]).then(function(result){
resolve(true);
}, function(reject){
resolve(false);
});
});
}
}
/**
* Immediately closes the open port.
*/
disconnect() {
if (this._connection){
this._connection.disconnect();
this._connection = null;
}
}
/**
* Opens a nativeMessaging port.
*/
connect(){
if (!this._connection){
this._connection = chrome.runtime.connectNative('gpgmejson');
}
}
/**
* Sends a message and resolves with the answer.
* @param {GPGME_Message} message
* @returns {Promise} the gnupg answer, or rejection with error
* information.
*/
post(message){
if (!this._connection) {
}
if (!message || !message instanceof GPGME_Message){
return Promise.reject(gpgme_error('PARAM_WRONG'), message);
}
if (message.isComplete !== true){
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
}
let me = this;
return new Promise(function(resolve, reject){
let answer = new Answer(message);
let listener = function(msg) {
if (!msg){
me._connection.onMessage.removeListener(listener)
reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else if (msg.type === "error"){
me._connection.onMessage.removeListener(listener);
reject(gpgme_error('GNUPG_ERROR', msg.msg));
} else {
let answer_result = answer.add(msg);
if (answer_result !== true){
me._connection.onMessage.removeListener(listener);
reject(answer_result);
}
if (msg.more === true){
me._connection.postMessage({'op': 'getmore'});
} else {
me._connection.onMessage.removeListener(listener)
resolve(answer.message);
}
}
};
me._connection.onMessage.addListener(listener);
if (permittedOperations[message.operation].pinentry){
return me._connection.postMessage(message.message);
} else {
return Promise.race([
me._connection.postMessage(message.message),
function(resolve, reject){
setTimeout(function(){
reject(gpgme_error('CONN_TIMEOUT'));
}, 5000);
}]).then(function(result){
return result;
}, function(reject){
if(!reject instanceof Error) {
return gpgme_error('GNUPG_ERROR', reject);
} else {
return reject;
}
});
}
});
}
};
/**
* 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 returning messages
*/
class Answer{
constructor(message){
this.operation = message.operation;
this.expected = message.expected;
}
/**
* 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 = {};
}
let messageKeys = Object.keys(msg);
let poa = permittedOperations[this.operation].answer;
if (messageKeys.length === 0){
return gpgme_error('CONN_UNEXPECTED_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){
return gpgme_error('CONN_UNEXPECTED_ANSWER');
}
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] += 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]);
}
}
//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[key].push(msg[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][i]);
+ }
}
else {
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(function(c) {
return '%' +
('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
} else {
msg[keys[i]] = this._response[keys[i]];
}
}
return msg;
}
}
diff --git a/lang/js/src/Errors.js b/lang/js/src/Errors.js
index 7e98f319..fa8a4efe 100644
--- a/lang/js/src/Errors.js
+++ b/lang/js/src/Errors.js
@@ -1,125 +1,129 @@
/* 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
const err_list = {
// Connection
'CONN_NO_CONNECT': {
msg:'Connection with the nativeMessaging host could not be'
+ ' established.',
type: 'error'
},
'CONN_EMPTY_GPG_ANSWER':{
msg: 'The nativeMessaging answer was empty.',
type: 'error'
},
'CONN_TIMEOUT': {
msg: 'A connection timeout was exceeded.',
type: 'error'
},
'CONN_UNEXPECTED_ANSWER': {
msg: 'The answer from gnupg was not as expected.',
type: 'error'
},
'CONN_ALREADY_CONNECTED':{
msg: 'A connection was already established.',
type: 'warning'
},
// Message/Data
'MSG_INCOMPLETE': {
msg: 'The Message did not match the minimum requirements for'
+ ' the interaction.',
type: 'error'
},
'MSG_EMPTY' : {
msg: 'The Message is empty.',
type: 'error'
},
'MSG_WRONG_OP': {
msg: 'The operation requested could not be found',
type: 'error'
},
'MSG_NO_KEYS' : {
msg: 'There were no valid keys provided.',
type: 'warning'
},
'MSG_NOT_A_FPR': {
msg: 'The String is not an accepted fingerprint',
type: 'warning'
},
'KEY_INVALID': {
msg:'Key object is invalid',
type: 'error'
},
+ 'KEY_NOKEY': {
+ msg:'This key does not exist in GPG',
+ type: 'error'
+ },
// generic
'PARAM_WRONG':{
msg: 'Invalid parameter was found',
type: 'error'
},
'PARAM_IGNORED': {
msg: 'An parameter was set that has no effect in gpgmejs',
type: 'warning'
},
'GENERIC_ERROR': {
msg: 'Unspecified error',
type: 'error'
}
};
/**
* Checks the given error code and returns an error object with some
* information about meaning and origin
* @param {*} code Error code. Should be in err_list or 'GNUPG_ERROR'
* @param {*} info Error message passed through if code is 'GNUPG_ERROR'
*/
export function gpgme_error(code = 'GENERIC_ERROR', info){
if (err_list.hasOwnProperty(code)){
if (err_list[code].type === 'error'){
return new GPGME_Error(code);
}
if (err_list[code].type === 'warning'){
console.warn(code + ': ' + err_list[code].msg);
}
return null;
} else if (code === 'GNUPG_ERROR'){
return new GPGME_Error(code, info);
}
else {
return new GPGME_Error('GENERIC_ERROR');
}
}
class GPGME_Error extends Error{
constructor(code, msg=''){
if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){
super(msg);
} else if (err_list.hasOwnProperty(code)){
super(err_list[code].msg);
} else {
super(err_list['GENERIC_ERROR'].msg);
}
this.code = code || 'GENERIC_ERROR';
}
set code(value){
this._code = value;
}
get code(){
return this._code;
}
}
\ No newline at end of file
diff --git a/lang/js/src/Helpers.js b/lang/js/src/Helpers.js
index fd0e7200..b26f40fb 100644
--- a/lang/js/src/Helpers.js
+++ b/lang/js/src/Helpers.js
@@ -1,103 +1,104 @@
/* 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
import { gpgme_error } from "./Errors";
import { GPGME_Key } from "./Key";
/**
* Tries to return an array of fingerprints, either from input fingerprints or
* from Key objects
* @param {Key |Array| GPGME_Key | Array|String|Array} input
* @returns {Array} Array of fingerprints.
*/
export function toKeyIdArray(input){
if (!input){
gpgme_error('MSG_NO_KEYS');
return [];
}
if (!Array.isArray(input)){
input = [input];
}
let result = [];
for (let i=0; i < input.length; i++){
if (typeof(input[i]) === 'string'){
if (isFingerprint(input[i]) === true){
result.push(input[i]);
} else {
gpgme_error('MSG_NOT_A_FPR');
}
} else if (typeof(input[i]) === 'object'){
let fpr = '';
if (input[i] instanceof GPGME_Key){
fpr = input[i].fingerprint;
} else if (input[i].hasOwnProperty('primaryKey') &&
input[i].primaryKey.hasOwnProperty('getFingerprint')){
fpr = input[i].primaryKey.getFingerprint();
}
if (isFingerprint(fpr) === true){
result.push(fpr);
} else {
gpgme_error('MSG_NOT_A_FPR');
}
} else {
return gpgme_error('PARAM_WRONG');
}
}
if (result.length === 0){
gpgme_error('MSG_NO_KEYS');
return [];
} else {
return result;
}
};
/**
* 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 no usage; check if the input is a valid Hex string with a length of 16
+ * check if the input is a valid Hex string with a length of 16
*/
-function isLongId(string){
+export function isLongId(string){
return hextest(string, 16);
};
// TODO still not needed anywhere
function isShortId(string){
return hextest(string, 8);
};
diff --git a/lang/js/src/Key.js b/lang/js/src/Key.js
index 075a190e..7d3d82b1 100644
--- a/lang/js/src/Key.js
+++ b/lang/js/src/Key.js
@@ -1,244 +1,427 @@
/* 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* The key class allows to query the information defined in gpgme Key Objects
* (see https://www.gnupg.org/documentation/manuals/gpgme/Key-objects.html)
*
* This is a stub, as the gpgme-json side is not yet implemented
*
*/
-import { isFingerprint } from './Helpers'
+import { isFingerprint, isLongId } from './Helpers'
import { gpgme_error } from './Errors'
import { createMessage } from './Message';
import { permittedOperations } from './permittedOperations';
import { Connection } from './Connection';
-
+/**
+ * Validates the fingerprint, and checks for tha availability of a connection.
+ * If both are available, a Key will be returned.
+ * @param {String} fingerprint
+ * @param {Object} parent Either a Connection, or the invoking object with a
+ * Connection (e.g. Keyring)
+ */
export function createKey(fingerprint, parent){
if (!isFingerprint(fingerprint)){
return gpgme_error('PARAM_WRONG');
}
if ( parent instanceof Connection){
return new GPGME_Key(fingerprint, parent);
} else if ( parent.hasOwnProperty('connection') &&
parent.connection instanceof Connection){
return new GPGME_Key(fingerprint, parent.connection);
} else {
return gpgme_error('PARAM_WRONG');
}
}
+/**
+ * Representing the Keys as stored in GPG
+ */
export class GPGME_Key {
constructor(fingerprint, connection){
this.fingerprint = fingerprint;
this.connection = connection;
}
set connection(conn){
if (this._connection instanceof Connection) {
gpgme_error('CONN_ALREADY_CONNECTED');
} else if (conn instanceof Connection ) {
this._connection = conn;
}
}
get connection(){
- if (!this._fingerprint){
+ if (!this._data.fingerprint){
return gpgme_error('KEY_INVALID');
}
if (!this._connection instanceof Connection){
return gpgme_error('CONN_NO_CONNECT');
} else {
return this._connection;
}
}
set fingerprint(fpr){
- if (isFingerprint(fpr) === true && !this._fingerprint){
- this._fingerprint = fpr;
+ if (isFingerprint(fpr) === true) {
+ if (this._data === undefined) {
+ this._data = {fingerprint: fpr};
+ } else {
+ if (this._data.fingerprint === undefined){
+ this._data.fingerprint = fpr;
+ }
+ }
}
}
get fingerprint(){
- if (!this._fingerprint){
+ if (!this._data || !this._data.fingerprint){
return gpgme_error('KEY_INVALID');
}
- return this._fingerprint;
+ return this._data.fingerprint;
}
/**
- * hasSecret returns true if a secret subkey is included in this Key
+ *
+ * @param {Object} data Bulk set data for this key, with the Object as sent
+ * by gpgme-json.
+ * @returns {GPGME_Key|GPGME_Error} The Key object itself after values have
+ * been set
*/
- get hasSecret(){
- return this.checkKey('secret');
- }
-
- get isRevoked(){
- return this.checkKey('revoked');
- }
-
- get isExpired(){
- return this.checkKey('expired');
- }
-
- get isDisabled(){
- return this.checkKey('disabled');
- }
-
- get isInvalid(){
- return this.checkKey('invalid');
- }
-
- get canEncrypt(){
- return this.checkKey('can_encrypt');
- }
+ setKeydata(data){
+ if (this._data === undefined) {
+ this._data = {};
+ }
+ if (
+ typeof(data) !== 'object') {
+ return gpgme_error('KEY_INVALID');
+ }
+ if (!this._data.fingerprint && isFingerprint(data.fingerprint)){
+ if (data.fingerprint !== this.fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
+ this._data.fingerprint = data.fingerprint;
+ } else if (this._data.fingerprint !== data.fingerprint){
+ return gpgme_error('KEY_INVALID');
+ }
- get canSign(){
- return this.checkKey('can_sign');
- }
+ let booleans = ['expired', 'disabled','invalid','can_encrypt',
+ 'can_sign','can_certify','can_authenticate','secret',
+ 'is_qualified'];
+ for (let b =0; b < booleans.length; b++) {
+ if (
+ !data.hasOwnProperty(booleans[b]) ||
+ typeof(data[booleans[b]]) !== 'boolean'
+ ){
+ return gpgme_error('KEY_INVALID');
+ }
+ this._data[booleans[b]] = data[booleans[b]];
+ }
+ if (typeof(data.protocol) !== 'string'){
+ return gpgme_error('KEY_INVALID');
+ }
+ // TODO check valid protocols?
+ this._data.protocol = data.protocol;
- get canCertify(){
- return this.checkKey('can_certify');
- }
+ if (typeof(data.owner_trust) !== 'string'){
+ return gpgme_error('KEY_INVALID');
+ }
+ // TODO check valid values?
+ this._data.owner_trust = data.owner_trust;
- get canAuthenticate(){
- return this.checkKey('can_authenticate');
- }
+ // TODO: what about origin ?
+ if (!Number.isInteger(data.last_update)){
+ return gpgme_error('KEY_INVALID');
+ }
+ this._data.last_update = data.last_update;
- get isQualified(){
- return this.checkKey('is_qualified');
- }
+ this._data.subkeys = [];
+ if (data.hasOwnProperty('subkeys')){
+ if (!Array.isArray(data.subkeys)){
+ return gpgme_error('KEY_INVALID');
+ }
+ for (let i=0; i< data.subkeys.length; i++) {
+ this._data.subkeys.push(
+ new GPGME_Subkey(data.subkeys[i]));
+ }
+ }
- get armored(){
- let msg = createMessage ('export_key');
- msg.setParameter('armor', true);
- if (msg instanceof Error){
- return gpgme_error('KEY_INVALID');
+ this._data.userids = [];
+ if (data.hasOwnProperty('userids')){
+ if (!Array.isArray(data.userids)){
+ return gpgme_error('KEY_INVALID');
+ }
+ for (let i=0; i< data.userids.length; i++) {
+ this._data.userids.push(
+ new GPGME_UserId(data.userids[i]));
+ }
}
- this.connection.post(msg).then(function(result){
- return result.data;
- });
- // TODO return value not yet checked. Should result in an armored block
- // in correct encoding
+ return this;
}
/**
- * TODO returns true if this is the default key used to sign
+ * Query any property of the Key list
+ * (TODO: armor not in here, might be unexpected)
+ * @param {String} property Key property to be retreived
+ * @param {*} cached (optional) if false, the data will be directly queried
+ * from gnupg.
+ * @returns {*|Promise<*>} the value, or if not cached, a Promise
+ * resolving on the value
*/
- get isDefault(){
- throw('NOT_YET_IMPLEMENTED');
+ get(property, cached=true) {
+ if (cached === false) {
+ let me = this;
+ return new Promise(function(resolve, reject) {
+ me.refreshKey().then(function(key){
+ resolve(key.get(property, true));
+ }, function(error){
+ reject(error);
+ });
+ });
+ } else {
+ if (!this._data.hasOwnProperty(property)){
+ return gpgme_error('PARAM_WRONG');
+ } else {
+ return (this._data[property]);
+ }
+ }
}
/**
- * get the Key's subkeys as GPGME_Key objects
- * @returns {Array}
+ * Reloads the Key from gnupg
*/
- get subkeys(){
- return this.checkKey('subkeys').then(function(result){
- // TBD expecting a list of fingerprints
- if (!Array.isArray(result)){
- result = [result];
+ refreshKey() {
+ let me = this;
+ return new Promise(function(resolve, reject) {
+ if (!me._data.fingerprint){
+ reject(gpgme_error('KEY_INVALID'));
}
- let resultset = [];
- for (let i=0; i < result.length; i++){
- let subkey = new GPGME_Key(result[i], this.connection);
- if (subkey instanceof GPGME_Key){
- resultset.push(subkey);
+ let msg = createMessage('keylist');
+ msg.setParameter('sigs', true);
+ msg.setParameter('keys', me._data.fingerprint);
+ me.connection.post(msg).then(function(result){
+ if (result.keys.length === 1){
+ me.setKeydata(result.keys[0]);
+ resolve(me);
+ } else {
+ reject(gpgme_error('KEY_NOKEY'));
}
- }
- return Promise.resolve(resultset);
- }, function(error){
- //TODO this.checkKey fails
+ }, function (error) {
+ reject(gpgme_error('GNUPG_ERROR'), error);
+ })
});
}
+ //TODO:
/**
- * creation time stamp of the key
- * @returns {Date|null} TBD
+ * Get the armored block of the non- secret parts of the Key.
+ * @returns {String} the armored Key block.
+ * Notice that this may be outdated cached info. Use the async getArmor if
+ * you need the most current info
*/
- get timestamp(){
- return this.checkKey('timestamp');
- //TODO GPGME: -1 if the timestamp is invalid, and 0 if it is not available.
- }
+ // get armor(){ TODO }
/**
- * The expiration timestamp of this key TBD
- * @returns {Date|null} TBD
+ * Query the armored block of the non- secret parts of the Key directly
+ * from gpg.
+ * Async, returns Promise
*/
- get expires(){
- return this.checkKey('expires');
- // TODO convert to Date; check for 0
+ // getArmor(){ TODO }
+ //
+
+ // get hasSecret(){TODO} // confusing difference to Key.get('secret')!
+ // getHasSecret(){TODO async version}
+}
+
+/**
+ * The subkeys of a Key. Currently, they cannot be refreshed separately
+ */
+class GPGME_Subkey {
+
+ constructor(data){
+ let keys = Object.keys(data);
+ for (let i=0; i< keys.length; i++) {
+ this.setProperty(keys[i], data[keys[i]]);
+ }
+ }
+
+ setProperty(property, value){
+ if (!this._data){
+ this._data = {};
+ }
+ if (validSubKeyProperties.hasOwnProperty(property)){
+ if (validSubKeyProperties[property](value) === true) {
+ this._data[property] = value;
+ }
+ }
}
/**
- * getter name TBD
- * @returns {String|Array} The user ids associated with this key
+ *
+ * @param {String} property Information to request
+ * @returns {String | Number}
+ * TODO: date properties are numbers with Date in seconds
*/
- get userIds(){
- return this.checkKey('uids');
+ get(property) {
+ if (this._data.hasOwnProperty(property)){
+ return (this._data[property]);
+ }
+ }
+}
+
+class GPGME_UserId {
+
+ constructor(data){
+ let keys = Object.keys(data);
+ for (let i=0; i< keys.length; i++) {
+ this.setProperty(keys[i], data[keys[i]]);
+ }
+ }
+
+ setProperty(property, value){
+ if (!this._data){
+ this._data = {};
+ }
+ if (validUserIdProperties.hasOwnProperty(property)){
+ if (validUserIdProperties[property](value) === true) {
+ this._data[property] = value;
+ }
+ }
}
/**
- * @returns {String} The public key algorithm supported by this subkey
+ *
+ * @param {String} property Information to request
+ * @returns {String | Number}
+ * TODO: date properties are numbers with Date in seconds
*/
- get pubkey_algo(){
- return this.checkKey('pubkey_algo');
+ get(property) {
+ if (this._data.hasOwnProperty(property)){
+ return (this._data[property]);
+ }
}
+}
- /**
- * generic function to query gnupg information on a key.
- * @param {*} property The gpgme-json property to check.
- * TODO: check if Promise.then(return)
- */
- checkKey(property){
- if (!this._fingerprint){
- return gpgme_error('KEY_INVALID');
+const validUserIdProperties = {
+ 'revoked': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'invalid': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'uid': function(value){
+ if (typeof(value) === 'string' || value === ''){
+ return true;;
}
- return gpgme_error('NOT_YET_IMPLEMENTED');
- // TODO: async is not what is to be ecpected from Key information :(
- if (!property || typeof(property) !== 'string' ||
- !permittedOperations['keyinfo'].hasOwnProperty(property)){
- return gpgme_error('PARAM_WRONG');
- }
- let msg = createMessage ('keyinfo');
- if (msg instanceof Error){
- return gpgme_error('PARAM_WRONG');
- }
- msg.setParameter('fingerprint', this.fingerprint);
- this.connection.post(msg).then(function(result, error){
- if (error){
- return gpgme_error('GNUPG_ERROR',error.msg);
- } else if (result.hasOwnProperty(property)){
- return result[property];
- }
- else if (property == 'secret'){
- // TBD property undefined means "not true" in case of secret?
- return false;
- } else {
- return gpgme_error('CONN_UNEXPECTED_ANSWER');
- }
- }, function(error){
- return gpgme_error('GENERIC_ERROR');
- });
+ return false;
+ },
+ 'validity': function(value){
+ if (typeof(value) === 'string'){
+ return true;;
+ }
+ return false;
+ },
+ 'name': function(value){
+ if (typeof(value) === 'string' || value === ''){
+ return true;;
+ }
+ return false;
+ },
+ 'email': function(value){
+ if (typeof(value) === 'string' || value === ''){
+ return true;;
+ }
+ return false;
+ },
+ 'address': function(value){
+ if (typeof(value) === 'string' || value === ''){
+ return true;;
+ }
+ return false;
+ },
+ 'comment': function(value){
+ if (typeof(value) === 'string' || value === ''){
+ return true;;
+ }
+ return false;
+ },
+ 'origin': function(value){
+ return Number.isInteger(value);
+ },
+ 'last_update': function(value){
+ return Number.isInteger(value);
}
-};
\ No newline at end of file
+};
+
+const validSubKeyProperties = {
+ 'invalid': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'can_encrypt': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'can_sign': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'can_certify': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'can_authenticate': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'secret': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'is_qualified': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'is_cardkey': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'is_de_vs': function(value){
+ return typeof(value) === 'boolean';
+ },
+ 'pubkey_algo_name': function(value){
+ return typeof(value) === 'string';
+ // TODO: check against list of known?['']
+ },
+ 'pubkey_algo_string': function(value){
+ return typeof(value) === 'string';
+ // TODO: check against list of known?['']
+ },
+ 'keyid': function(value){
+ return isLongId(value);
+ },
+ 'pubkey_algo': function(value) {
+ return (Number.isInteger(value) && value >= 0);
+ },
+ 'length': function(value){
+ return (Number.isInteger(value) && value > 0);
+ },
+ 'timestamp': function(value){
+ return (Number.isInteger(value) && value > 0);
+ },
+ 'expires': function(value){
+ return (Number.isInteger(value) && value > 0);
+ }
+}
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index aa02a8bc..42213ec3 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -1,259 +1,278 @@
/* 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
/**
* Definition of the possible interactions with gpgme-json.
* operation:
required: Array
name The name of the property
allowed: Array of allowed types. Currently accepted values:
['number', 'string', 'boolean', 'Uint8Array']
array_allowed: Boolean. If the value can be an array of the above
allowed_data: If present, restricts to the given value
optional: Array
see 'required', with these parameters not being mandatory for a
complete message
pinentry: boolean If a pinentry dialog is expected, and a timeout of
5000 ms would be too short
answer:
type: The payload property of the answer. May be
partial and in need of concatenation
params: Array Information that do not change throughout
the message
infos: Array<*> arbitrary information that may result in a list
}
}
*/
export const permittedOperations = {
encrypt: {
required: {
'keys': {
allowed: ['string'],
array_allowed: true
},
'data': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'chunksize': {
allowed: ['number']
},
'base64': {
allowed: ['boolean']
},
'mime': {
allowed: ['boolean']
},
'armor': {
allowed: ['boolean']
},
'always-trust': {
allowed: ['boolean']
},
'no-encrypt-to': {
allowed: ['string'],
array_allowed: true
},
'no-compress': {
allowed: ['boolean']
},
'throw-keyids': {
allowed: ['boolean']
},
'want-address': {
allowed: ['boolean']
},
'wrap': {
allowed: ['boolean']
},
},
answer: {
type: ['ciphertext'],
data: ['data'],
params: ['base64'],
infos: []
}
},
decrypt: {
pinentry: true,
required: {
'data': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'chunksize': {
allowed: ['number'],
},
'base64': {
allowed: ['boolean']
}
},
answer: {
type: ['plaintext'],
data: ['data'],
params: ['base64', 'mime'],
infos: [] // TODO pending. Info about signatures and validity
//{
//signatures: [{
//Key : Fingerprint,
//valid:
// }]
}
},
sign: {
pinentry: true,
required: {
'data': {
allowed: ['string']},
'keys': {
allowed: ['string'],
array_allowed: true
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'chunksize': {
allowed: ['number'],
},
'sender': {
allowed: ['string'],
},
'mode': {
allowed: ['string'],
allowed_data: ['detached', 'clearsign'] // TODO 'opaque' not used
},
'base64': {
allowed: ['boolean']
},
'armor': {
allowed: ['boolean']
},
},
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']
}
},
-
- /** TBD: querying the Key's information (keyinfo)
- TBD name: {
- required: {
- 'fingerprint': {
- allowed: ['string']
- },
- },
- answer: {
- type: ['TBD'],
- data: [],
- params: ['hasSecret','isRevoked','isExpired','armored',
- 'timestamp','expires','pubkey_algo'],
- infos: ['subkeys', 'userIds']
- // {'hasSecret': ,
- // 'isRevoked': ,
- // 'isExpired': ,
- // 'armored': , // armored public Key block
- // 'timestamp': , //
- // 'expires': ,
- // 'pubkey_algo': TBD // TBD (optional?),
- // 'userIds': Array,
- // 'subkeys': Array Fingerprints of Subkeys
- // }
- }*/
-
- /**
- listkeys:{
- required: {};
+ keylist:{
+ required: {},
optional: {
- 'with-secret':{
+ 'protocol': {
+ allowed: ['string'],
+ allowed_data: ['cms', 'openpgp']
+ },
+ 'chunksize': {
+ allowed: ['number'],
+ },
+ // note: For the meaning of the flags, refer to
+ // https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html
+ 'secret': {
allowed: ['boolean']
- },{
- 'pattern': {
- allowed: ['string']
+ },
+ 'extern': {
+ allowed: ['boolean']
+ },
+ 'local':{
+ allowed: ['boolean']
+ },
+ 'sigs':{
+ allowed: ['boolean']
+ },
+ 'notations':{
+ allowed: ['boolean']
+ },
+ 'tofu': {
+ allowed: ['boolean']
+ },
+ 'ephemeral': {
+ allowed: ['boolean']
+ },
+ 'validate': {
+ allowed: ['boolean']
+ },
+ // 'pattern': { TODO
+ // allowed: ['string']
+ // },
+ 'keys': {
+ allowed: ['string'],
+ array_allowed: true
}
},
- answer: {
- type: ['TBD'],
- infos: ['TBD']
- // keys: Array Fingerprints representing the results
+ answer: {
+ type: [],
+ data: [],
+ params: [],
+ infos: ['keys']
+ }
},
- */
/**
importkey: {
required: {
'keyarmored': {
allowed: ['string']
}
},
answer: {
type: ['TBD'],
infos: ['TBD'],
// for each key if import was a success,
// and if it was an update of preexisting key
}
},
*/
/**
deletekey: {
pinentry: true,
required: {
'fingerprint': {
allowed: ['string'],
// array_allowed: TBD Allow several Keys to be deleted at once?
},
optional: {
'TBD' //Flag to delete secret Key ?
}
answer: {
type ['TBD'],
infos: ['']
// TBD (optional) Some kind of 'ok' if delete was successful.
}
}
*/
/**
*TBD get armored secret different treatment from keyinfo!
* TBD key modification?
* encryptsign: TBD
*/
+
+ version: {
+ required: {},
+ optional: {},
+ answer: {
+ type: [''],
+ data: ['gpgme'],
+ infos: ['info'],
+ params:[]
+ }
+ }
}
diff --git a/lang/js/unittest_inputvalues.js b/lang/js/unittest_inputvalues.js
index 3450afd2..ca51f4ae 100644
--- a/lang/js/unittest_inputvalues.js
+++ b/lang/js/unittest_inputvalues.js
@@ -1,45 +1,51 @@
import {Connection} from "./src/Connection";
import {createKey} from "./src/Key";
let conn = new Connection;
export const helper_params = {
validLongId: '0A0A0A0A0A0A0A0A',
validKeys: ['A1E3BC45BDC8E87B74F4392D53B151A1368E50F3',
- createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn),
+ createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),
'EE17AEE730F88F1DE7713C54BBE0A4FF7851650A'],
validFingerprint: '9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
validFingerprints: ['9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A9A9A7A7A8A',
'9AAE7A338A9A9A7A7A8A9A9A7A7A8A9A9A7A7DDA'],
invalidLongId: '9A9A7A7A8A9A9A7A7A8A',
invalidFingerprints: [{hello:'World'}, ['kekekeke'], new Uint32Array(40)],
invalidKeyArray: {curiosity:'uncat'},
invalidKeyArray_OneBad: [
- createKey('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08', conn),
+ createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),
'E1D18E6E994FA9FE9360Bx0E687B940FEFEB095A',
'3AEA7FE4F5F416ED18CEC63DD519450D9C0FAEE5'],
invalidErrorCode: 'Please type in all your passwords.',
- validGPGME_Key: createKey('ADDBC303B6D31026F5EB4591A27EABDF283121BB', conn),
+ validGPGME_Key: createKey('D41735B91236FDB882048C5A2301635EEFF0CB05', conn),
valid_openpgplike: { primaryKey: {
getFingerprint: function(){
return '85DE2A8BA5A5AB3A8A7BE2000B8AED24D7534BC2';}
}
}
}
export const message_params = {
invalid_op_action : 'dance',
invalid_op_type : [234, 34, '<>'],
valid_encrypt_data: "مرحبا بالعالم",
invalid_param_test: {
valid_op: 'encrypt',
invalid_param_names: [22,'dance', {}],
validparam_name_0: 'mime',
invalid_values_0: [2134, 'All your passwords',
createKey('12AE9F3E41B33BF77DF52B6BE8EE1992D7909B08', conn), null]
}
}
export const whatever_params = {
- four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}']
+ four_invalid_params: ['<(((-<', '>°;==;~~', '^^', '{{{{o}}}}'],
+}
+export const key_params = {
+ validKeyFingerprint: 'D41735B91236FDB882048C5A2301635EEFF0CB05',
+ invalidKeyFingerprint: 'CDC3A2B2860625CCBFC5AAAAAC6D1B604967FC4A',
+ validKeyProperties: ['expired', 'disabled','invalid','can_encrypt',
+ 'can_sign','can_certify','can_authenticate','secret','is_qualified']
}
diff --git a/lang/js/unittests.js b/lang/js/unittests.js
index 06b2b23a..bb06309d 100644
--- a/lang/js/unittests.js
+++ b/lang/js/unittests.js
@@ -1,327 +1,378 @@
/* 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 .
* SPDX-License-Identifier: LGPL-2.1+
*/
import "./node_modules/mocha/mocha";
import "./node_modules/chai/chai";
import { helper_params as hp } from "./unittest_inputvalues";
import { message_params as mp } from "./unittest_inputvalues";
import { whatever_params as wp } from "./unittest_inputvalues";
+import { key_params as kp } from "./unittest_inputvalues";
import { Connection } from "./src/Connection";
import { gpgme_error } from "./src/Errors";
import { toKeyIdArray , isFingerprint } from "./src/Helpers";
import { GPGME_Key , createKey } from "./src/Key";
import { GPGME_Keyring } from "./src/Keyring";
import {GPGME_Message, createMessage} from "./src/Message";
import { setTimeout } from "timers";
mocha.setup('bdd');
var expect = chai.expect;
chai.config.includeStack = true;
function unittests (){
describe('Connection testing', function(){
it('Connecting', function(done) {
let conn0 = new Connection;
conn0.checkConnection().then(function(answer) {
expect(answer).to.not.be.empty;
expect(answer.gpgme).to.not.be.undefined;
expect(answer.gpgme).to.be.a('string');
expect(answer.info).to.be.an('Array');
expect(conn0.disconnect).to.be.a('function');
expect(conn0.post).to.be.a('function');
+ conn0.disconnect();
done();
});
});
it('Disconnecting', function(done) {
let conn0 = new Connection;
conn0.checkConnection(false).then(function(answer) {
expect(answer).to.be.true;
conn0.disconnect();
conn0.checkConnection(false).then(function(result) {
expect(result).to.be.false;
done();
});
});
});
});
describe('Error Object handling', function(){
-
+ // TODO: new GPGME_Error codes
it('check the Timeout error', function(){
let test0 = gpgme_error('CONN_TIMEOUT');
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('CONN_TIMEOUT');
});
it('Error Object returns generic code if code is not listed', function(){
let test0 = gpgme_error(hp.invalidErrorCode);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('GENERIC_ERROR');
});
it('Warnings like PARAM_IGNORED should not return errors', function(){
let test0 = gpgme_error('PARAM_IGNORED');
expect(test0).to.be.null;
});
});
describe('Fingerprint checking', function(){
it('isFingerprint(): valid Fingerprint', function(){
let test0 = isFingerprint(hp.validFingerprint);
expect(test0).to.be.true;
});
it('isFingerprint(): invalid Fingerprints', function(){
for (let i=0; i < hp.invalidFingerprints.length; i++){
let test0 = isFingerprint(hp.invalidFingerprints[i]);
expect(test0).to.be.false;
}
});
});
describe('toKeyIdArray() (converting input to fingerprint)', function(){
it('Correct fingerprint string', function(){
let test0 = toKeyIdArray(hp.validFingerprint);
expect(test0).to.be.an('array');
expect(test0).to.include(hp.validFingerprint);
});
it('correct GPGME_Key', function(){
expect(hp.validGPGME_Key).to.be.an.instanceof(GPGME_Key);
let test0 = toKeyIdArray(hp.validGPGME_Key);
expect(test0).to.be.an('array');
expect(test0).to.include(hp.validGPGME_Key.fingerprint);
});
it('openpgpjs-like object', function(){
let test0 = toKeyIdArray(hp.valid_openpgplike);
expect(test0).to.be.an('array').with.lengthOf(1);
expect(test0).to.include(
hp.valid_openpgplike.primaryKey.getFingerprint());
});
it('Array of valid inputs', function(){
let test0 = toKeyIdArray(hp.validKeys);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(hp.validKeys.length);
});
it('Incorrect inputs', function(){
it('valid Long ID', function(){
let test0 = toKeyIdArray(hp.validLongId);
expect(test0).to.be.empty;
});
it('invalidFingerprint', function(){
let test0 = toKeyIdArray(hp.invalidFingerprint);
expect(test0).to.be.empty;
});
it('invalidKeyArray', function(){
let test0 = toKeyIdArray(hp.invalidKeyArray);
expect(test0).to.be.empty;
});
it('Partially invalid array', function(){
let test0 = toKeyIdArray(hp.invalidKeyArray_OneBad);
expect(test0).to.be.an('array');
expect(test0).to.have.lengthOf(
hp.invalidKeyArray_OneBad.length - 1);
});
});
});
describe('GPGME_Key', function(){
it('correct Key initialization', function(){
let conn = new Connection;
- let key = createKey(hp.validFingerprint, conn);
-
+ let key = createKey(kp.validKeyFingerprint, conn);
expect(key).to.be.an.instanceof(GPGME_Key);
expect(key.connection).to.be.an.instanceof(Connection);
- // TODO not implemented yet: Further Key functionality
+ conn.disconnect();
+ });
+ it('Key has data after a first refresh', function(done) {
+ let conn = new Connection;
+ let key = createKey(kp.validKeyFingerprint, conn);
+ key.refreshKey().then(function(key2){
+ expect(key2).to.be.an.instanceof(GPGME_Key);
+ expect(key2.get).to.be.a('function');
+ for (let i=0; i < kp.validKeyProperties.length; i++) {
+ let prop = key2.get(kp.validKeyProperties[i]);
+ expect(prop).to.not.be.undefined;
+ expect(prop).to.be.a('boolean');
+ }
+ expect(isFingerprint(key2.get('fingerprint'))).to.be.true;
+ expect(
+ key2.get('fingerprint')).to.equal(kp.validKeyFingerprint);
+ expect(
+ key2.get('fingerprint')).to.equal(key.fingerprint);
+ conn.disconnect();
+ done();
+ });
});
+ it('Non-cached key async data retrieval', function (done){
+ let conn = new Connection;
+ let key = createKey(kp.validKeyFingerprint, conn);
+ key.get('can_authenticate',false).then(function(result){
+ expect(result).to.be.a('boolean');
+ conn.disconnect();
+ done();
+ });
+ })
+
+ it('Querying non-existing Key returns an error', function(done) {
+ let conn = new Connection;
+ let key = createKey(kp.invalidKeyFingerprint, conn);
+ key.refreshKey().then(function(){},
+ function(error){
+ expect(error).to.be.an.instanceof(Error);
+ expect(error.code).to.equal('KEY_NOKEY');
+ conn.disconnect();
+ done();
+ });
+ });
+
+
it('Key can use the connection', function(done){
let conn = new Connection;
let key = createKey(hp.validFingerprint, conn);
key.connection.checkConnection(false).then(function(result){
expect(result).to.be.true;
key.connection.disconnect();
key.connection.checkConnection(false).then(function(result2){
expect(result2).to.be.false;
+ conn.disconnect();
done();
});
});
});
it('createKey returns error if parameters are wrong', function(){
let conn = new Connection;
for (let i=0; i< 4; i++){
let key0 = createKey(wp.four_invalid_params[i], conn);
expect(key0).to.be.an.instanceof(Error);
expect(key0.code).to.equal('PARAM_WRONG');
}
for (let i=0; i< 4; i++){
let key0 = createKey(
hp.validFingerprint, wp.four_invalid_params[i]);
expect(key0).to.be.an.instanceof(Error);
expect(key0.code).to.equal('PARAM_WRONG');
}
+ conn.disconnect();
});
- it('bad GPGME_Key returns Error if used', function(){
+
+ it('malformed GPGME_Key cannot be used', function(){
let conn = new Connection;
for (let i=0; i < 4; i++){
let key = new GPGME_Key(wp.four_invalid_params[i], conn);
-
- expect(key.connection).to.be.an.instanceof(Error);
- expect(key.connection.code).to.equal('KEY_INVALID');
+ expect(key.fingerprint).to.be.an.instanceof(Error);
+ expect(key.fingerprint.code).to.equal('KEY_INVALID');
}
+ conn.disconnect();
});
+
+ // TODO: tests for subkeys
+ // TODO: tests for userids
+ // TODO: some invalid tests for key/keyring
});
describe('GPGME_Keyring', function(){
it('correct initialization', function(){
let conn = new Connection;
let keyring = new GPGME_Keyring(conn);
expect(keyring).to.be.an.instanceof(GPGME_Keyring);
expect(keyring.connection).to.be.an.instanceof(Connection);
expect(keyring.getKeys).to.be.a('function');
expect(keyring.getSubset).to.be.a('function');
});
it('Keyring should return errors if not connected', function(){
let keyring = new GPGME_Keyring;
expect(keyring).to.be.an.instanceof(GPGME_Keyring);
expect(keyring.connection).to.be.an.instanceof(Error);
expect(keyring.connection.code).to.equal('CONN_NO_CONNECT');
// not yet implemented:
// keyring.getKeys().then(
// function(result){},
//function(reject){
// expect(reject).to.be.an.instanceof(Error);
// done();
});
//TODO not yet implemented:
// getKeys(pattern, include_secret) //note: pattern can be null
// getSubset(flags, pattern)
// available Boolean flags: secret revoked expired
});
describe('GPGME_Message', function(){
it('creating encrypt Message', function(){
let test0 = createMessage('encrypt');
expect(test0).to.be.an.instanceof(GPGME_Message);
expect(test0.isComplete).to.be.false;
});
it('Message is complete after setting mandatory data', function(){
let test0 = createMessage('encrypt');
test0.setParameter('data', mp.valid_encrypt_data);
test0.setParameter('keys', hp.validFingerprints);
expect(test0.isComplete).to.be.true;
});
it('Message is not complete after mandatory data is empty', function(){
let test0 = createMessage('encrypt');
test0.setParameter('data', '');
test0.setParameter('keys', hp.validFingerprints);
expect(test0.isComplete).to.be.false;
});
it('Complete Message contains the data that was set', function(){
let test0 = createMessage('encrypt');
test0.setParameter('data', mp.valid_encrypt_data);
test0.setParameter('keys', hp.validFingerprints);
expect(test0.message).to.not.be.null;
expect(test0.message).to.have.keys('op', 'data', 'keys');
expect(test0.message.op).to.equal('encrypt');
expect(test0.message.data).to.equal(
mp.valid_encrypt_data);
});
it ('Not accepting non-allowed operation', function(){
let test0 = createMessage(mp.invalid_op_action);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('MSG_WRONG_OP');
});
it('Not accepting wrong parameter type', function(){
let test0 = createMessage(mp.invalid_op_type);
expect(test0).to.be.an.instanceof(Error);
expect(test0.code).to.equal('PARAM_WRONG');
});
it('Not accepting wrong parameter name', function(){
let test0 = createMessage(mp.invalid_param_test.valid_op);
for (let i=0;
i < mp.invalid_param_test.invalid_param_names.length; i++){
let ret = test0.setParameter(
mp.invalid_param_test.invalid_param_names[i],
'Somevalue');
expect(ret).to.be.an.instanceof(Error);
expect(ret.code).to.equal('PARAM_WRONG');
}
});
it('Not accepting wrong parameter value', function(){
let test0 = createMessage(mp.invalid_param_test.valid_op);
for (let j=0;
j < mp.invalid_param_test.invalid_values_0.length; j++){
let ret = test0.setParameter(
mp.invalid_param_test.validparam_name_0,
mp.invalid_param_test.invalid_values_0[j]);
expect(ret).to.be.an.instanceof(Error);
expect(ret.code).to.equal('PARAM_WRONG');
}
});
});
}
export default {unittests};
\ No newline at end of file