js: revert changes to class read/write restriction

--

* undoes 94ee0988d4 and
  e16a87e839.
  I do not fully understand why my approach was bad, but I am not in
  a position to argue. This revert was requested to me after a review,
  and I'm doing it in the assumption that more experienced people know
  better than me.
* unittests: Also changed some outdated tests that stopped working
  since 754e799d35 (as GPGME_Key is not
  exported, one cannot check for instanceof in the tests anymore)
This commit is contained in:
Maximilian Krambach 2018-08-20 12:12:43 +02:00
parent 74684f24c6
commit 1954d27be8
9 changed files with 1203 additions and 1294 deletions

View File

@ -39,18 +39,18 @@ import { decode } from './Helpers';
export class Connection{ export class Connection{
constructor(){ constructor(){
let _connection = chrome.runtime.connectNative('gpgmejson'); this._connection = chrome.runtime.connectNative('gpgmejson');
}
/** /**
* Immediately closes an open port. * Immediately closes an open port.
*/ */
this.disconnect = function () { disconnect() {
if (_connection){ if (this._connection){
_connection.disconnect(); this._connection.disconnect();
_connection = null; this._connection = null;
}
} }
};
/** /**
@ -75,7 +75,7 @@ export class Connection{
* backend * backend
* @async * @async
*/ */
this.checkConnection = function(details = true){ checkConnection (details = true){
const msg = createMessage('version'); const msg = createMessage('version');
if (details === true) { if (details === true) {
return this.post(msg); return this.post(msg);
@ -96,7 +96,7 @@ export class Connection{
}); });
}); });
} }
}; }
/** /**
* Sends a {@link GPGME_Message} via tghe nativeMessaging port. It * Sends a {@link GPGME_Message} via tghe nativeMessaging port. It
@ -107,7 +107,7 @@ export class Connection{
* @returns {Promise<Object>} The collected answer * @returns {Promise<Object>} The collected answer
* @async * @async
*/ */
this.post = function (message){ post(message){
if (!message || !(message instanceof GPGME_Message)){ if (!message || !(message instanceof GPGME_Message)){
this.disconnect(); this.disconnect();
return Promise.reject(gpgme_error( return Promise.reject(gpgme_error(
@ -118,28 +118,29 @@ export class Connection{
return Promise.reject(gpgme_error('MSG_INCOMPLETE')); return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
} }
let chunksize = message.chunksize; let chunksize = message.chunksize;
const me = this;
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
let answer = Object.freeze(new Answer(message)); let answer = new Answer(message);
let listener = function(msg) { let listener = function(msg) {
if (!msg){ if (!msg){
_connection.onMessage.removeListener(listener); me._connection.onMessage.removeListener(listener);
_connection.disconnect(); me._connection.disconnect();
reject(gpgme_error('CONN_EMPTY_GPG_ANSWER')); reject(gpgme_error('CONN_EMPTY_GPG_ANSWER'));
} else { } else {
let answer_result = answer.collect(msg); let answer_result = answer.collect(msg);
if (answer_result !== true){ if (answer_result !== true){
_connection.onMessage.removeListener(listener); me._connection.onMessage.removeListener(listener);
_connection.disconnect(); me._connection.disconnect();
reject(answer_result); reject(answer_result);
} else { } else {
if (msg.more === true){ if (msg.more === true){
_connection.postMessage({ me._connection.postMessage({
'op': 'getmore', 'op': 'getmore',
'chunksize': chunksize 'chunksize': chunksize
}); });
} else { } else {
_connection.onMessage.removeListener(listener); me._connection.onMessage.removeListener(listener);
_connection.disconnect(); me._connection.disconnect();
const message = answer.getMessage(); const message = answer.getMessage();
if (message instanceof Error){ if (message instanceof Error){
reject(message); reject(message);
@ -150,22 +151,23 @@ export class Connection{
} }
} }
}; };
_connection.onMessage.addListener(listener); me._connection.onMessage.addListener(listener);
if (permittedOperations[message.operation].pinentry){ if (permittedOperations[message.operation].pinentry){
return _connection.postMessage(message.message); return me._connection.postMessage(message.message);
} else { } else {
return Promise.race([ return Promise.race([
_connection.postMessage(message.message), me._connection.postMessage(message.message),
function(resolve, reject){ function(resolve, reject){
setTimeout(function(){ setTimeout(function(){
_connection.disconnect(); me._connection.disconnect();
reject(gpgme_error('CONN_TIMEOUT')); reject(gpgme_error('CONN_TIMEOUT'));
}, 5000); }, 5000);
}]).then(function(result){ }
]).then(function(result){
return result; return result;
}, function(reject){ }, function(reject){
if(!(reject instanceof Error)) { if(!(reject instanceof Error)) {
_connection.disconnect(); me._connection.disconnect();
return gpgme_error('GNUPG_ERROR', reject); return gpgme_error('GNUPG_ERROR', reject);
} else { } else {
return reject; return reject;
@ -173,10 +175,10 @@ export class Connection{
}); });
} }
}); });
};
} }
} }
/** /**
* A class for answer objects, checking and processing the return messages of * A class for answer objects, checking and processing the return messages of
* the nativeMessaging communication. * the nativeMessaging communication.
@ -188,17 +190,18 @@ class Answer{
* @param {GPGME_Message} message * @param {GPGME_Message} message
*/ */
constructor(message){ constructor(message){
const operation = message.operation; this._operation = message.operation;
const expected = message.getExpect(); this._expected = message.expected;
let response_b64 = null; this._response_b64 = null;
}
this.getOperation = function(){ get operation (){
return operation; return this._operation;
}; }
this.getExpect = function(){ get expected (){
return expected; return this._expected;
}; }
/** /**
* Adds incoming base64 encoded data to the existing response * Adds incoming base64 encoded data to the existing response
@ -207,30 +210,30 @@ class Answer{
* *
* @private * @private
*/ */
this.collect = function (msg){ collect (msg){
if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) { if (typeof(msg) !== 'object' || !msg.hasOwnProperty('response')) {
return gpgme_error('CONN_UNEXPECTED_ANSWER'); return gpgme_error('CONN_UNEXPECTED_ANSWER');
} }
if (response_b64 === null){ if (!this._response_b64){
response_b64 = msg.response; this._response_b64 = msg.response;
return true; return true;
} else { } else {
response_b64 += msg.response; this._response_b64 += msg.response;
return true; return true;
} }
}; }
/** /**
* Returns the base64 encoded answer data with the content verified * Returns the base64 encoded answer data with the content verified
* against {@link permittedOperations}. * against {@link permittedOperations}.
*/ */
this.getMessage = function (){ getMessage(){
if (response_b64 === undefined){ if (this._response_b64 === null){
return gpgme_error('CONN_UNEXPECTED_ANSWER'); return gpgme_error('CONN_UNEXPECTED_ANSWER');
} }
let _decodedResponse = JSON.parse(atob(response_b64)); let _decodedResponse = JSON.parse(atob(this._response_b64));
let _response = {}; let _response = {};
let messageKeys = Object.keys(_decodedResponse); let messageKeys = Object.keys(_decodedResponse);
let poa = permittedOperations[this.getOperation()].answer; let poa = permittedOperations[this.operation].answer;
if (messageKeys.length === 0){ if (messageKeys.length === 0){
return gpgme_error('CONN_UNEXPECTED_ANSWER'); return gpgme_error('CONN_UNEXPECTED_ANSWER');
} }
@ -249,8 +252,7 @@ class Answer{
break; break;
case 'msg': case 'msg':
if (_decodedResponse.type === 'error'){ if (_decodedResponse.type === 'error'){
return (gpgme_error('GNUPG_ERROR', return (gpgme_error('GNUPG_ERROR', _decodedResponse.msg));
_decodedResponse.msg));
} }
break; break;
default: default:
@ -262,7 +264,7 @@ class Answer{
} }
if (_decodedResponse.base64 === true if (_decodedResponse.base64 === true
&& poa.data[key] === 'string' && poa.data[key] === 'string'
&& this.getExpect() !== 'base64' && this.expected !== 'base64'
){ ){
_response[key] = decodeURIComponent( _response[key] = decodeURIComponent(
atob(_decodedResponse[key]).split('').map( atob(_decodedResponse[key]).split('').map(
@ -277,6 +279,5 @@ class Answer{
} }
} }
return _response; return _response;
};
} }
} }

View File

@ -123,7 +123,7 @@ const err_list = {
export function gpgme_error(code = 'GENERIC_ERROR', info){ export function gpgme_error(code = 'GENERIC_ERROR', info){
if (err_list.hasOwnProperty(code)){ if (err_list.hasOwnProperty(code)){
if (err_list[code].type === 'error'){ if (err_list[code].type === 'error'){
return Object.freeze(new GPGME_Error(code)); return new GPGME_Error(code);
} }
if (err_list[code].type === 'warning'){ if (err_list[code].type === 'warning'){
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -131,10 +131,10 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
} }
return null; return null;
} else if (code === 'GNUPG_ERROR'){ } else if (code === 'GNUPG_ERROR'){
return Object.freeze(new GPGME_Error(code, info)); return new GPGME_Error(code, info);
} }
else { else {
return Object.freeze(new GPGME_Error('GENERIC_ERROR')); return new GPGME_Error('GENERIC_ERROR');
} }
} }
@ -148,6 +148,7 @@ export function gpgme_error(code = 'GENERIC_ERROR', info){
*/ */
class GPGME_Error extends Error{ class GPGME_Error extends Error{
constructor(code = 'GENERIC_ERROR', msg=''){ constructor(code = 'GENERIC_ERROR', msg=''){
if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){ if (code === 'GNUPG_ERROR' && typeof(msg) === 'string'){
super(msg); super(msg);
} else if (err_list.hasOwnProperty(code)){ } else if (err_list.hasOwnProperty(code)){
@ -159,12 +160,10 @@ class GPGME_Error extends Error{
} else { } else {
super(err_list['GENERIC_ERROR'].msg); super(err_list['GENERIC_ERROR'].msg);
} }
this.getCode = function(){ this._code = code;
return code;
};
} }
get code(){ get code(){
return this.getCode(); return this._code;
} }
} }

View File

@ -40,12 +40,12 @@ export function createKey(fingerprint, async = false, data){
return gpgme_error('PARAM_WRONG'); return gpgme_error('PARAM_WRONG');
} }
if (data !== undefined){ if (data !== undefined){
data = validateKeyData(data); data = validateKeyData(fingerprint, data);
} }
if (data instanceof Error){ if (data instanceof Error){
return gpgme_error('KEY_INVALID'); return gpgme_error('KEY_INVALID');
} else { } else {
return Object.freeze(new GPGME_Key(fingerprint, async, data)); return new GPGME_Key(fingerprint, async, data);
} }
} }
@ -65,20 +65,15 @@ class GPGME_Key {
/** /**
* @property {Boolean} If true, most answers will be asynchronous * @property {Boolean} If true, most answers will be asynchronous
*/ */
this.isAsync = async; this._async = async;
let _data = {fingerprint: fingerprint.toUpperCase()}; this._data = {fingerprint: fingerprint.toUpperCase()};
if (data !== undefined if (data !== undefined
&& data.fingerprint.toUpperCase() === _data.fingerprint && data.fingerprint.toUpperCase() === this._data.fingerprint
) { ) {
_data = data; this._data = data;
} }
this.getFingerprint = function(){
if (!_data.fingerprint || !isFingerprint(_data.fingerprint)){
return gpgme_error('KEY_INVALID');
} }
return _data.fingerprint;
};
/** /**
* Query any property of the Key listed in {@link validKeyProperties} * Query any property of the Key listed in {@link validKeyProperties}
@ -89,15 +84,15 @@ class GPGME_Key {
* async, the armored property is not available (it can still be * async, the armored property is not available (it can still be
* retrieved asynchronously by {@link Key.getArmor}) * retrieved asynchronously by {@link Key.getArmor})
*/ */
this.get = function(property) { get(property) {
if (this.isAsync === true) { if (this._async === true) {
switch (property){ switch (property){
case 'armored': case 'armored':
return this.getArmor(); return this.getArmor();
case 'hasSecret': case 'hasSecret':
return this.getGnupgSecretState(); return this.getGnupgSecretState();
default: default:
return getGnupgState(property); return getGnupgState(this.fingerprint, property);
} }
} else { } else {
if (property === 'armored') { if (property === 'armored') {
@ -106,10 +101,10 @@ class GPGME_Key {
if (!validKeyProperties.hasOwnProperty(property)){ if (!validKeyProperties.hasOwnProperty(property)){
return gpgme_error('PARAM_WRONG'); return gpgme_error('PARAM_WRONG');
} else { } else {
return (_data[property]); return (this._data[property]);
}
} }
} }
};
/** /**
* Reloads the Key information from gnupg. This is only useful if you * Reloads the Key information from gnupg. This is only useful if you
@ -119,23 +114,23 @@ class GPGME_Key {
* @returns {Promise<GPGME_Key|GPGME_Error>} * @returns {Promise<GPGME_Key|GPGME_Error>}
* @async * @async
*/ */
this.refreshKey = function() { refreshKey() {
let me = this; let me = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!_data.fingerprint){ if (!me._data.fingerprint){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} }
let msg = createMessage('keylist'); let msg = createMessage('keylist');
msg.setParameter('sigs', true); msg.setParameter('sigs', true);
msg.setParameter('keys', _data.fingerprint); msg.setParameter('keys', me._data.fingerprint);
msg.post().then(function(result){ msg.post().then(function(result){
if (result.keys.length === 1){ if (result.keys.length === 1){
const newdata = validateKeyData( const newdata = validateKeyData(
_data.fingerprint, result.keys[0]); me._data.fingerprint, result.keys[0]);
if (newdata instanceof Error){ if (newdata instanceof Error){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} else { } else {
_data = newdata; me._data = newdata;
me.getGnupgSecretState().then(function(){ me.getGnupgSecretState().then(function(){
me.getArmor().then(function(){ me.getArmor().then(function(){
resolve(me); resolve(me);
@ -153,7 +148,7 @@ class GPGME_Key {
reject(gpgme_error('GNUPG_ERROR'), error); reject(gpgme_error('GNUPG_ERROR'), error);
}); });
}); });
}; }
/** /**
* Query the armored block of the Key directly from gnupg. Please note * Query the armored block of the Key directly from gnupg. Please note
@ -162,21 +157,22 @@ class GPGME_Key {
* @returns {Promise<String|GPGME_Error>} * @returns {Promise<String|GPGME_Error>}
* @async * @async
*/ */
this.getArmor = function(){ getArmor() {
const me = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!_data.fingerprint){ if (!me._data.fingerprint){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} }
let msg = createMessage('export'); let msg = createMessage('export');
msg.setParameter('armor', true); msg.setParameter('armor', true);
msg.setParameter('keys', _data.fingerprint); msg.setParameter('keys', me._data.fingerprint);
msg.post().then(function(result){ msg.post().then(function(result){
resolve(result.data); resolve(result.data);
}, function(error){ }, function(error){
reject(error); reject(error);
}); });
}); });
}; }
/** /**
* Find out if the Key is part of a Key pair including public and * Find out if the Key is part of a Key pair including public and
@ -188,25 +184,26 @@ class GPGME_Key {
* available in the gnupg Keyring. * available in the gnupg Keyring.
* @async * @async
*/ */
this.getGnupgSecretState = function (){ getGnupgSecretState (){
const me = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!_data.fingerprint){ if (!me._data.fingerprint){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} else { } else {
let msg = createMessage('keylist'); let msg = createMessage('keylist');
msg.setParameter('keys', _data.fingerprint); msg.setParameter('keys', me._data.fingerprint);
msg.setParameter('secret', true); msg.setParameter('secret', true);
msg.post().then(function(result){ msg.post().then(function(result){
_data.hasSecret = null; me._data.hasSecret = null;
if ( if (
result.keys && result.keys &&
result.keys.length === 1 && result.keys.length === 1 &&
result.keys[0].secret === true result.keys[0].secret === true
) { ) {
_data.hasSecret = true; me._data.hasSecret = true;
resolve(true); resolve(true);
} else { } else {
_data.hasSecret = false; me._data.hasSecret = false;
resolve(false); resolve(false);
} }
}, function(error){ }, function(error){
@ -214,7 +211,7 @@ class GPGME_Key {
}); });
} }
}); });
}; }
/** /**
* Deletes the (public) Key from the GPG Keyring. Note that a deletion * Deletes the (public) Key from the GPG Keyring. Note that a deletion
@ -222,27 +219,27 @@ class GPGME_Key {
* @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted, * @returns {Promise<Boolean|GPGME_Error>} Success if key was deleted,
* rejects with a GPG error otherwise. * rejects with a GPG error otherwise.
*/ */
this.delete= function (){ delete(){
const me = this;
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
if (!_data.fingerprint){ if (!me._data.fingerprint){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} }
let msg = createMessage('delete'); let msg = createMessage('delete');
msg.setParameter('key', _data.fingerprint); msg.setParameter('key', me._data.fingerprint);
msg.post().then(function(result){ msg.post().then(function(result){
resolve(result.success); resolve(result.success);
}, function(error){ }, function(error){
reject(error); reject(error);
}); });
}); });
};
} }
/** /**
* @returns {String} The fingerprint defining this Key. Convenience getter * @returns {String} The fingerprint defining this Key. Convenience getter
*/ */
get fingerprint(){ get fingerprint(){
return this.getFingerprint(); return this._data.fingerprint;
} }
} }
@ -259,8 +256,9 @@ class GPGME_Subkey {
* @private * @private
*/ */
constructor(data){ constructor(data){
let _data = {}; this._data = {};
let keys = Object.keys(data); let keys = Object.keys(data);
const me = this;
/** /**
* Validates a subkey property against {@link validSubKeyProperties} and * Validates a subkey property against {@link validSubKeyProperties} and
@ -273,9 +271,9 @@ class GPGME_Subkey {
if (validSubKeyProperties.hasOwnProperty(property)){ if (validSubKeyProperties.hasOwnProperty(property)){
if (validSubKeyProperties[property](value) === true) { if (validSubKeyProperties[property](value) === true) {
if (property === 'timestamp' || property === 'expires'){ if (property === 'timestamp' || property === 'expires'){
_data[property] = new Date(value * 1000); me._data[property] = new Date(value * 1000);
} else { } else {
_data[property] = value; me._data[property] = value;
} }
} }
} }
@ -283,18 +281,19 @@ class GPGME_Subkey {
for (let i=0; i< keys.length; i++) { for (let i=0; i< keys.length; i++) {
setProperty(keys[i], data[keys[i]]); setProperty(keys[i], data[keys[i]]);
} }
}
/** /**
* Fetches any information about this subkey * Fetches any information about this subkey
* @param {String} property Information to request * @param {String} property Information to request
* @returns {String | Number | Date} * @returns {String | Number | Date}
*/ */
this.get = function(property) { get(property) {
if (_data.hasOwnProperty(property)){ if (this._data.hasOwnProperty(property)){
return (_data[property]); return (this._data[property]);
} }
};
} }
} }
/** /**
@ -310,15 +309,16 @@ class GPGME_UserId {
* @private * @private
*/ */
constructor(data){ constructor(data){
let _data = {}; this._data = {};
const me = this;
let keys = Object.keys(data); let keys = Object.keys(data);
const setProperty = function(property, value){ const setProperty = function(property, value){
if (validUserIdProperties.hasOwnProperty(property)){ if (validUserIdProperties.hasOwnProperty(property)){
if (validUserIdProperties[property](value) === true) { if (validUserIdProperties[property](value) === true) {
if (property === 'last_update'){ if (property === 'last_update'){
_data[property] = new Date(value*1000); me._data[property] = new Date(value*1000);
} else { } else {
_data[property] = value; me._data[property] = value;
} }
} }
} }
@ -326,18 +326,19 @@ class GPGME_UserId {
for (let i=0; i< keys.length; i++) { for (let i=0; i< keys.length; i++) {
setProperty(keys[i], data[keys[i]]); setProperty(keys[i], data[keys[i]]);
} }
}
/** /**
* Fetches information about the user * Fetches information about the user
* @param {String} property Information to request * @param {String} property Information to request
* @returns {String | Number} * @returns {String | Number}
*/ */
this.get = function (property) { get(property) {
if (_data.hasOwnProperty(property)){ if (this._data.hasOwnProperty(property)){
return (_data[property]); return (this._data[property]);
} }
};
} }
} }
/** /**
@ -569,10 +570,11 @@ const validKeyProperties = {
* an error if something went wrong. * an error if something went wrong.
* @private * @private
*/ */
function validateKeyData(data){ function validateKeyData(fingerprint, data){
const key = {}; const key = {};
if ( typeof(data) !== 'object' if (!fingerprint || typeof(data) !== 'object' || !data.fingerprint
|| !data.fingerprint){ || fingerprint !== data.fingerprint.toUpperCase()
){
return gpgme_error('KEY_INVALID'); return gpgme_error('KEY_INVALID');
} }
let props = Object.keys(data); let props = Object.keys(data);
@ -588,15 +590,15 @@ function validateKeyData(data){
case 'subkeys': case 'subkeys':
key.subkeys = []; key.subkeys = [];
for (let i=0; i< data.subkeys.length; i++) { for (let i=0; i< data.subkeys.length; i++) {
key.subkeys.push(Object.freeze( key.subkeys.push(
new GPGME_Subkey(data.subkeys[i]))); new GPGME_Subkey(data.subkeys[i]));
} }
break; break;
case 'userids': case 'userids':
key.userids = []; key.userids = [];
for (let i=0; i< data.userids.length; i++) { for (let i=0; i< data.userids.length; i++) {
key.userids.push(Object.freeze( key.userids.push(
new GPGME_UserId(data.userids[i]))); new GPGME_UserId(data.userids[i]));
} }
break; break;
case 'last_update': case 'last_update':
@ -623,19 +625,19 @@ function getGnupgState (fingerprint, property){
} else { } else {
let msg = createMessage('keylist'); let msg = createMessage('keylist');
msg.setParameter('keys', fingerprint); msg.setParameter('keys', fingerprint);
msg.post().then(function(result){ msg.post().then(function(res){
if (!result.keys || result.keys.length !== 1){ if (!res.keys || res.keys.length !== 1){
reject(gpgme_error('KEY_INVALID')); reject(gpgme_error('KEY_INVALID'));
} else { } else {
const key = result.keys[0]; const key = res.keys[0];
let result; let result;
switch (property){ switch (property){
case 'subkeys': case 'subkeys':
result = []; result = [];
if (key.subkeys.length){ if (key.subkeys.length){
for (let i=0; i < key.subkeys.length; i++) { for (let i=0; i < key.subkeys.length; i++) {
result.push(Object.freeze( result.push(
new GPGME_Subkey(key.subkeys[i]))); new GPGME_Subkey(key.subkeys[i]));
} }
} }
resolve(result); resolve(result);
@ -644,8 +646,8 @@ function getGnupgState (fingerprint, property){
result = []; result = [];
if (key.userids.length){ if (key.userids.length){
for (let i=0; i< key.userids.length; i++) { for (let i=0; i< key.userids.length; i++) {
result.push(Object.freeze( result.push(
new GPGME_UserId(key.userids[i]))); new GPGME_UserId(key.userids[i]));
} }
} }
resolve(result); resolve(result);

View File

@ -32,6 +32,7 @@ import { gpgme_error } from './Errors';
*/ */
export class GPGME_Keyring { export class GPGME_Keyring {
constructor(){ constructor(){
}
/** /**
* Queries Keys (all Keys or a subset) from gnupg. * Queries Keys (all Keys or a subset) from gnupg.
@ -49,7 +50,7 @@ export class GPGME_Keyring {
* @static * @static
* @async * @async
*/ */
this.getKeys = function(pattern, prepare_sync=false, search=false){ getKeys (pattern, prepare_sync=false, search=false){
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let msg = createMessage('keylist'); let msg = createMessage('keylist');
if (pattern !== undefined && pattern !== null){ if (pattern !== undefined && pattern !== null){
@ -109,7 +110,7 @@ export class GPGME_Keyring {
} }
}); });
}); });
}; }
/** /**
* @typedef {Object} exportResult The result of a getKeysArmored * @typedef {Object} exportResult The result of a getKeysArmored
@ -134,7 +135,7 @@ export class GPGME_Keyring {
* @static * @static
* @async * @async
*/ */
this.getKeysArmored = function(pattern, with_secret_fpr) { getKeysArmored (pattern, with_secret_fpr) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let msg = createMessage('export'); let msg = createMessage('export');
msg.setParameter('armor', true); msg.setParameter('armor', true);
@ -156,7 +157,7 @@ export class GPGME_Keyring {
reject(error); reject(error);
}); });
}); });
}; }
/** /**
* Returns the Key used by default in gnupg. * Returns the Key used by default in gnupg.
@ -168,7 +169,7 @@ export class GPGME_Keyring {
* @async * @async
* @static * @static
*/ */
this.getDefaultKey = function(prepare_sync = false) { getDefaultKey(prepare_sync = false) {
let me = this; let me = this;
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
let msg = createMessage('config_opt'); let msg = createMessage('config_opt');
@ -218,7 +219,7 @@ export class GPGME_Keyring {
reject(error); reject(error);
}); });
}); });
}; }
/** /**
* @typedef {Object} importResult The result of a Key update * @typedef {Object} importResult The result of a Key update
@ -257,7 +258,7 @@ export class GPGME_Keyring {
* @async * @async
* @static * @static
*/ */
this.importKey = function (armored, prepare_sync) { importKey (armored, prepare_sync) {
let feedbackValues = ['considered', 'no_user_id', 'imported', let feedbackValues = ['considered', 'no_user_id', 'imported',
'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys', 'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
'new_signatures', 'new_revocations', 'secret_read', 'new_signatures', 'new_revocations', 'secret_read',
@ -340,7 +341,7 @@ export class GPGME_Keyring {
}); });
}; }
/** /**
* Convenience function for deleting a Key. See {@link Key.delete} for * Convenience function for deleting a Key. See {@link Key.delete} for
@ -350,14 +351,14 @@ export class GPGME_Keyring {
* @async * @async
* @static * @static
*/ */
this.deleteKey = function(fingerprint){ deleteKey(fingerprint){
if (isFingerprint(fingerprint) === true) { if (isFingerprint(fingerprint) === true) {
let key = createKey(fingerprint); let key = createKey(fingerprint);
return key.delete(); return key.delete();
} else { } else {
return Promise.reject(gpgme_error('KEY_INVALID')); return Promise.reject(gpgme_error('KEY_INVALID'));
} }
}; }
/** /**
* Generates a new Key pair directly in gpg, and returns a GPGME_Key * Generates a new Key pair directly in gpg, and returns a GPGME_Key
@ -374,7 +375,7 @@ export class GPGME_Keyring {
* @return {Promise<Key|GPGME_Error>} * @return {Promise<Key|GPGME_Error>}
* @async * @async
*/ */
this.generateKey = function (userId, algo = 'default', expires){ generateKey(userId, algo = 'default', expires){
if ( if (
typeof(userId) !== 'string' || typeof(userId) !== 'string' ||
supportedKeyAlgos.indexOf(algo) < 0 || supportedKeyAlgos.indexOf(algo) < 0 ||
@ -405,10 +406,10 @@ export class GPGME_Keyring {
reject(error); reject(error);
}); });
}); });
};
} }
} }
/** /**
* List of algorithms supported for key generation. Please refer to the gnupg * List of algorithms supported for key generation. Please refer to the gnupg
* documentation for details * documentation for details

View File

@ -36,7 +36,7 @@ export function createMessage(operation){
return gpgme_error('PARAM_WRONG'); return gpgme_error('PARAM_WRONG');
} }
if (permittedOperations.hasOwnProperty(operation)){ if (permittedOperations.hasOwnProperty(operation)){
return Object.freeze(new GPGME_Message(operation)); return new GPGME_Message(operation);
} else { } else {
return gpgme_error('MSG_WRONG_OP'); return gpgme_error('MSG_WRONG_OP');
} }
@ -52,25 +52,26 @@ export function createMessage(operation){
export class GPGME_Message { export class GPGME_Message {
constructor(operation){ constructor(operation){
let _msg = { this._msg = {
op: operation, op: operation,
chunksize: 1023* 1024 chunksize: 1023* 1024
}; };
let expected = null; this._expected = null;
this.getOperation = function(){
return _msg.op;
};
this.setExpect = function(value){
if (value === 'base64'){
expected = value;
} }
};
this.getExpect = function(){
return expected;
};
get operation(){
return this._msg.op;
}
set expected (value){
if (value === 'base64'){
this._expected = value;
}
}
get expected() {
return this._expected;
}
/** /**
* The maximum size of responses from gpgme in bytes. As of July 2018, * The maximum size of responses from gpgme in bytes. As of July 2018,
* most browsers will only accept answers up to 1 MB of size. * most browsers will only accept answers up to 1 MB of size.
@ -80,23 +81,32 @@ export class GPGME_Message {
* will not encounter problems, larger messages will be received in * will not encounter problems, larger messages will be received in
* chunks. If the value is not explicitly specified, 1023 KB is used. * chunks. If the value is not explicitly specified, 1023 KB is used.
*/ */
this.setChunksize = function (value){ set chunksize(value){
if ( if (
Number.isInteger(value) && Number.isInteger(value) &&
value > 10 * 1024 && value > 10 * 1024 &&
value <= 1024 * 1024 value <= 1024 * 1024
){ ){
_msg.chunksize = value; this._msg.chunksize = value;
}
} }
};
this.getMsg = function(){ get chunksize(){
return _msg; return this._msg.chunksize;
}; }
this.getChunksize= function() { /**
return _msg.chunksize; * Returns the prepared message with parameters and completeness checked
}; * @returns {Object|null} Object to be posted to gnupg, or null if
* incomplete
*/
get message() {
if (this.isComplete() === true){
return this._msg;
} else {
return null;
}
}
/** /**
* Sets a parameter for the message. It validates with * Sets a parameter for the message. It validates with
@ -105,11 +115,11 @@ export class GPGME_Message {
* @param {any} value Value to set * @param {any} value Value to set
* @returns {Boolean} If the parameter was set successfully * @returns {Boolean} If the parameter was set successfully
*/ */
this.setParameter = function ( param,value ){ setParameter ( param,value ){
if (!param || typeof(param) !== 'string'){ if (!param || typeof(param) !== 'string'){
return gpgme_error('PARAM_WRONG'); return gpgme_error('PARAM_WRONG');
} }
let po = permittedOperations[_msg.op]; let po = permittedOperations[this._msg.op];
if (!po){ if (!po){
return gpgme_error('MSG_WRONG_OP'); return gpgme_error('MSG_WRONG_OP');
} }
@ -179,10 +189,9 @@ export class GPGME_Message {
return gpgme_error('PARAM_WRONG'); return gpgme_error('PARAM_WRONG');
} }
} }
_msg[param] = value; this._msg[param] = value;
return true; return true;
}; }
/** /**
@ -190,31 +199,31 @@ export class GPGME_Message {
* all 'required' parameters according to {@link permittedOperations}. * all 'required' parameters according to {@link permittedOperations}.
* @returns {Boolean} true if message is complete. * @returns {Boolean} true if message is complete.
*/ */
this.isComplete = function(){ isComplete(){
if (!_msg.op){ if (!this._msg.op){
return false; return false;
} }
let reqParams = Object.keys( let reqParams = Object.keys(
permittedOperations[_msg.op].required); permittedOperations[this._msg.op].required);
let msg_params = Object.keys(_msg); let msg_params = Object.keys(this._msg);
for (let i=0; i < reqParams.length; i++){ for (let i=0; i < reqParams.length; i++){
if (msg_params.indexOf(reqParams[i]) < 0){ if (msg_params.indexOf(reqParams[i]) < 0){
return false; return false;
} }
} }
return true; return true;
}; }
/** /**
* Sends the Message via nativeMessaging and resolves with the answer. * Sends the Message via nativeMessaging and resolves with the answer.
* @returns {Promise<Object|GPGME_Error>} * @returns {Promise<Object|GPGME_Error>}
* @async * @async
*/ */
this.post = function(){ post (){
let me = this; let me = this;
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (me.isComplete() === true) { if (me.isComplete() === true) {
let conn = Object.freeze(new Connection); let conn = new Connection;
conn.post(me).then(function(response) { conn.post(me).then(function(response) {
resolve(response); resolve(response);
}, function(reason) { }, function(reason) {
@ -225,26 +234,6 @@ export class GPGME_Message {
reject(gpgme_error('MSG_INCOMPLETE')); reject(gpgme_error('MSG_INCOMPLETE'));
} }
}); });
};
} }
/**
* Returns the prepared message with parameters and completeness checked
* @returns {Object|null} Object to be posted to gnupg, or null if
* incomplete
*/
get message(){
if (this.isComplete() === true){
return this.getMsg();
}
else {
return null;
}
}
get operation(){
return this.getOperation();
}
get chunksize(){
return this.getChunksize();
}
} }

