Merge pull request #179 from toberndo/keyring_enh
Refactoring keyring, Key update method, Fixes
This commit is contained in:
commit
7cba2be2fc
148
src/key.js
148
src/key.js
|
@ -474,13 +474,13 @@ function getExpirationTime(keyPacket, selfCertificate) {
|
|||
return new Date(keyPacket.created.getTime() + selfCertificate.keyExpirationTime*1000);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns primary user and most significant (latest valid) self signature
|
||||
* - if multiple users are marked as primary users returns the one with the latest self signature
|
||||
* - if no primary user is found returns the user with the latest self signature
|
||||
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}} The primary user and the self signature
|
||||
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
|
||||
*/
|
||||
Key.prototype.getPrimaryUser = function() {
|
||||
var user = null;
|
||||
|
@ -493,9 +493,9 @@ Key.prototype.getPrimaryUser = function() {
|
|||
if (!selfCert) {
|
||||
continue;
|
||||
}
|
||||
if (!user ||
|
||||
!userSelfCert.isPrimaryUserID && selfCert.isPrimaryUserID ||
|
||||
userSelfCert.created < selfCert.created) {
|
||||
if (!user ||
|
||||
(!userSelfCert.isPrimaryUserID || selfCert.isPrimaryUserID) &&
|
||||
userSelfCert.created > selfCert.created) {
|
||||
user = this.users[i];
|
||||
userSelfCert = selfCert;
|
||||
}
|
||||
|
@ -503,6 +503,102 @@ Key.prototype.getPrimaryUser = function() {
|
|||
return user ? {user: user, selfCertificate: userSelfCert} : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update key with new components from specified key with same key ID:
|
||||
* users, subkeys, certificates are merged into the destination key,
|
||||
* duplicates are ignored.
|
||||
* If the specified key is a private key and the destination key is public,
|
||||
* the destination key is tranformed to a private key.
|
||||
* @param {module:key~Key} key source key to merge
|
||||
*/
|
||||
Key.prototype.update = function(key) {
|
||||
var that = this;
|
||||
if (key.verifyPrimaryKey() === enums.keyStatus.invalid) {
|
||||
return;
|
||||
}
|
||||
if (this.primaryKey.getFingerprint() !== key.primaryKey.getFingerprint()) {
|
||||
throw new Error('Key update method: fingerprints of keys not equal');
|
||||
}
|
||||
if (this.isPublic() && key.isPrivate()) {
|
||||
// check for equal subkey packets
|
||||
var equal = ((this.subKeys && this.subKeys.length) === (key.subKeys && key.subKeys.length)) &&
|
||||
(!this.subKeys || this.subKeys.every(function(destSubKey) {
|
||||
return key.subKeys.some(function(srcSubKey) {
|
||||
return destSubKey.subKey.getFingerprint() === srcSubKey.subKey.getFingerprint();
|
||||
});
|
||||
}));
|
||||
if (!equal) {
|
||||
throw new Error('Cannot update public key with private key if subkey mismatch');
|
||||
}
|
||||
this.primaryKey = key.primaryKey;
|
||||
}
|
||||
// revocation signature
|
||||
if (!this.revocationSignature && key.revocationSignature && !key.revocationSignature.isExpired() &&
|
||||
(key.revocationSignature.verified ||
|
||||
key.revocationSignature.verify(key.primaryKey, {key: key.primaryKey}))) {
|
||||
this.revocationSignature = key.revocationSignature;
|
||||
}
|
||||
// direct signatures
|
||||
mergeSignatures(key, this, 'directSignatures');
|
||||
// users
|
||||
key.users.forEach(function(srcUser) {
|
||||
var found = false;
|
||||
for (var i = 0; i < that.users.length; i++) {
|
||||
if (srcUser.userId && (srcUser.userId.userid === that.users[i].userId.userid) ||
|
||||
srcUser.userAttribute && (srcUser.userAttribute.equals(that.users[i].userAttribute))) {
|
||||
that.users[i].update(srcUser, that.primaryKey);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
that.users.push(srcUser);
|
||||
}
|
||||
});
|
||||
// subkeys
|
||||
if (key.subKeys) {
|
||||
key.subKeys.forEach(function(srcSubKey) {
|
||||
var found = false;
|
||||
for (var i = 0; i < that.subKeys.length; i++) {
|
||||
if (srcSubKey.subKey.getFingerprint() === that.subKeys[i].subKey.getFingerprint()) {
|
||||
that.subKeys[i].update(srcSubKey, that.primaryKey);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
that.subKeys.push(srcSubKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges signatures from source[attr] to dest[attr]
|
||||
* @private
|
||||
* @param {Object} source
|
||||
* @param {Object} dest
|
||||
* @param {String} attr
|
||||
* @param {Function} checkFn optional, signature only merged if true
|
||||
*/
|
||||
function mergeSignatures(source, dest, attr, checkFn) {
|
||||
source = source[attr];
|
||||
if (source) {
|
||||
if (!dest[attr]) {
|
||||
dest[attr] = source;
|
||||
} else {
|
||||
source.forEach(function(sourceSig) {
|
||||
if (!sourceSig.isExpired() && (!checkFn || checkFn(sourceSig)) &&
|
||||
!dest[attr].some(function(destSig) {
|
||||
return destSig.signature === sourceSig.signature;
|
||||
})) {
|
||||
dest[attr].push(sourceSig);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
Key.prototype.revoke = function() {
|
||||
|
||||
|
@ -616,6 +712,24 @@ User.prototype.verify = function(primaryKey) {
|
|||
return status;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user with new components from specified user
|
||||
* @param {module:key~User} user source user to merge
|
||||
* @param {module:packet/signature} primaryKey primary key used for validation
|
||||
*/
|
||||
User.prototype.update = function(user, primaryKey) {
|
||||
var that = this;
|
||||
// self signatures
|
||||
mergeSignatures(user, this, 'selfCertifications', function(srcSelfSig) {
|
||||
return srcSelfSig.verified ||
|
||||
srcSelfSig.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey});
|
||||
});
|
||||
// other signatures
|
||||
mergeSignatures(user, this, 'otherCertifications');
|
||||
// revocation signatures
|
||||
mergeSignatures(user, this, 'revocationCertifications');
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @classdesc Class that represents a subkey packet and the relevant signatures.
|
||||
|
@ -706,6 +820,30 @@ SubKey.prototype.getExpirationTime = function() {
|
|||
return getExpirationTime(this.subKey, this.bindingSignature);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update subkey with new components from specified subkey
|
||||
* @param {module:key~SubKey} subKey source subkey to merge
|
||||
* @param {module:packet/signature} primaryKey primary key used for validation
|
||||
*/
|
||||
SubKey.prototype.update = function(subKey, primaryKey) {
|
||||
if (this.verify(primaryKey) === enums.keyStatus.invalid) {
|
||||
return;
|
||||
}
|
||||
if (this.subKey.getFingerprint() !== subKey.subKey.getFingerprint()) {
|
||||
throw new Error('SubKey update method: fingerprints of subkeys not equal');
|
||||
}
|
||||
if (this.subKey.tag === enums.packet.publicSubkey &&
|
||||
subKey.subKey.tag === enums.packet.secretSubkey) {
|
||||
this.subKey = subKey.subKey;
|
||||
}
|
||||
// revocation signature
|
||||
if (!this.revocationSignature && subKey.revocationSignature && !subKey.revocationSignature.isExpired() &&
|
||||
(subKey.revocationSignature.verified ||
|
||||
subKey.revocationSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
|
||||
this.revocationSignature = subKey.revocationSignature;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads an OpenPGP armored text and returns one or multiple key objects
|
||||
* @param {String} armoredText text to be parsed
|
||||
|
|
|
@ -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,25 +37,87 @@ 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 lowercase hex number
|
||||
* withouth 0x prefix (can be 16-character key ID or fingerprint)
|
||||
* @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 lowercase hex number
|
||||
* withouth 0x prefix (can be 16-character key ID or fingerprint)
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks a key to see if it matches the specified email address
|
||||
* @private
|
||||
* @param {String} email email address 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
|
||||
|
@ -83,111 +137,84 @@ 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
|
||||
* @private
|
||||
* @param {String} keyId provided as string of lowercase hex number
|
||||
* withouth 0x prefix (can be 16-character key ID or fingerprint)
|
||||
* @param {module:packet/secret_key|public_key|public_subkey|secret_subkey} keypacket The keypacket to be checked
|
||||
* @return {Boolean} True if keypacket has the specified keyid
|
||||
*/
|
||||
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;
|
||||
}
|
||||
function keyIdCheck(keyId, keypacket) {
|
||||
if (keyId.length === 16) {
|
||||
return keyId === keypacket.getKeyId().toHex();
|
||||
} else {
|
||||
return keyId === keypacket.getFingerprint();
|
||||
}
|
||||
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
|
||||
* Searches the KeyArray for a key having the specified key id
|
||||
* @param {String} keyId provided as string of lowercase hex number
|
||||
* withouth 0x prefix (can be 16-character key ID or fingerprint)
|
||||
* @param {Boolean} deep if true search also in subkeys
|
||||
* @return {module:key~Key|null} key found or null
|
||||
*/
|
||||
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);
|
||||
KeyArray.prototype.getForId = function (keyId, deep) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (keyIdCheck(keyId, this.keys[i].primaryKey)) {
|
||||
return this.keys[i];
|
||||
}
|
||||
if (deep && this.keys[i].subKeys) {
|
||||
for (var j = 0; j < this.keys[i].subKeys.length; j++) {
|
||||
if (keyIdCheck(keyId, this.keys[i].subKeys[j].subKey)) {
|
||||
return this.keys[i];
|
||||
}
|
||||
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 public keys having the specified key id
|
||||
* @param {String} keyId provided as string of hex number (lowercase)
|
||||
* @return {Array<module:key~Key>} public keys found
|
||||
*/
|
||||
Keyring.prototype.getKeysForKeyId = function (keyId) {
|
||||
return checkForIdentityAndKeyTypeMatch(this.keys, idCheck, keyId, enums.packet.publicKey);
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 lowercase hex number
|
||||
* withouth 0x prefix (can be 16-character key ID or fingerprint)
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Keyring.prototype.exportPublicKey = function (index) {
|
||||
return this.keys[index].toPublic().armor();
|
||||
KeyArray.prototype.removeForId = function (keyId) {
|
||||
for (var i = 0; i < this.keys.length; i++) {
|
||||
if (keyIdCheck(keyId, this.keys[i].primaryKey)) {
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -55,6 +55,16 @@ function PublicKey() {
|
|||
this.algorithm = 'rsa_sign';
|
||||
// time in days (V3 only)
|
||||
this.expirationTimeV3 = 0;
|
||||
/**
|
||||
* Fingerprint in lowercase hex
|
||||
* @type {String}
|
||||
*/
|
||||
this.fingerprint = null;
|
||||
/**
|
||||
* Keyid
|
||||
* @type {module:type/keyid}
|
||||
*/
|
||||
this.keyid = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,31 +168,39 @@ PublicKey.prototype.writeOld = function () {
|
|||
* @return {String} A 8 byte key id
|
||||
*/
|
||||
PublicKey.prototype.getKeyId = function () {
|
||||
var keyid = new type_keyid();
|
||||
if (this.version == 4) {
|
||||
keyid.read(this.getFingerprint().substr(12, 8));
|
||||
} else if (this.version == 3) {
|
||||
keyid.read(this.mpi[0].write().substr(-8));
|
||||
if (this.keyid) {
|
||||
return this.keyid;
|
||||
}
|
||||
return keyid;
|
||||
this.keyid = new type_keyid();
|
||||
if (this.version == 4) {
|
||||
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
|
||||
} else if (this.version == 3) {
|
||||
this.keyid.read(this.mpi[0].write().substr(-8));
|
||||
}
|
||||
return this.keyid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates the fingerprint of the key
|
||||
* @return {String} A string containing the fingerprint
|
||||
* @return {String} A string containing the fingerprint in lowercase hex
|
||||
*/
|
||||
PublicKey.prototype.getFingerprint = function () {
|
||||
if (this.fingerprint) {
|
||||
return this.fingerprint;
|
||||
}
|
||||
var toHash = '';
|
||||
if (this.version == 4) {
|
||||
toHash = this.writeOld();
|
||||
return crypto.hash.sha1(toHash);
|
||||
this.fingerprint = crypto.hash.sha1(toHash);
|
||||
} else if (this.version == 3) {
|
||||
var mpicount = crypto.getPublicMpiCount(this.algorithm);
|
||||
for (var i = 0; i < mpicount; i++) {
|
||||
toHash += this.mpi[i].toBytes();
|
||||
}
|
||||
return crypto.hash.md5(toHash);
|
||||
this.fingerprint = crypto.hash.md5(toHash);
|
||||
}
|
||||
this.fingerprint = util.hexstrdump(this.fingerprint);
|
||||
return this.fingerprint;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -200,4 +218,7 @@ PublicKey.prototype.postCloneTypeFix = function() {
|
|||
for (var i = 0; i < this.mpi.length; i++) {
|
||||
this.mpi[i] = type_mpi.fromClone(this.mpi[i]);
|
||||
}
|
||||
if (this.keyid) {
|
||||
this.keyid = type_keyid.fromClone(this.keyid);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -51,6 +51,7 @@ function Signature() {
|
|||
this.publicKeyAlgorithm = null;
|
||||
|
||||
this.signatureData = null;
|
||||
this.unhashedSubpackets = null;
|
||||
this.signedHashValue = null;
|
||||
|
||||
this.created = new Date();
|
||||
|
@ -166,9 +167,11 @@ Signature.prototype.read = function (bytes) {
|
|||
// hash algorithm, the hashed subpacket length, and the hashed
|
||||
// subpacket body.
|
||||
this.signatureData = bytes.substr(0, i);
|
||||
var sigDataLength = i;
|
||||
|
||||
// unhashed subpackets
|
||||
i += subpackets.call(this, bytes.substr(i), false);
|
||||
this.unhashedSubpackets = bytes.substr(sigDataLength, i - sigDataLength);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
@ -184,7 +187,8 @@ Signature.prototype.read = function (bytes) {
|
|||
|
||||
Signature.prototype.write = function () {
|
||||
return this.signatureData +
|
||||
util.writeNumber(0, 2) + // Number of unsigned subpackets.
|
||||
// unhashed subpackets or two octets for zero
|
||||
(this.unhashedSubpackets ? this.unhashedSubpackets : util.writeNumber(0, 2)) +
|
||||
this.signedHashValue +
|
||||
this.signature;
|
||||
};
|
||||
|
@ -559,7 +563,7 @@ Signature.prototype.toSign = function (type, data) {
|
|||
|
||||
case t.key:
|
||||
if (data.key === undefined)
|
||||
throw new Error('Key packet is required for this sigtature.');
|
||||
throw new Error('Key packet is required for this signature.');
|
||||
|
||||
return data.key.writeOld();
|
||||
|
||||
|
|
|
@ -64,3 +64,17 @@ UserAttribute.prototype.read = function(bytes) {
|
|||
i += len.len;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare for equality
|
||||
* @param {module:user_attribute~UserAttribute} usrAttr
|
||||
* @return {Boolean} true if equal
|
||||
*/
|
||||
UserAttribute.prototype.equals = function(usrAttr) {
|
||||
if (!usrAttr || !(usrAttr instanceof UserAttribute)) {
|
||||
return false;
|
||||
}
|
||||
return this.attributes.every(function(attr, index) {
|
||||
return attr === usrAttr.attributes[index];
|
||||
});
|
||||
};
|
||||
|
|
|
@ -221,6 +221,48 @@ describe('Key', function() {
|
|||
'=e8xo',
|
||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
||||
|
||||
var priv_key_rsa =
|
||||
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
|
||||
'Version: GnuPG v2.0.19 (GNU/Linux)',
|
||||
'Type: RSA/RSA 1024',
|
||||
'Pwd: hello world',
|
||||
'',
|
||||
'lQH+BFJhL04BBADclrUEDDsm0PSZbQ6pml9FpzTyXiyCyDN+rMOsy9J300Oc10kt',
|
||||
'/nyBej9vZSRcaW5VpNNj0iA+c1/w2FPf84zNsTzvDmuMaNHFUzky4/vkYuZra//3',
|
||||
'+Ri7CF8RawSYQ/4IRbC9zqdBlzniyfQOW7Dp/LYe8eibnDSrmkQem0G0jwARAQAB',
|
||||
'/gMDAu7L//czBpE40p1ZqO8K3k7UejemjsQqc7kOqnlDYd1Z6/3NEA/UM30Siipr',
|
||||
'KjdIFY5+hp0hcs6EiiNq0PDfm/W2j+7HfrZ5kpeQVxDek4irezYZrl7JS2xezaLv',
|
||||
'k0Fv/6fxasnFtjOM6Qbstu67s5Gpl9y06ZxbP3VpT62+Xeibn/swWrfiJjuGEEhM',
|
||||
'bgnsMpHtzAz/L8y6KSzViG/05hBaqrvk3/GeEA6nE+o0+0a6r0LYLTemmq6FbaA1',
|
||||
'PHo+x7k7oFcBFUUeSzgx78GckuPwqr2mNfeF+IuSRnrlpZl3kcbHASPAOfEkyMXS',
|
||||
'sWGE7grCAjbyQyM3OEXTSyqnehvGS/1RdB6kDDxGwgE/QFbwNyEh6K4eaaAThW2j',
|
||||
'IEEI0WEnRkPi9fXyxhFsCLSI1XhqTaq7iDNqJTxE+AX2b9ZuZXAxI3Tc/7++vEyL',
|
||||
'3p18N/MB2kt1Wb1azmXWL2EKlT1BZ5yDaJuBQ8BhphM3tCRUZXN0IE1jVGVzdGlu',
|
||||
'Z3RvbiA8dGVzdEBleGFtcGxlLmNvbT6IuQQTAQIAIwUCUmEvTgIbLwcLCQgHAwIB',
|
||||
'BhUIAgkKCwQWAgMBAh4BAheAAAoJEEpjYTpNbkCUMAwD+gIK08qpEZSVas9qW+Ok',
|
||||
'32wzNkwxe6PQgZwcyBqMQYZUcKagC8+89pMQQ5sKUGvpIgat42Tf1KLGPcvG4cDA',
|
||||
'JZ6w2PYz9YHQqPh9LA+PAnV8m25TcGmKcKgvFUqQ3U53X/Y9sBP8HooRqfwwHcv9',
|
||||
'pMgQmojmNbI4VHydRqIBePawnQH+BFJhL04BBADpH8+0EVolpPiOrXTKoBKTiyrB',
|
||||
'UyxzodyJ8zmVJ3HMTEU/vidpQwzISwoc/ndDFMXQauq6xqBCD9m2BPQI3UdQzXnb',
|
||||
'LsAI52nWCIqOkzM5NAKWoKhyXK9Y4UH4v9LAYQgl/stIISvCgG4mJ8lzzEBWvRdf',
|
||||
'Qm2Ghb64/3V5NDdemwARAQAB/gMDAu7L//czBpE40iPcpLzL7GwBbWFhSWgSLy53',
|
||||
'Md99Kxw3cApWCok2E8R9/4VS0490xKZIa5y2I/K8thVhqk96Z8Kbt7MRMC1WLHgC',
|
||||
'qJvkeQCI6PrFM0PUIPLHAQtDJYKtaLXxYuexcAdKzZj3FHdtLNWCooK6n3vJlL1c',
|
||||
'WjZcHJ1PH7USlj1jup4XfxsbziuysRUSyXkjn92GZLm+64vCIiwhqAYoizF2NHHG',
|
||||
'hRTN4gQzxrxgkeVchl+ag7DkQUDANIIVI+A63JeLJgWJiH1fbYlwESByHW+zBFNt',
|
||||
'qStjfIOhjrfNIc3RvsggbDdWQLcbxmLZj4sB0ydPSgRKoaUdRHJY0S4vp9ouKOtl',
|
||||
'2au/P1BP3bhD0fDXl91oeheYth+MSmsJFDg/vZJzCJhFaQ9dp+2EnjN5auNCNbaI',
|
||||
'beFJRHFf9cha8p3hh+AK54NRCT++B2MXYf+TPwqX88jYMBv8kk8vYUgo8128r1zQ',
|
||||
'EzjviQE9BBgBAgAJBQJSYS9OAhsuAKgJEEpjYTpNbkCUnSAEGQECAAYFAlJhL04A',
|
||||
'CgkQ4IT3RGwgLJe6ogQA2aaJEIBIXtgrs+8WSJ4k3DN4rRXcXaUZf667pjdD9nF2',
|
||||
'3BzjFH6Z78JIGaxRHJdM7b05aE8YuzM8f3NIlT0F0OLq/TI2muYU9f/U2DQBuf+w',
|
||||
'KTB62+PELVgi9MsXC1Qv/u/o1LZtmmxTFFOD35xKsxZZI2OJj2pQpqObW27M8Nlc',
|
||||
'BQQAw2YA3fFc38qPK+PY4rZyTRdbvjyyX+1zeqIo8wn7QCQwXs+OGaH2fGoT35AI',
|
||||
'SXuqKcWqoEuO7OBSEFThCXBfUYMC01OrqKEswPm/V3zZkLu01q12UMwZach28QwK',
|
||||
'/YZly4ioND2tdazj17u2rU2dwtiHPe1iMqGgVMoQirfLc+k=',
|
||||
'=lw5e',
|
||||
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
|
||||
|
||||
it('Parsing armored text with two keys', function(done) {
|
||||
var pubKeys = openpgp.key.readArmored(twoKeys);
|
||||
expect(pubKeys).to.exist;
|
||||
|
@ -250,9 +292,9 @@ describe('Key', function() {
|
|||
expect(pubKeyV3).to.exist;
|
||||
|
||||
expect(pubKeyV4.getKeyPacket().getKeyId().toHex()).to.equal('4a63613a4d6e4094');
|
||||
expect(openpgp.util.hexstrdump(pubKeyV4.getKeyPacket().getFingerprint())).to.equal('f470e50dcb1ad5f1e64e08644a63613a4d6e4094');
|
||||
expect(pubKeyV4.getKeyPacket().getFingerprint()).to.equal('f470e50dcb1ad5f1e64e08644a63613a4d6e4094');
|
||||
expect(pubKeyV3.getKeyPacket().getKeyId().toHex()).to.equal('e5b7a014a237ba9d');
|
||||
expect(openpgp.util.hexstrdump(pubKeyV3.getKeyPacket().getFingerprint())).to.equal('a44fcee620436a443bc4913640ab3e49');
|
||||
expect(pubKeyV3.getKeyPacket().getFingerprint()).to.equal('a44fcee620436a443bc4913640ab3e49');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -322,5 +364,95 @@ describe('Key', function() {
|
|||
expect(pubKey.subKeys[0].getExpirationTime().toISOString()).to.be.equal('2018-11-26T10:58:29.000Z');
|
||||
});
|
||||
|
||||
it('update() - throw error if fingerprints not equal', function() {
|
||||
var keys = openpgp.key.readArmored(twoKeys).keys;
|
||||
expect(keys[0].update.bind(keys[0], keys[1])).to.throw('Key update method: fingerprints of keys not equal');
|
||||
});
|
||||
|
||||
it('update() - merge revocation signature', function() {
|
||||
var source = openpgp.key.readArmored(pub_revoked).keys[0];
|
||||
var dest = openpgp.key.readArmored(pub_revoked).keys[0];
|
||||
expect(source.revocationSignature).to.exist;
|
||||
dest.revocationSignature = null;
|
||||
dest.update(source);
|
||||
expect(dest.revocationSignature).to.exist.and.be.an.instanceof(openpgp.packet.Signature);
|
||||
});
|
||||
|
||||
it('update() - merge user', function() {
|
||||
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
expect(source.users[1]).to.exist;
|
||||
dest.users.pop();
|
||||
dest.update(source);
|
||||
expect(dest.users[1]).to.exist;
|
||||
expect(dest.users[1].userId).to.equal(source.users[1].userId);
|
||||
});
|
||||
|
||||
it('update() - merge user - other and revocation certification', function() {
|
||||
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
expect(source.users[1].otherCertifications).to.exist;
|
||||
expect(source.users[1].revocationCertifications).to.exist;
|
||||
dest.users[1].otherCertifications = null;
|
||||
dest.users[1].revocationCertifications.pop();
|
||||
dest.update(source);
|
||||
expect(dest.users[1].otherCertifications).to.exist.and.to.have.length(1);
|
||||
expect(dest.users[1].otherCertifications[0].signature).to.equal(source.users[1].otherCertifications[0].signature);
|
||||
expect(dest.users[1].revocationCertifications).to.exist.and.to.have.length(2);
|
||||
expect(dest.users[1].revocationCertifications[1].signature).to.equal(source.users[1].revocationCertifications[1].signature);
|
||||
});
|
||||
|
||||
it('update() - merge subkey', function() {
|
||||
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
expect(source.subKeys[1]).to.exist;
|
||||
dest.subKeys.pop();
|
||||
dest.update(source);
|
||||
expect(dest.subKeys[1]).to.exist;
|
||||
expect(dest.subKeys[1].subKey.getKeyId().toHex()).to.equal(source.subKeys[1].subKey.getKeyId().toHex());
|
||||
});
|
||||
|
||||
it('update() - merge subkey - revocation signature', function() {
|
||||
var source = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
var dest = openpgp.key.readArmored(pub_sig_test).keys[0];
|
||||
expect(source.subKeys[0].revocationSignature).to.exist;
|
||||
dest.subKeys[0].revocationSignature = null;
|
||||
dest.update(source);
|
||||
expect(dest.subKeys[0].revocationSignature).to.exist;
|
||||
expect(dest.subKeys[0].revocationSignature.signature).to.equal(dest.subKeys[0].revocationSignature.signature);
|
||||
});
|
||||
|
||||
it('update() - merge private key into public key', function() {
|
||||
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
|
||||
var dest = openpgp.key.readArmored(twoKeys).keys[0];
|
||||
expect(dest.isPublic()).to.be.true;
|
||||
dest.update(source);
|
||||
expect(dest.isPrivate()).to.be.true;
|
||||
expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey());
|
||||
expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey));
|
||||
expect(source.subKeys[0].verify(source.primaryKey)).to.equal(dest.subKeys[0].verify(dest.primaryKey));
|
||||
});
|
||||
|
||||
it('update() - merge private key into public key - no subkeys', function() {
|
||||
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
|
||||
var dest = openpgp.key.readArmored(twoKeys).keys[0];
|
||||
source.subKeys = null;
|
||||
dest.subKeys = null;
|
||||
expect(dest.isPublic()).to.be.true;
|
||||
dest.update(source);
|
||||
expect(dest.isPrivate()).to.be.true;
|
||||
expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey());
|
||||
expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey));
|
||||
});
|
||||
|
||||
it('update() - merge private key into public key - mismatch throws error', function() {
|
||||
var source = openpgp.key.readArmored(priv_key_rsa).keys[0];
|
||||
var dest = openpgp.key.readArmored(twoKeys).keys[0];
|
||||
source.subKeys = null;
|
||||
expect(dest.subKeys).to.exist;
|
||||
expect(dest.isPublic()).to.be.true;
|
||||
expect(dest.update.bind(dest, source)).to.throw('Cannot update public key with private key if subkey mismatch');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ describe("Keyring", function() {
|
|||
var user = 'whiteout.test@t-online.de',
|
||||
passphrase = 'asdf',
|
||||
keySize = 512,
|
||||
keyId = 'F6F60E9B42CDFF4C',
|
||||
keyId = 'f6f60e9b42cdff4c',
|
||||
keyFingerP = '5856cef789c3a307e8a1b976f6f60e9b42cdff4c',
|
||||
pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\n' +
|
||||
'Version: OpenPGP.js v.1.20131011\n' +
|
||||
'Comment: http://openpgpjs.org\n' +
|
||||
|
@ -37,65 +38,243 @@ describe("Keyring", function() {
|
|||
'Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXqIiN602mWrkd8jcEzLsW5\n' +
|
||||
'IUNzVPLhrFIuKyBDTpLnC07Loce1\n' +
|
||||
'=ULta\n' +
|
||||
'-----END PGP PRIVATE KEY BLOCK-----';
|
||||
'-----END PGP PRIVATE KEY BLOCK-----',
|
||||
keyId2 = 'ba993fc2aee18a3a',
|
||||
keyFingerP2 = '560b7a7f3f9ab516b233b299ba993fc2aee18a3a',
|
||||
subkeyId2 = 'f47c5210a8cc2740',
|
||||
subkeyFingerP2 = '2a20c371141e000833848d85f47c5210a8cc2740',
|
||||
pubkey2 =
|
||||
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||
'Version: GnuPG v2.0.22 (GNU/Linux)',
|
||||
'',
|
||||
'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',
|
||||
'og2umGfGng==',
|
||||
'=v3+L',
|
||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
||||
|
||||
it('Import key pair', function(done) {
|
||||
it('Import key pair', function() {
|
||||
// clear any keys already in the keychain
|
||||
keyring.clear();
|
||||
keyring.importKey(privkey);
|
||||
keyring.importKey(pubkey);
|
||||
done();
|
||||
keyring.store();
|
||||
keyring.publicKeys.importKey(pubkey);
|
||||
keyring.publicKeys.importKey(pubkey2);
|
||||
keyring.privateKeys.importKey(privkey);
|
||||
});
|
||||
|
||||
it('getPublicKeyForAddress() - unknown address', function(done) {
|
||||
var key = keyring.getPublicKeyForAddress('nobody@example.com');
|
||||
expect(key).to.be.empty;
|
||||
done();
|
||||
it('getKeysForId() - unknown id', function() {
|
||||
var keys = keyring.getKeysForId('01234567890123456');
|
||||
expect(keys).to.be.null;
|
||||
});
|
||||
it('getPublicKeyForAddress() - valid address', function(done) {
|
||||
var key = keyring.getPublicKeyForAddress(user);
|
||||
expect(key).to.exist.and.have.length(1);
|
||||
done();
|
||||
|
||||
it('getKeysForId() - valid id', function() {
|
||||
var keys = keyring.getKeysForId(keyId);
|
||||
// we get public and private key
|
||||
expect(keys).to.exist.and.have.length(2);
|
||||
expect(keys[0].primaryKey.getKeyId().toHex()).equals(keyId);
|
||||
});
|
||||
it('getPrivateKeyForAddress() - unknown address', function(done) {
|
||||
var key = keyring.getPrivateKeyForAddress('nobody@example.com');
|
||||
expect(key).to.be.empty;
|
||||
done();
|
||||
|
||||
it('publicKeys.getForId() - unknown id', function() {
|
||||
var key = keyring.publicKeys.getForId('01234567890123456');
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
it('getPrivateKeyForAddress() - valid address', function(done) {
|
||||
var key = keyring.getPrivateKeyForAddress(user);
|
||||
expect(key).to.exist.and.have.length(1);
|
||||
done();
|
||||
|
||||
it('publicKeys.getForId() - valid id', function() {
|
||||
var key = keyring.publicKeys.getForId(keyId);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
|
||||
});
|
||||
it('getKeysForKeyId() - unknown id', function(done) {
|
||||
var keys = keyring.getKeysForKeyId('000102030405060708');
|
||||
|
||||
it('privateKeys.getForId() - unknown id', function() {
|
||||
var key = keyring.privateKeys.getForId('01234567890123456');
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
|
||||
it('privateKeys.getForId() - valid id', function() {
|
||||
var key = keyring.privateKeys.getForId(keyId);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - subkey id', function() {
|
||||
var key = keyring.publicKeys.getForId(subkeyId2);
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - deep, including subkeys - subkey id', function() {
|
||||
var key = keyring.publicKeys.getForId(subkeyId2, true);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
|
||||
});
|
||||
|
||||
it('getKeysForId() - unknown fingerprint', function() {
|
||||
var keys = keyring.getKeysForId('71130e8383bef9526e062600d5e9f93acbbc7275');
|
||||
expect(keys).to.be.null;
|
||||
});
|
||||
|
||||
it('getKeysForId() - valid fingerprint', function() {
|
||||
var keys = keyring.getKeysForId(keyFingerP2);
|
||||
expect(keys).to.exist.and.have.length(1);
|
||||
expect(keys[0].primaryKey.getKeyId().toHex()).equals(keyId2);
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - unknown fingerprint', function() {
|
||||
var key = keyring.publicKeys.getForId('71130e8383bef9526e062600d5e9f93acbbc7275');
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - valid fingerprint', function() {
|
||||
var key = keyring.publicKeys.getForId(keyFingerP2);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - subkey fingerprint', function() {
|
||||
var key = keyring.publicKeys.getForId(subkeyFingerP2);
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
|
||||
it('publicKeys.getForId() - deep, including subkeys - subkey fingerprint', function() {
|
||||
var key = keyring.publicKeys.getForId(subkeyFingerP2, true);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
|
||||
});
|
||||
|
||||
it('publicKeys.getForAddress() - unknown address', function() {
|
||||
var keys = keyring.publicKeys.getForAddress('nobody@example.com');
|
||||
expect(keys).to.be.empty;
|
||||
done();
|
||||
});
|
||||
it('getKeysForKeyId() - valid id', function(done) {
|
||||
var keys = keyring.getKeysForKeyId(keyId.toLowerCase());
|
||||
|
||||
it('publicKeys.getForAddress() - valid address', function() {
|
||||
var keys = keyring.publicKeys.getForAddress(user);
|
||||
expect(keys).to.exist.and.have.length(1);
|
||||
done();
|
||||
});
|
||||
it('store keys in localstorage', function(done){
|
||||
|
||||
it('privateKeys.getForAddress() - unknown address', function() {
|
||||
var key = keyring.privateKeys.getForAddress('nobody@example.com');
|
||||
expect(key).to.be.empty;
|
||||
});
|
||||
|
||||
it('privateKeys.getForAddress() - valid address', function() {
|
||||
var key = keyring.privateKeys.getForAddress(user);
|
||||
expect(key).to.exist.and.have.length(1);
|
||||
});
|
||||
|
||||
it('store keys in localstorage', function(){
|
||||
keyring.store();
|
||||
done();
|
||||
});
|
||||
it('after loading from localstorage: getKeysForKeyId() - valid id', function(done) {
|
||||
|
||||
it('after loading from localstorage: getKeysForKeyId() - valid id', function() {
|
||||
var keyring = new openpgp.Keyring(),
|
||||
keys = keyring.getKeysForKeyId(keyId.toLowerCase());
|
||||
expect(keys).to.exist.and.have.length(1);
|
||||
done();
|
||||
keys = keyring.getKeysForId(keyId);
|
||||
// we expect public and private key
|
||||
expect(keys).to.exist.and.have.length(2);
|
||||
});
|
||||
|
||||
it('publicKeys.removeForId() - unknown id', function() {
|
||||
var key = keyring.publicKeys.removeForId('01234567890123456');
|
||||
expect(key).to.be.null;
|
||||
});
|
||||
|
||||
it('publicKeys.removeForId() - valid id', function() {
|
||||
var key = keyring.publicKeys.removeForId(keyId);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId);
|
||||
expect(keyring.publicKeys.keys).to.exist.and.have.length(1);
|
||||
});
|
||||
|
||||
it('publicKeys.removeForId() - unknown fingerprint', function() {
|
||||
var key = keyring.publicKeys.removeForId('71130e8383bef9526e062600d5e9f93acbbc7275');
|
||||
expect(key).to.be.null;
|
||||
expect(keyring.publicKeys.keys).to.exist.and.have.length(1);
|
||||
});
|
||||
|
||||
it('publicKeys.removeForId() - valid fingerprint', function() {
|
||||
var key = keyring.publicKeys.removeForId(keyFingerP2);
|
||||
expect(key).to.exist.and.be.an.instanceof(openpgp.key.Key);
|
||||
expect(key.primaryKey.getKeyId().toHex()).equals(keyId2);
|
||||
expect(keyring.publicKeys.keys).to.be.empty;
|
||||
});
|
||||
|
||||
it('customize localstorage itemname', function() {
|
||||
var localstore1 = new openpgp.Keyring.localstore('my-custom-name');
|
||||
var localstore2 = new openpgp.Keyring.localstore('my-custom-name');
|
||||
var localstore1 = new openpgp.Keyring.localstore('my-custom-prefix-');
|
||||
var localstore2 = new openpgp.Keyring.localstore('my-custom-prefix-');
|
||||
var localstore3 = new openpgp.Keyring.localstore();
|
||||
localstore3.store([]);
|
||||
localstore3.storePublic([]);
|
||||
var key = openpgp.key.readArmored(pubkey).keys[0];
|
||||
localstore1.store([key]);
|
||||
expect(localstore2.load()[0].primaryKey.getKeyId().equals(key.primaryKey.getKeyId())).to.be.true;
|
||||
expect(localstore3.load()).to.have.length(0);
|
||||
localstore1.storePublic([key]);
|
||||
expect(localstore2.loadPublic()[0].primaryKey.getKeyId().equals(key.primaryKey.getKeyId())).to.be.true;
|
||||
expect(localstore3.loadPublic()).to.have.length(0);
|
||||
});
|
||||
|
||||
it('removeKeysForId() - unknown id', function() {
|
||||
keyring.publicKeys.importKey(pubkey);
|
||||
keyring.publicKeys.importKey(pubkey2);
|
||||
keyring.privateKeys.importKey(privkey);
|
||||
expect(keyring.publicKeys.keys).to.have.length(2);
|
||||
expect(keyring.privateKeys.keys).to.have.length(1);
|
||||
var keys = keyring.removeKeysForId('01234567890123456');
|
||||
expect(keys).to.be.null;
|
||||
expect(keyring.publicKeys.keys).to.have.length(2);
|
||||
expect(keyring.privateKeys.keys).to.have.length(1);
|
||||
});
|
||||
|
||||
it('removeKeysForId() - valid id', function() {
|
||||
var keys = keyring.removeKeysForId(keyId);
|
||||
expect(keys).to.have.length(2);
|
||||
expect(keyring.publicKeys.keys).to.have.length(1);
|
||||
expect(keyring.privateKeys.keys).to.have.length(0);
|
||||
});
|
||||
|
||||
it('removeKeysForId() - unknown fingerprint', function() {
|
||||
keyring.publicKeys.importKey(pubkey);
|
||||
keyring.publicKeys.importKey(pubkey2);
|
||||
keyring.privateKeys.importKey(privkey);
|
||||
expect(keyring.publicKeys.keys).to.have.length(2);
|
||||
expect(keyring.privateKeys.keys).to.have.length(1);
|
||||
var keys = keyring.removeKeysForId('71130e8383bef9526e062600d5e9f93acbbc7275');
|
||||
expect(keys).to.be.null;
|
||||
expect(keyring.publicKeys.keys).to.have.length(2);
|
||||
expect(keyring.privateKeys.keys).to.have.length(1);
|
||||
});
|
||||
|
||||
it('removeKeysForId() - valid fingerprint', function() {
|
||||
var keys = keyring.removeKeysForId(keyFingerP);
|
||||
expect(keys).to.have.length(2);
|
||||
expect(keyring.publicKeys.keys).to.have.length(1);
|
||||
expect(keyring.privateKeys.keys).to.have.length(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
|
|
@ -571,4 +571,12 @@ describe("Signature", function() {
|
|||
expect(verified).to.be.true;
|
||||
done();
|
||||
});
|
||||
|
||||
it('Write unhashed subpackets', function() {
|
||||
var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
|
||||
expect(pubKey.users[0].selfCertifications).to.exist;
|
||||
pubKey = openpgp.key.readArmored(pubKey.armor()).keys[0]
|
||||
expect(pubKey.users[0].selfCertifications).to.exist;
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user