aboutsummaryrefslogtreecommitdiffstats
path: root/lang/js/src/Key.js
diff options
context:
space:
mode:
Diffstat (limited to 'lang/js/src/Key.js')
-rw-r--r--lang/js/src/Key.js433
1 files changed, 308 insertions, 125 deletions
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
@@ -26,13 +26,19 @@
*
*/
-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');
@@ -47,6 +53,9 @@ export function createKey(fingerprint, parent){
}
}
+/**
+ * Representing the Keys as stored in GPG
+ */
export class GPGME_Key {
constructor(fingerprint, connection){
@@ -63,7 +72,7 @@ export class GPGME_Key {
}
get connection(){
- if (!this._fingerprint){
+ if (!this._data.fingerprint){
return gpgme_error('KEY_INVALID');
}
if (!this._connection instanceof Connection){
@@ -74,171 +83,345 @@ export class GPGME_Key {
}
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<GPGME_Key>}
+ * 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<String>
*/
- 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<String>} 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);
+ }
+}