js: more Keyring/Key handling
-- * src/Keys.js - made setKeyData more consistent with other methods - added convenience methods (Key.armored, Key.hasSecret) - Added a Key delete function * src/Keyring.js: - added a getkeysArmored which allows for bulk export of public Keys gpgmejs: - removed deleteKey. It is now a method of the Key itself - Encrypt: Added some common options as parameter, and the possibility to set all allowed flags via an additional Object
This commit is contained in:
parent
53ce2b94bc
commit
332b4adbcc
@ -71,6 +71,10 @@ const err_list = {
|
|||||||
msg:'This key does not exist in GPG',
|
msg:'This key does not exist in GPG',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
},
|
},
|
||||||
|
'KEY_NO_INIT': {
|
||||||
|
msg:'This property has not been retrieved yet from GPG',
|
||||||
|
type: 'error'
|
||||||
|
}
|
||||||
// generic
|
// generic
|
||||||
'PARAM_WRONG':{
|
'PARAM_WRONG':{
|
||||||
msg: 'Invalid parameter was found',
|
msg: 'Invalid parameter was found',
|
||||||
|
@ -93,57 +93,32 @@ export class GPGME_Key {
|
|||||||
} else if (this._data.fingerprint !== data.fingerprint){
|
} else if (this._data.fingerprint !== data.fingerprint){
|
||||||
return gpgme_error('KEY_INVALID');
|
return gpgme_error('KEY_INVALID');
|
||||||
}
|
}
|
||||||
|
let dataKeys = Object.keys(data);
|
||||||
let booleans = ['expired', 'disabled','invalid','can_encrypt',
|
for (let i=0; i< dataKeys.length; i++){
|
||||||
'can_sign','can_certify','can_authenticate','secret',
|
if (!validKeyProperties.hasOwnProperty(dataKeys[i])){
|
||||||
'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');
|
return gpgme_error('KEY_INVALID');
|
||||||
}
|
}
|
||||||
this._data[booleans[b]] = data[booleans[b]];
|
if (validKeyProperties[dataKeys[i]](data[dataKeys[i]]) !== true ){
|
||||||
}
|
|
||||||
if (typeof(data.protocol) !== 'string'){
|
|
||||||
return gpgme_error('KEY_INVALID');
|
return gpgme_error('KEY_INVALID');
|
||||||
}
|
}
|
||||||
// TODO check valid protocols?
|
switch (dataKeys[i]){
|
||||||
this._data.protocol = data.protocol;
|
case 'subkeys':
|
||||||
|
|
||||||
if (typeof(data.owner_trust) !== 'string'){
|
|
||||||
return gpgme_error('KEY_INVALID');
|
|
||||||
}
|
|
||||||
// TODO check valid values?
|
|
||||||
this._data.owner_trust = data.owner_trust;
|
|
||||||
|
|
||||||
// TODO: what about origin ?
|
|
||||||
if (!Number.isInteger(data.last_update)){
|
|
||||||
return gpgme_error('KEY_INVALID');
|
|
||||||
}
|
|
||||||
this._data.last_update = data.last_update;
|
|
||||||
|
|
||||||
this._data.subkeys = [];
|
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++) {
|
for (let i=0; i< data.subkeys.length; i++) {
|
||||||
this._data.subkeys.push(
|
this._data.subkeys.push(
|
||||||
new GPGME_Subkey(data.subkeys[i]));
|
new GPGME_Subkey(data.subkeys[i]));
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
case 'userids':
|
||||||
this._data.userids = [];
|
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++) {
|
for (let i=0; i< data.userids.length; i++) {
|
||||||
this._data.userids.push(
|
this._data.userids.push(
|
||||||
new GPGME_UserId(data.userids[i]));
|
new GPGME_UserId(data.userids[i]));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this._data[dataKeys[i]] = data[dataKeys[i]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -161,7 +136,9 @@ export class GPGME_Key {
|
|||||||
if (cached === false) {
|
if (cached === false) {
|
||||||
let me = this;
|
let me = this;
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
if (property === 'armor'){
|
if (!validKeyProperties.hasOwnProperty(property)){
|
||||||
|
reject('PARAM_WRONG');
|
||||||
|
} else if (property === 'armored'){
|
||||||
resolve(me.getArmor());
|
resolve(me.getArmor());
|
||||||
} else if (property === 'hasSecret'){
|
} else if (property === 'hasSecret'){
|
||||||
resolve(me.getHasSecret());
|
resolve(me.getHasSecret());
|
||||||
@ -174,14 +151,22 @@ export class GPGME_Key {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (!this._data.hasOwnProperty(property)){
|
if (!validKeyProperties.hasOwnProperty(property)){
|
||||||
return gpgme_error('PARAM_WRONG');
|
return gpgme_error('PARAM_WRONG');
|
||||||
|
}
|
||||||
|
if (!this._data.hasOwnProperty(property)){
|
||||||
|
return gpgme_error('KEY_NO_INIT');
|
||||||
} else {
|
} else {
|
||||||
return (this._data[property]);
|
return (this._data[property]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get armored () {
|
||||||
|
return this.get('armored');
|
||||||
|
//TODO exception if empty
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the Key from gnupg
|
* Reloads the Key from gnupg
|
||||||
*/
|
*/
|
||||||
@ -207,15 +192,6 @@ export class GPGME_Key {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 armor(){ TODO }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the armored block of the non- secret parts of the Key directly
|
* Query the armored block of the non- secret parts of the Key directly
|
||||||
* from gpg.
|
* from gpg.
|
||||||
@ -279,6 +255,49 @@ export class GPGME_Key {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to be directly used as properties of the Key
|
||||||
|
* Notice that these rely on cached info and may be outdated. Use the async
|
||||||
|
* get(property, false) if you need the most current info
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {String} The armored public Key block
|
||||||
|
*/
|
||||||
|
get armored(){
|
||||||
|
return this.get('armored', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Boolean} If the key is considered a "private Key",
|
||||||
|
* i.e. owns a secret subkey.
|
||||||
|
*/
|
||||||
|
get hasSecret(){
|
||||||
|
return this.get('hasSecret', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the public Key from the GPG Keyring. Note that a deletion of a
|
||||||
|
* secret key is not supported by the native backend.
|
||||||
|
* @returns {Boolean} Success if key was deleted, rejects with a GPG error
|
||||||
|
* otherwise
|
||||||
|
*/
|
||||||
|
delete(){
|
||||||
|
let me = this;
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
if (!me._data.fingerprint){
|
||||||
|
reject(gpgme_error('KEY_INVALID'));
|
||||||
|
}
|
||||||
|
let msg = createMessage('delete');
|
||||||
|
msg.setParameter('key', me._data.fingerprint);
|
||||||
|
msg.post().then(function(result){
|
||||||
|
resolve(result.success);
|
||||||
|
}, function(error){
|
||||||
|
reject(error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -453,3 +472,78 @@ const validSubKeyProperties = {
|
|||||||
return (Number.isInteger(value) && value > 0);
|
return (Number.isInteger(value) && value > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const validKeyProperties = {
|
||||||
|
//TODO better validation?
|
||||||
|
'fingerprint': function(value){
|
||||||
|
return isFingerprint(value);
|
||||||
|
},
|
||||||
|
'armored': function(value){
|
||||||
|
return typeof(value === 'string');
|
||||||
|
},
|
||||||
|
'revoked': function(value){
|
||||||
|
return typeof(value) === 'boolean';
|
||||||
|
},
|
||||||
|
'expired': function(value){
|
||||||
|
return typeof(value) === 'boolean';
|
||||||
|
},
|
||||||
|
'disabled': function(value){
|
||||||
|
return typeof(value) === 'boolean';
|
||||||
|
},
|
||||||
|
'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';
|
||||||
|
},
|
||||||
|
'protocol': function(value){
|
||||||
|
return typeof(value) === 'string';
|
||||||
|
//TODO check for implemented ones
|
||||||
|
},
|
||||||
|
'issuer_serial': function(value){
|
||||||
|
return typeof(value) === 'string';
|
||||||
|
},
|
||||||
|
'issuer_name': function(value){
|
||||||
|
return typeof(value) === 'string';
|
||||||
|
},
|
||||||
|
'chain_id': function(value){
|
||||||
|
return typeof(value) === 'string';
|
||||||
|
},
|
||||||
|
'owner_trust': function(value){
|
||||||
|
return typeof(value) === 'string';
|
||||||
|
},
|
||||||
|
'last_update': function(value){
|
||||||
|
return (Number.isInteger(value));
|
||||||
|
//TODO undefined/null possible?
|
||||||
|
},
|
||||||
|
'origin': function(value){
|
||||||
|
return (Number.isInteger(value));
|
||||||
|
},
|
||||||
|
'subkeys': function(value){
|
||||||
|
return (Array.isArray(value));
|
||||||
|
},
|
||||||
|
'userids': function(value){
|
||||||
|
return (Array.isArray(value));
|
||||||
|
},
|
||||||
|
'tofu': function(value){
|
||||||
|
return (Array.isArray(value));
|
||||||
|
},
|
||||||
|
'hasSecret': function(value){
|
||||||
|
return typeof(value) === 'boolean';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
import {createMessage} from './Message'
|
import {createMessage} from './Message'
|
||||||
import {GPGME_Key, createKey} from './Key'
|
import {GPGME_Key, createKey} from './Key'
|
||||||
import { isFingerprint } from './Helpers';
|
import { isFingerprint, toKeyIdArray } from './Helpers';
|
||||||
import { gpgme_error } from './Errors';
|
import { gpgme_error } from './Errors';
|
||||||
|
|
||||||
export class GPGME_Keyring {
|
export class GPGME_Keyring {
|
||||||
@ -43,10 +43,10 @@ export class GPGME_Keyring {
|
|||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
let msg;
|
let msg;
|
||||||
msg = createMessage('keylist');
|
msg = createMessage('keylist');
|
||||||
if (pattern && typeof(pattern) === 'string'){
|
if (pattern !== undefined){
|
||||||
msg.setParameter('keys', pattern);
|
msg.setParameter('keys', pattern);
|
||||||
}
|
}
|
||||||
msg.setParameter('sigs', true); //TODO do we need this?
|
msg.setParameter('sigs', true);
|
||||||
msg.post().then(function(result){
|
msg.post().then(function(result){
|
||||||
let resultset = [];
|
let resultset = [];
|
||||||
let promises = [];
|
let promises = [];
|
||||||
@ -72,10 +72,30 @@ export class GPGME_Keyring {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// TODO:
|
|
||||||
// deleteKey(key, include_secret=false)
|
/**
|
||||||
// getKeysArmored(pattern) //just dump all armored keys
|
* Fetches the armored public Key blocks for all Keys matchin the pattern
|
||||||
|
* (if no pattern is given, fetches all known to gnupg)
|
||||||
|
* @param {String|Array<String>} pattern (optional)
|
||||||
|
* @returns {Promise<String>} Armored Key blocks
|
||||||
|
*/
|
||||||
|
getKeysArmored(pattern) {
|
||||||
|
if (pattern)
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let msg = createMessage('export');
|
||||||
|
msg.setParameter('armor', true);
|
||||||
|
if (pattern !== undefined){
|
||||||
|
msg.setParameter('keys', pattern);
|
||||||
|
}
|
||||||
|
msg.post().then(function(result){
|
||||||
|
resolve(result.data);
|
||||||
|
}, function(error){
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// getDefaultKey() Big TODO
|
// getDefaultKey() Big TODO
|
||||||
// importKeys(armoredKeys)
|
// importKeys(armoredKeys)
|
||||||
|
// generateKey --> TODO (Andre noch anfragen!)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -46,28 +46,48 @@ export class GpgME {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} data text/data to be encrypted as String
|
* Encrypt (and optionally sign) a Message
|
||||||
|
* @param {String|Object} data text/data to be encrypted as String. Also accepts Objects with a getText method
|
||||||
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
|
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} publicKeys Keys used to encrypt the message
|
||||||
|
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} secretKeys (optional) Keys used to sign the message
|
||||||
|
* @param {Boolean} base64 (optional) The data is already considered to be in base64 encoding
|
||||||
|
* @param {Boolean} armor (optional) Request the output as armored block
|
||||||
* @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
|
* @param {Boolean} wildcard (optional) If true, recipient information will not be added to the message
|
||||||
|
* @param {Object} additional use additional gpg options (refer to src/permittedOperations)
|
||||||
|
* @returns {Promise<Object>} Encrypted message:
|
||||||
|
* data: The encrypted message
|
||||||
|
* base64: Boolean indicating whether data is base64 encoded.
|
||||||
|
* @async
|
||||||
*/
|
*/
|
||||||
encrypt(data, publicKeys, base64=false, wildcard=false){
|
encrypt(data, publicKeys, secretKeys, base64=false, armor=true,
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
// TODO temporary
|
msg.setParameter('armor', armor);
|
||||||
msg.setParameter('armor', true);
|
|
||||||
msg.setParameter('always-trust', true);
|
msg.setParameter('always-trust', true);
|
||||||
if (base64 === true) {
|
if (base64 === true) {
|
||||||
msg.setParameter('base64', true);
|
msg.setParameter('base64', true);
|
||||||
}
|
}
|
||||||
let pubkeys = toKeyIdArray(publicKeys);
|
let pubkeys = toKeyIdArray(publicKeys);
|
||||||
msg.setParameter('keys', pubkeys);
|
msg.setParameter('keys', pubkeys);
|
||||||
|
let sigkeys = toKeyIdArray(secretKeys);
|
||||||
|
if (sigkeys.length > 0) {
|
||||||
|
msg.setParameter('signing_keys', sigkeys);
|
||||||
|
}
|
||||||
putData(msg, data);
|
putData(msg, data);
|
||||||
if (wildcard === true){
|
if (wildcard === true){
|
||||||
msg.setParameter('throw-keyids', true);
|
msg.setParameter('throw-keyids', true);
|
||||||
};
|
};
|
||||||
|
if (additional){
|
||||||
|
let additional_Keys = Object.keys(additional);
|
||||||
|
for (let k = 0; k < additional_Keys.length; k++) {
|
||||||
|
msg.setParameter(additional_Keys[k],
|
||||||
|
additional[additional_Keys[k]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (msg.isComplete === true){
|
if (msg.isComplete === true){
|
||||||
return msg.post();
|
return msg.post();
|
||||||
} else {
|
} else {
|
||||||
@ -76,16 +96,17 @@ export class GpgME {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} data TODO base64? Message with the encrypted data
|
* Decrypt a Message
|
||||||
* @param {Boolean} base64 (optional) Response should stay base64
|
* @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a getText method
|
||||||
|
* @param {Boolean} base64 (optional) Response is expected to be base64 encoded
|
||||||
* @returns {Promise<Object>} decrypted message:
|
* @returns {Promise<Object>} decrypted message:
|
||||||
data: The decrypted data. This may be base64 encoded.
|
data: The decrypted data. This may be base64 encoded.
|
||||||
base64: Boolean indicating whether data is base64 encoded.
|
base64: Boolean indicating whether data is base64 encoded.
|
||||||
mime: A Boolean indicating whether the data is a MIME object.
|
mime: A Boolean indicating whether the data is a MIME object.
|
||||||
info: An optional object with extra information.
|
signatures: Array of signature Objects TODO not yet implemented.
|
||||||
|
// should be an object that can tell if all signatures are valid etc.
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
|
|
||||||
decrypt(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'));
|
||||||
@ -99,10 +120,22 @@ export class GpgME {
|
|||||||
}
|
}
|
||||||
putData(msg, data);
|
putData(msg, data);
|
||||||
return msg.post();
|
return msg.post();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sign(data, keys, mode='clearsign', base64=false) { //sender
|
/**
|
||||||
|
* Sign a Message
|
||||||
|
* @param {String|Object} data text/data to be decrypted. Accepts Strings and Objects with a gettext methos
|
||||||
|
* @param {GPGME_Key|String|Array<String>|Array<GPGME_Key>} keys The key/keys to use for signing
|
||||||
|
* @param {*} mode The signing mode. Currently supported:
|
||||||
|
* 'clearsign': (default) The Message is embedded into the signature
|
||||||
|
* 'detached': The signature is stored separately
|
||||||
|
* @param {*} base64 input is considered base64
|
||||||
|
* @returns {Promise<Object>}
|
||||||
|
* data: The resulting data. In clearsign mode this includes the signature
|
||||||
|
* signature: The detached signature (if in detached mode)
|
||||||
|
* @async
|
||||||
|
*/
|
||||||
|
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'));
|
||||||
}
|
}
|
||||||
@ -139,38 +172,10 @@ export class GpgME {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteKey(key, delete_secret = false, no_confirm = false){
|
|
||||||
return Promise.reject(gpgme_error('NOT_YET_IMPLEMENTED'));
|
|
||||||
let msg = createMessage('deletekey');
|
|
||||||
if (msg instanceof Error){
|
|
||||||
return Promise.reject(msg);
|
|
||||||
}
|
|
||||||
let key_arr = toKeyIdArray(key);
|
|
||||||
if (key_arr.length !== 1){
|
|
||||||
return Promise.reject(
|
|
||||||
gpgme_error('GENERIC_ERROR'));
|
|
||||||
// TBD should always be ONE key?
|
|
||||||
}
|
|
||||||
msg.setParameter('key', key_arr[0]);
|
|
||||||
if (delete_secret === true){
|
|
||||||
msg.setParameter('allow_secret', true);
|
|
||||||
// TBD
|
|
||||||
}
|
|
||||||
if (no_confirm === true){ //TODO: Do we want this hidden deep in the code?
|
|
||||||
msg.setParameter('delete_force', true);
|
|
||||||
// TBD
|
|
||||||
}
|
|
||||||
if (msg.isComplete === true){
|
|
||||||
return msg.post();
|
|
||||||
} else {
|
|
||||||
return Promise.reject(gpgme_error('MSG_INCOMPLETE'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the data of the message
|
* Sets the data of the message, setting flags according on the data type
|
||||||
* @param {GPGME_Message} message The message where this data will be set
|
* @param {GPGME_Message} message The message where this data will be set
|
||||||
* @param {*} data The data to enter
|
* @param {*} data The data to enter
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user