Keyring: separate storage for public and private keys. ImportKey method supports update.
This commit is contained in:
parent
d6e4e3c028
commit
efc384e71c
|
@ -27,14 +27,6 @@ var enums = require('../enums.js'),
|
|||
keyModule = require('../key.js'),
|
||||
util = require('../util.js');
|
||||
|
||||
/**
|
||||
* Callback to check if a key matches the input
|
||||
* @callback module:keyring/keyring.checkCallback
|
||||
* @param {String} input input to search for
|
||||
* @param {module:key~Key} key The key to be checked.
|
||||
* @return {Boolean} True if the input matches the specified key
|
||||
*/
|
||||
|
||||
module.exports = Keyring;
|
||||
|
||||
/**
|
||||
|
@ -45,21 +37,105 @@ module.exports = Keyring;
|
|||
*/
|
||||
function Keyring(storeHandler) {
|
||||
this.storeHandler = storeHandler || new (require('./localstore.js'))();
|
||||
this.keys = this.storeHandler.load();
|
||||
};
|
||||
this.publicKeys = new KeyArray(this.storeHandler.loadPublic());
|
||||
this.privateKeys = new KeyArray(this.storeHandler.loadPrivate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the storeHandler to save the keys
|
||||
*/
|
||||
Keyring.prototype.store = function () {
|
||||
this.storeHandler.store(this.keys);
|
||||
this.storeHandler.storePublic(this.publicKeys.keys);
|
||||
this.storeHandler.storePrivate(this.privateKeys.keys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the keyring - erase all the keys
|
||||
*/
|
||||
Keyring.prototype.clear = function() {
|
||||
this.keys = [];
|
||||
this.publicKeys.keys = [];
|
||||
this.privateKeys.keys = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the keyring for keys having the specified key id
|
||||
* @param {String} keyId provided as string of hex number (lowercase)
|
||||
* @param {Boolean} deep if true search also in subkeys
|
||||
* @return {Array<module:key~Key>|null} keys found or null
|
||||
*/
|
||||
Keyring.prototype.getKeysForId = function (keyId, deep) {
|
||||
var result = [];
|
||||
result = result.concat(this.publicKeys.getForId(keyId, deep) || []);
|
||||
result = result.concat(this.privateKeys.getForId(keyId, deep) || []);
|
||||
return result.length ? result : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes keys having the specified key id from the keyring
|
||||
* @param {String} keyId provided as string of hex number (lowercase)
|
||||
* @return {Array<module:key~Key>|null} keys found or null
|
||||
*/
|
||||
Keyring.prototype.removeKeysForId = function (keyId) {
|
||||
var result = [];
|
||||
result = result.concat(this.publicKeys.removeForId(keyId) || []);
|
||||
result = result.concat(this.privateKeys.removeForId(keyId) || []);
|
||||
return result.length ? result : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the keyring for keys having the specified long key id (fingerprint)
|
||||
* @param {String} longKeyId fingerprint in lowercase hex
|
||||
* @param {Boolean} deep if true search also in subkeys
|
||||
* @return {Array<module:key~Key>|null} keys found or null
|
||||
*/
|
||||
Keyring.prototype.getKeysForLongId = function (longKeyId, deep) {
|
||||
var result = [];
|
||||
result = result.concat(this.publicKeys.getForLongId(longKeyId, deep) || []);
|
||||
result = result.concat(this.privateKeys.getForLongId(longKeyId, deep) || []);
|
||||
return result.length ? result : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes keys having the specified long key id (fingerprint) from the keyring
|
||||
* @param {String} longKeyId fingerprint in lowercase hex
|
||||
* @return {Array<module:key~Key>|null} keys found or null
|
||||
*/
|
||||
Keyring.prototype.removeKeysForLongId = function (longKeyId) {
|
||||
var result = [];
|
||||
result = result.concat(this.publicKeys.removeForLongId(longKeyId) || []);
|
||||
result = result.concat(this.privateKeys.removeForLongId(longKeyId) || []);
|
||||
return result.length ? result : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all public and private keys
|
||||
* @return {Array<module:key~Key>} all keys
|
||||
*/
|
||||
Keyring.prototype.getAllKeys = function () {
|
||||
return this.publicKeys.keys.concat(this.privateKeys.keys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Array of keys
|
||||
* @param {Array<module:key~Key>} keys The keys to store in this array
|
||||
*/
|
||||
function KeyArray(keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches all keys in the KeyArray matching the address or address part of the user ids
|
||||
* @param {String} email email address to search for
|
||||
* @return {Array<module:key~Key>} The public keys associated with provided email address.
|
||||
*/
|
||||
KeyArray.prototype.getForAddress = function(email) {
|
||||
var results = [];
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (emailCheck(email, this.keys[i])) {
|
||||
results.push(this.keys[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -82,82 +158,19 @@ function emailCheck(email, key) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks a key to see if it matches the specified keyid
|
||||
* @param {String} id hex string keyid to search for
|
||||
* @param {module:key~Key} key the key to be checked.
|
||||
* @return {Boolean} true if the email address is defined in the specified key
|
||||
* @inner
|
||||
*/
|
||||
function idCheck(id, key) {
|
||||
var keyids = key.getKeyIds();
|
||||
for (var i = 0; i < keyids.length; i++) {
|
||||
if (util.hexstrdump(keyids[i].write()) == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* searches all public keys in the keyring matching the address or address part of the user ids
|
||||
* @param {Array<module:key~Key>} keys array of keys to search
|
||||
* @param {module:keyring/keyring.checkCallback} identityFunction callback function which checks for a match
|
||||
* @param {String} identityInput input to check against
|
||||
* @param {module:enums.packet} keyType packet types of keys to check
|
||||
* @return {Array<module:key~Key>} array of keys which match
|
||||
*/
|
||||
function checkForIdentityAndKeyTypeMatch(keys, identityFunction, identityInput, keyType) {
|
||||
var results = [];
|
||||
for (var p = 0; p < keys.length; p++) {
|
||||
var key = keys[p];
|
||||
switch (keyType) {
|
||||
case enums.packet.publicKey:
|
||||
if (key.isPublic() && identityFunction(identityInput, key)) {
|
||||
results.push(key);
|
||||
}
|
||||
break;
|
||||
case enums.packet.secretKey:
|
||||
if (key.isPrivate() && identityFunction(identityInput, key)) {
|
||||
results.push(key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* searches all public keys in the keyring matching the address or address part of the user ids
|
||||
* @param {String} email email address to search for
|
||||
* @return {Array<module:key~Key>} The public keys associated with provided email address.
|
||||
*/
|
||||
Keyring.prototype.getPublicKeyForAddress = function (email) {
|
||||
return checkForIdentityAndKeyTypeMatch(this.keys, emailCheck, email, enums.packet.publicKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the keyring for a private key containing the specified email address
|
||||
* @param {String} email email address to search for
|
||||
* @return {Array<module:key~Key>} private keys found
|
||||
*/
|
||||
Keyring.prototype.getPrivateKeyForAddress = function (email) {
|
||||
return checkForIdentityAndKeyTypeMatch(this.keys, emailCheck, email, enums.packet.secretKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the keyring for a key having the specified key id
|
||||
* Searches the KeyArray for a key having the specified key id
|
||||
* @param {String} keyId provided as string of hex number (lowercase)
|
||||
* @param {Boolean} deep if true search also in subkeys
|
||||
* @return {module:key~Key|null} key found or null
|
||||
*/
|
||||
Keyring.prototype.getKeyForId = function (keyId, deep) {
|
||||
KeyArray.prototype.getForId = function (keyId, deep) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].primaryKey.getKeyId().toHex() === keyId) {
|
||||
return this.keys[i];
|
||||
}
|
||||
if (deep) {
|
||||
if (deep && this.keys[i].subKeys) {
|
||||
for (var j = 0; j < this.keys[i].subKeys.length; j++) {
|
||||
if (this.keys[i].subKeys[j].getKeyId().toHex() === keyId) {
|
||||
if (this.keys[i].subKeys[j].subKey.getKeyId().toHex() === keyId) {
|
||||
return this.keys[i];
|
||||
}
|
||||
}
|
||||
|
@ -172,14 +185,14 @@ Keyring.prototype.getKeyForId = function (keyId, deep) {
|
|||
* @param {Boolean} deep if true search also in subkeys
|
||||
* @return {module:key~Key|null} key found or null
|
||||
*/
|
||||
Keyring.prototype.getKeyForLongId = function(longKeyId, deep) {
|
||||
KeyArray.prototype.getForLongId = function(longKeyId, deep) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].primaryKey.getFingerprint() === longKeyId) {
|
||||
return this.keys[i];
|
||||
}
|
||||
if (deep) {
|
||||
if (deep && this.keys[i].subKeys) {
|
||||
for (var j = 0; j < this.keys[i].subKeys.length; j++) {
|
||||
if (this.keys[i].subKeys[j].getFingerprint() === longKeyId) {
|
||||
if (this.keys[i].subKeys[j].subKey.getFingerprint() === longKeyId) {
|
||||
return this.keys[i];
|
||||
}
|
||||
}
|
||||
|
@ -191,38 +204,57 @@ Keyring.prototype.getKeyForLongId = function(longKeyId, deep) {
|
|||
/**
|
||||
* Imports a key from an ascii armored message
|
||||
* @param {String} armored message to read the keys/key from
|
||||
* @return {Array<Error>|null} array of error objects or null
|
||||
*/
|
||||
Keyring.prototype.importKey = function (armored) {
|
||||
this.keys = this.keys.concat(keyModule.readArmored(armored).keys);
|
||||
|
||||
return true;
|
||||
KeyArray.prototype.importKey = function (armored) {
|
||||
var imported = keyModule.readArmored(armored);
|
||||
var that = this;
|
||||
imported.keys.forEach(function(key) {
|
||||
// check if key already in key array
|
||||
var keyidHex = key.primaryKey.getKeyId().toHex();
|
||||
var keyFound = that.getForId(keyidHex);
|
||||
if (keyFound) {
|
||||
keyFound.update(key);
|
||||
} else {
|
||||
that.push(key);
|
||||
}
|
||||
});
|
||||
return imported.err ? imported.err : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* returns the armored message representation of the key at key ring index
|
||||
* @param {Integer} index the index of the key within the array
|
||||
* @return {String} armored message representing the key object
|
||||
* Add key to KeyArray
|
||||
* @param {module:key~Key} key The key that will be added to the keyring
|
||||
* @return {Number} The new length of the KeyArray
|
||||
*/
|
||||
Keyring.prototype.exportKey = function (index) {
|
||||
return this.keys[index].armor();
|
||||
KeyArray.prototype.push = function (key) {
|
||||
return this.keys.push(key);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a public key from the public key keyring at the specified index
|
||||
* @param {Integer} index the index of the public key within the publicKeys array
|
||||
* @return {module:key~Key} The public key object which has been removed
|
||||
* Removes a key with the specified keyid from the keyring
|
||||
* @param {String} keyId provided as string of hex number (lowercase)
|
||||
* @return {module:key~Key|null} The key object which has been removed or null
|
||||
*/
|
||||
Keyring.prototype.removeKey = function (index) {
|
||||
var removed = this.keys.splice(index, 1);
|
||||
|
||||
return removed;
|
||||
KeyArray.prototype.removeForId = function (keyId) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].primaryKey.getKeyId().toHex() === keyId) {
|
||||
return this.keys.splice(i, 1)[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* returns the armored message representation of the public key portion of the key at key ring index
|
||||
* @param {Integer} index the index of the key within the array
|
||||
* @return {String} armored message representing the public key object
|
||||
* Removes a key with the specified long key id (fingerprint) from the keyring
|
||||
* @param {String} longKeyId fingerprint in lowercase hex
|
||||
* @return {module:key~Key|null} The key object which has been removed or null
|
||||
*/
|
||||
Keyring.prototype.exportPublicKey = function (index) {
|
||||
return this.keys[index].toPublic().armor();
|
||||
KeyArray.prototype.removeForLongId = function (longKeyId) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].primaryKey.getFingerprint() === longKeyId) {
|
||||
return this.keys.splice(i, 1)[0];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -17,56 +17,83 @@
|
|||
|
||||
/**
|
||||
* The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage.
|
||||
* @requires openpgp
|
||||
* @requires config
|
||||
* @module keyring/localstore
|
||||
* @param {String} item itemname in localstore
|
||||
* @param {String} prefix prefix for itemnames in localstore
|
||||
*/
|
||||
module.exports = LocalStore;
|
||||
|
||||
var openpgp = require('../');
|
||||
var config = require('../config'),
|
||||
keyModule = require('../key.js');
|
||||
|
||||
function LocalStore(item) {
|
||||
function LocalStore(prefix) {
|
||||
prefix = prefix || 'openpgp-';
|
||||
this.publicKeysItem = prefix + this.publicKeysItem;
|
||||
this.privateKeysItem = prefix + this.privateKeysItem;
|
||||
if (typeof window != 'undefined' && window.localStorage) {
|
||||
this.storage = window.localStorage;
|
||||
} else {
|
||||
this.storage = new (require('node-localstorage').LocalStorage)(openpgp.config.node_store);
|
||||
}
|
||||
if(typeof item == 'string') {
|
||||
this.item = item;
|
||||
this.storage = new (require('node-localstorage').LocalStorage)(config.node_store);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare the localstore itemname
|
||||
* Declare the localstore itemnames
|
||||
*/
|
||||
LocalStore.prototype.item = 'armoredKeys';
|
||||
LocalStore.prototype.publicKeysItem = 'public-keys';
|
||||
LocalStore.prototype.privateKeysItem = 'private-keys';
|
||||
|
||||
/**
|
||||
* Load the keyring from HTML5 local storage and initializes this instance.
|
||||
* Load the public keys from HTML5 local storage.
|
||||
* @return {Array<module:key~Key>} array of keys retrieved from localstore
|
||||
*/
|
||||
LocalStore.prototype.load = function () {
|
||||
var armoredKeys = JSON.parse(this.storage.getItem(this.item));
|
||||
LocalStore.prototype.loadPublic = function () {
|
||||
return loadKeys(this.storage, this.publicKeysItem);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the private keys from HTML5 local storage.
|
||||
* @return {Array<module:key~Key>} array of keys retrieved from localstore
|
||||
*/
|
||||
LocalStore.prototype.loadPrivate = function () {
|
||||
return loadKeys(this.storage, this.privateKeysItem);
|
||||
};
|
||||
|
||||
function loadKeys(storage, itemname) {
|
||||
var armoredKeys = JSON.parse(storage.getItem(itemname));
|
||||
var keys = [];
|
||||
if (armoredKeys !== null && armoredKeys.length !== 0) {
|
||||
var key;
|
||||
for (var i = 0; i < armoredKeys.length; i++) {
|
||||
key = openpgp.key.readArmored(armoredKeys[i]).keys[0];
|
||||
key = keyModule.readArmored(armoredKeys[i]).keys[0];
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current state of the public keys to HTML5 local storage.
|
||||
* The key array gets stringified using JSON
|
||||
* @param {Array<module:key~Key>} keys array of keys to save in localstore
|
||||
*/
|
||||
LocalStore.prototype.storePublic = function (keys) {
|
||||
storeKeys(this.storage, this.publicKeysItem, keys);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the current state of the keyring to HTML5 local storage.
|
||||
* The privateKeys array and publicKeys array gets Stringified using JSON
|
||||
* Saves the current state of the private keys to HTML5 local storage.
|
||||
* The key array gets stringified using JSON
|
||||
* @param {Array<module:key~Key>} keys array of keys to save in localstore
|
||||
*/
|
||||
LocalStore.prototype.store = function (keys) {
|
||||
LocalStore.prototype.storePrivate = function (keys) {
|
||||
storeKeys(this.storage, this.privateKeysItem, keys);
|
||||
};
|
||||
|
||||
function storeKeys(storage, itemname, keys) {
|
||||
var armoredKeys = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
armoredKeys.push(keys[i].armor());
|
||||
}
|
||||
this.storage.setItem(this.item, JSON.stringify(armoredKeys));
|
||||
};
|
||||
storage.setItem(itemname, JSON.stringify(armoredKeys));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user