View File

@ -66,7 +66,7 @@ export function createSignature(sigObject){
} }
} }
} }
return Object.freeze(new GPGME_Signature(sigObject)); return new GPGME_Signature(sigObject);
} }
@ -82,47 +82,47 @@ export function createSignature(sigObject){
class GPGME_Signature { class GPGME_Signature {
constructor(sigObject){ constructor(sigObject){
let _rawSigObject = sigObject; this._rawSigObject = sigObject;
}
this.getFingerprint = function(){ get fingerprint(){
if (!_rawSigObject.fingerprint){ if (!this._rawSigObject.fingerprint){
return gpgme_error('SIG_WRONG'); return gpgme_error('SIG_WRONG');
} else { } else {
return _rawSigObject.fingerprint; return this._rawSigObject.fingerprint;
}
} }
};
/** /**
* The expiration of this Signature as Javascript date, or null if * The expiration of this Signature as Javascript date, or null if
* signature does not expire * signature does not expire
* @returns {Date | null} * @returns {Date | null}
*/ */
this.getExpiration = function(){ get expiration(){
if (!_rawSigObject.exp_timestamp){ if (!this._rawSigObject.exp_timestamp){
return null; return null;
} }
return new Date(_rawSigObject.exp_timestamp* 1000); return new Date(this._rawSigObject.exp_timestamp* 1000);
}; }
/** /**
* The creation date of this Signature in Javascript Date * The creation date of this Signature in Javascript Date
* @returns {Date} * @returns {Date}
*/ */
this.getTimestamp= function (){ get timestamp (){
return new Date(_rawSigObject.timestamp * 1000); return new Date(this._rawSigObject.timestamp * 1000);
}; }
/** /**
* The overall validity of the key. If false, errorDetails may contain * The overall validity of the key. If false, errorDetails may contain
* additional information. * additional information.
*/ */
this.getValid= function() { get valid () {
if (_rawSigObject.summary.valid === true){ if (this._rawSigObject.summary.valid === true){
return true; return true;
} else { } else {
return false; return false;
} }
}; }
/** /**
* gives more information on non-valid signatures. Refer to the gpgme * gives more information on non-valid signatures. Refer to the gpgme
@ -130,53 +130,17 @@ class GPGME_Signature {
* for details on the values. * for details on the values.
* @returns {Object} Object with boolean properties * @returns {Object} Object with boolean properties
*/ */
this.getErrorDetails = function (){ get errorDetails(){
let properties = ['revoked', 'key-expired', 'sig-expired', let properties = ['revoked', 'key-expired', 'sig-expired',
'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy', 'key-missing', 'crl-missing', 'crl-too-old', 'bad-policy',
'sys-error']; 'sys-error'];
let result = {}; let result = {};
for (let i=0; i< properties.length; i++){ for (let i=0; i< properties.length; i++){
if ( _rawSigObject.hasOwnProperty(properties[i]) ){ if ( this._rawSigObject.hasOwnProperty(properties[i]) ){
result[properties[i]] = _rawSigObject[properties[i]]; result[properties[i]] = this._rawSigObject[properties[i]];
} }
} }
return result; return result;
};
}
/**
* Convenience getter for {@link getFingerprint}
*/
get fingerprint(){
return this.getFingerprint();
}
/**
* Convenience getter for {@link getExpiration}
*/
get expiration(){
return this.getExpiration();
}
/**
* Convenience getter for {@link getTimeStamp}
*/
get timestamp(){
return this.getTimestamp();
}
/**
* Convenience getter for {@link getValid}
*/
get valid(){
return this.getValid();
}
/**
* Convenience getter for {@link getErrorDetails}
*/
get errorDetails(){
return this.getErrorDetails();
} }
} }

View File

@ -85,27 +85,27 @@ import { createSignature } from './Signature';
export class GpgME { export class GpgME {
constructor(){ constructor(){
let _Keyring = null; this._Keyring = null;
}
/** /**
* Sets a new Keyring to be used * setter for {@link setKeyring}.
* @param {GPGME_Keyring} keyring * @param {GPGME_Keyring} keyring A Keyring to use
*/ */
this.setKeyring = function(keyring){ set Keyring(keyring){
if (keyring && keyring instanceof GPGME_Keyring){ if (keyring && keyring instanceof GPGME_Keyring){
_Keyring = keyring; this._Keyring = keyring;
}
} }
};
/** /**
* Accesses the {@link GPGME_Keyring}. * Accesses the {@link GPGME_Keyring}.
*/ */
this.getKeyring = function(){ get Keyring(){
if (!_Keyring){ if (!this._Keyring){
_Keyring = Object.freeze(new GPGME_Keyring); this._Keyring = new GPGME_Keyring;
}
return this._Keyring;
} }
return _Keyring;
};
/** /**
* Encrypt (and optionally sign) data * Encrypt (and optionally sign) data
@ -128,9 +128,8 @@ export class GpgME {
* message and additional info. * message and additional info.
* @async * @async
*/ */
this.encrypt = function (data, publicKeys, secretKeys, base64=false, encrypt (data, publicKeys, secretKeys, base64=false, armor=true,
armor=true, wildcard=false, additional = {} wildcard=false, additional = {}){
){
let msg = createMessage('encrypt'); let msg = createMessage('encrypt');
if (msg instanceof Error){ if (msg instanceof Error){
return Promise.reject(msg); return Promise.reject(msg);
@ -162,7 +161,7 @@ export class GpgME {
} else { } else {
return Promise.reject(gpgme_error('MSG_INCOMPLETE')); return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
} }
}; }
/** /**
* Decrypts a Message * Decrypts a Message
@ -173,7 +172,7 @@ export class GpgME {
* @returns {Promise<decrypt_result>} Decrypted Message and information * @returns {Promise<decrypt_result>} Decrypted Message and information
* @async * @async
*/ */
this.decrypt = function (data, base64=false){ decrypt (data, base64=false){
if (data === undefined){ if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY')); return Promise.reject(gpgme_error('MSG_EMPTY'));
} }
@ -211,7 +210,7 @@ export class GpgME {
reject(error); reject(error);
}); });
}); });
}; }
/** /**
* Sign a Message * Sign a Message
@ -225,7 +224,7 @@ export class GpgME {
* @returns {Promise<signResult>} * @returns {Promise<signResult>}
* @async * @async
*/ */
this.sign = function (data, keys, mode='clearsign', base64=false) { sign (data, keys, mode='clearsign', base64=false) {
if (data === undefined){ if (data === undefined){
return Promise.reject(gpgme_error('MSG_EMPTY')); return Promise.reject(gpgme_error('MSG_EMPTY'));
} }
@ -243,7 +242,7 @@ export class GpgME {
putData(msg, data); putData(msg, data);
return new Promise(function(resolve,reject) { return new Promise(function(resolve,reject) {
if (mode ==='detached'){ if (mode ==='detached'){
msg.setExpect('base64'); msg.expected ='base64';
} }
msg.post().then( function(message) { msg.post().then( function(message) {
if (mode === 'clearsign'){ if (mode === 'clearsign'){
@ -260,7 +259,7 @@ export class GpgME {
reject(error); reject(error);
}); });
}); });
}; }
/** /**
* Verifies data. * Verifies data.
@ -272,7 +271,7 @@ export class GpgME {
* @returns {Promise<verifyResult>} * @returns {Promise<verifyResult>}
*@async *@async
*/ */
this.verify= function (data, signature, base64 = false){ verify (data, signature, base64 = false){
let msg = createMessage('verify'); let msg = createMessage('verify');
let dt = putData(msg, data); let dt = putData(msg, data);
if (dt instanceof Error){ if (dt instanceof Error){
@ -306,22 +305,6 @@ export class GpgME {
reject(error); reject(error);
}); });
}); });
};
}
/**
* setter for {@link setKeyring}.
* @param {GPGME_Keyring} keyring A Keyring to use
*/
set Keyring(keyring){
this.setKeyring(keyring);
}
/**
* Accesses the {@link GPGME_Keyring}.
*/
get Keyring(){
return this.getKeyring();
} }
} }

View File

@ -34,11 +34,11 @@ import { Connection } from './Connection';
*/ */
function init(){ function init(){
return new Promise(function(resolve, reject){ return new Promise(function(resolve, reject){
const connection = Object.freeze(new Connection); const connection = new Connection;
connection.checkConnection(false).then( connection.checkConnection(false).then(
function(result){ function(result){
if (result === true) { if (result === true) {
resolve(Object.freeze(new GpgME())); resolve(new GpgME());
} else { } else {
reject(gpgme_error('CONN_NO_CONNECT')); reject(gpgme_error('CONN_NO_CONNECT'));
} }
@ -48,5 +48,5 @@ function init(){
}); });
} }
const exportvalue = Object.freeze({init:init}); const exportvalue = {init:init};
export default exportvalue; export default exportvalue;

View File

@ -27,7 +27,7 @@ import { key_params as kp } from './unittest_inputvalues';
import { Connection } from './src/Connection'; import { Connection } from './src/Connection';
import { gpgme_error } from './src/Errors'; import { gpgme_error } from './src/Errors';
import { toKeyIdArray , isFingerprint } from './src/Helpers'; import { toKeyIdArray , isFingerprint } from './src/Helpers';
import { GPGME_Key , createKey } from './src/Key'; import { createKey } from './src/Key';
import { GPGME_Keyring } from './src/Keyring'; import { GPGME_Keyring } from './src/Keyring';
import {GPGME_Message, createMessage} from './src/Message'; import {GPGME_Message, createMessage} from './src/Message';
@ -116,14 +116,6 @@ function unittests (){
expect(test0).to.include(hp.validFingerprint); 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(){ it('openpgpjs-like object', function(){
let test0 = toKeyIdArray(hp.valid_openpgplike); let test0 = toKeyIdArray(hp.valid_openpgplike);
@ -169,15 +161,9 @@ function unittests (){
}); });
describe('GPGME_Key', function(){ describe('GPGME_Key', function(){
it('correct Key initialization', function(){
let key = createKey(kp.validKeyFingerprint);
expect(key).to.be.an.instanceof(GPGME_Key);
});
it('Key has data after a first refresh', function(done) { it('Key has data after a first refresh', function(done) {
let key = createKey(kp.validKeyFingerprint); let key = createKey(kp.validKeyFingerprint);
key.refreshKey().then(function(key2){ key.refreshKey().then(function(key2){
expect(key2).to.be.an.instanceof(GPGME_Key);
expect(key2.get).to.be.a('function'); expect(key2.get).to.be.a('function');
for (let i=0; i < kp.validKeyProperties.length; i++) { for (let i=0; i < kp.validKeyProperties.length; i++) {
let prop = key2.get(kp.validKeyProperties[i]); let prop = key2.get(kp.validKeyProperties[i]);
@ -220,7 +206,6 @@ function unittests (){
it('Non-cached key async hasSecret (no secret in Key)', function (done){ it('Non-cached key async hasSecret (no secret in Key)', function (done){
let key = createKey(kp.validFingerprintNoSecret, true); let key = createKey(kp.validFingerprintNoSecret, true);
expect(key).to.be.an.instanceof(GPGME_Key);
key.get('hasSecret').then(function(result){ key.get('hasSecret').then(function(result){
expect(result).to.be.a('boolean'); expect(result).to.be.a('boolean');
expect(result).to.equal(false); expect(result).to.equal(false);
@ -246,32 +231,21 @@ function unittests (){
} }
}); });
it('malformed GPGME_Key cannot be used', function(){ // it('Overwriting getFingerprint does not work', function(){
for (let i=0; i < 4; i++){ // const evilFunction = function(){
let key = new GPGME_Key(wp.four_invalid_params[i]); // return 'bad Data';
expect(key.fingerprint).to.be.an.instanceof(Error); // };
expect(key.fingerprint.code).to.equal('KEY_INVALID'); // let key = createKey(kp.validKeyFingerprint, true);
} // expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
}); // try {
// key.getFingerprint = evilFunction;
it('Overwriting getFingerprint does not work', function(){ // }
const evilFunction = function(){ // catch(e) {
return 'bad Data'; // expect(e).to.be.an.instanceof(TypeError);
}; // }
let key = createKey(kp.validKeyFingerprint, true); // expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
expect(key.fingerprint).to.equal(kp.validKeyFingerprint); // expect(key.getFingerprint).to.not.equal(evilFunction);
try { // });
key.getFingerprint = evilFunction;
}
catch(e) {
expect(e).to.be.an.instanceof(TypeError);
}
expect(key.fingerprint).to.equal(kp.validKeyFingerprint);
expect(key.getFingerprint).to.not.equal(evilFunction);
});
// TODO: tests for subkeys
// TODO: tests for userids
// TODO: some invalid tests for key/keyring
}); });
describe('GPGME_Keyring', function(){ describe('GPGME_Keyring', function(){
@ -287,10 +261,7 @@ function unittests (){
let keyring = new GPGME_Keyring; let keyring = new GPGME_Keyring;
keyring.getKeys(null, true).then(function(result){ keyring.getKeys(null, true).then(function(result){
expect(result).to.be.an('array'); expect(result).to.be.an('array');
expect(result[0]).to.be.an.instanceof(GPGME_Key);
expect(result[0].get('hasSecret')).to.be.a('boolean'); expect(result[0].get('hasSecret')).to.be.a('boolean');
// expect(result[0].get('armored')).to.include(
// '-----END PGP PUBLIC KEY BLOCK-----');
done(); done();
}); });
} }
@ -302,7 +273,6 @@ function unittests (){
keyring.getKeys(kp.validKeyFingerprint, true).then( keyring.getKeys(kp.validKeyFingerprint, true).then(
function(result){ function(result){
expect(result).to.be.an('array'); expect(result).to.be.an('array');
expect(result[0]).to.be.an.instanceof(GPGME_Key);
expect(result[0].get('hasSecret')).to.be.a('boolean'); expect(result[0].get('hasSecret')).to.be.a('boolean');
done(); done();
} }