Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623326
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
30 KB
Subscribers
None
View Options
diff --git a/lang/js/DemoExtension/maindemo.js b/lang/js/DemoExtension/maindemo.js
index d0127c73..4cae934e 100644
--- a/lang/js/DemoExtension/maindemo.js
+++ b/lang/js/DemoExtension/maindemo.js
@@ -1,102 +1,119 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1+
*
* Author(s):
* Maximilian Krambach <mkrambach@intevation.de>
*/
/* global document, Gpgmejs */
document.addEventListener('DOMContentLoaded', function() {
Gpgmejs.init().then(function(gpgmejs){
document.getElementById('buttonencrypt').addEventListener('click',
function(){
let data = document.getElementById('inputtext').value;
let keyId = document.getElementById('pubkey').value;
gpgmejs.encrypt(data, keyId).then(
function(answer){
if (answer.data){
document.getElementById(
'answer').value = answer.data;
}
}, function(errormsg){
alert( errormsg.message);
});
});
document.getElementById('buttondecrypt').addEventListener('click',
function(){
let data = document.getElementById('inputtext').value;
gpgmejs.decrypt(data).then(
function(answer){
if (answer.data){
document.getElementById(
'answer').value = answer.data;
}
}, function(errormsg){
alert(errormsg.message);
});
});
document.getElementById('getdefaultkey').addEventListener('click',
function(){
gpgmejs.Keyring.getDefaultKey().then(function(answer){
document.getElementById('pubkey').value =
answer.fingerprint;
}, function(errormsg){
alert(errormsg.message);
});
});
document.getElementById('signtext').addEventListener('click',
function(){
let data = document.getElementById('inputtext').value;
let keyId = document.getElementById('pubkey').value;
gpgmejs.sign(data, keyId).then(
function(answer){
if (answer.data){
document.getElementById(
'answer').value = answer.data;
}
}, function(errormsg){
alert( errormsg.message);
});
});
document.getElementById('verifytext').addEventListener('click',
function(){
let data = document.getElementById('inputtext').value;
gpgmejs.verify(data).then(
function(answer){
let vals = '';
if (answer.all_valid === true){
vals = 'Success! ';
} else {
vals = 'Failure! ';
}
vals = vals + (answer.count - answer.failures) + 'of '
+ answer.count + ' signature(s) were successfully '
+ 'verified.\n\n' + answer.data;
document.getElementById('answer').value = vals;
}, function(errormsg){
alert( errormsg.message);
});
});
+ document.getElementById('searchkey').addEventListener('click',
+ function(){
+ let data = document.getElementById('inputtext').value;
+ gpgmejs.Keyring.getKeys(data, true, true).then(function(keys){
+ if (keys.length === 1){
+ document.getElementById(
+ 'pubkey').value = keys[0].fingerprint;
+ } else if (keys.length > 1) {
+ alert('The pattern was not unambigious enough for a Key. '
+ + keys.length + ' Keys were found');
+ } else {
+ alert('No keys found');
+ }
+ }, function(errormsg){
+ alert( errormsg.message);
+ });
+ });
});
});
diff --git a/lang/js/DemoExtension/mainui.html b/lang/js/DemoExtension/mainui.html
index b6390363..c773c9b9 100644
--- a/lang/js/DemoExtension/mainui.html
+++ b/lang/js/DemoExtension/mainui.html
@@ -1,43 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="ui.css"/>
<script src="libs/gpgmejs.bundle.js"></script>
<script src="maindemo.js"></script>
</head>
<body>
<div>
<div class="left">
<ul>
<li>
<span class="label">Input</span>
<textarea rows="5" cols="65" id="inputtext" wrap="hard"></textarea>
</li>
<li>
<span class="label">Fingerprint of Key to use: </span>
- </li>
- <input type="text" id="pubkey" value="" /> <br>
- <button id="getdefaultkey">Set to default signing key</button>
+ <input type="text" id="pubkey" value="" />
+ <button id="getdefaultkey">
+ Set to default signing key
+ </button>
+ <button id="searchkey">
+ Look up Key
+ </button>
</li>
</ul>
</div>
<div class="right">
<ul>
<li>
<span class="label">Result</span>
<textarea id="answer" rows="5" cols="65" wrap="hard"></textarea>
</li>
</ul>
</div>
</div>
<div class="center">
<button id="buttonencrypt">Encrypt input text</button><br>
<button id="buttondecrypt">Decrypt input text</button><br>
<button id="signtext">Sign input text</button> <br>
<button id="verifytext">Verify input text</button><br>
</div>
</body>
</html>
diff --git a/lang/js/src/Keyring.js b/lang/js/src/Keyring.js
index 8bec1cea..0d7643f0 100644
--- a/lang/js/src/Keyring.js
+++ b/lang/js/src/Keyring.js
@@ -1,327 +1,332 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1+
*
* Author(s):
* Maximilian Krambach <mkrambach@intevation.de>
*/
import {createMessage} from './Message';
import {createKey} from './Key';
import { isFingerprint } from './Helpers';
import { gpgme_error } from './Errors';
export class GPGME_Keyring {
constructor(){
}
/**
* @param {String} pattern (optional) pattern A pattern to search for,
* in userIds or KeyIds
* @param {Boolean} prepare_sync (optional, default true) if set to true,
* Key.armor and Key.hasSecret will be called, so they can be used
* inmediately. This allows for full synchronous use. If set to false,
* these will initially only be available as Promises in getArmor() and
* getHasSecret()
+ * @param {Boolean} search (optional) retrieve the Keys from servers with
+ * the method(s) defined in gnupg (e.g. WKD/HKP lookup)
* @returns {Promise.<Array<GPGME_Key>>}
*
*/
- getKeys(pattern, prepare_sync){
+ getKeys(pattern, prepare_sync, search){
return new Promise(function(resolve, reject) {
let msg = createMessage('keylist');
if (pattern !== undefined){
msg.setParameter('keys', pattern);
}
msg.setParameter('sigs', true);
+ if (search === true){
+ msg.setParameter('locate', true);
+ }
msg.post().then(function(result){
let resultset = [];
let promises = [];
if (result.keys.length === 0){
resolve([]);
} else {
for (let i=0; i< result.keys.length; i++){
let k = createKey(result.keys[i].fingerprint);
k.setKeyData(result.keys[i]);
if (prepare_sync === true){
promises.push(k.getArmor());
promises.push(k.getHasSecret());
}
resultset.push(k);
}
if (promises.length > 0) {
Promise.all(promises).then(function() {
resolve(resultset);
}, function(error){
reject(error);
});
} else {
resolve(resultset);
}
}
});
});
}
/**
* 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) {
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);
});
});
}
/**
* Returns the Key to be used by default for signing operations,
* looking up the gpg configuration, or returning the first key that
* contains a secret key.
* @returns {Promise<GPGME_Key>}
*
* @async
* TODO: getHasSecret always returns false at this moment, so this fucntion
* still does not fully work as intended.
*
*/
getDefaultKey() {
let me = this;
return new Promise(function(resolve, reject){
let msg = createMessage('config_opt');
msg.setParameter('component', 'gpg');
msg.setParameter('option', 'default-key');
msg.post().then(function(response){
if (response.value !== undefined
&& response.value.hasOwnProperty('string')
&& typeof(response.value.string) === 'string'
){
me.getKeys(response.value.string,true).then(function(keys){
if(keys.length === 1){
resolve(keys[0]);
} else {
reject(gpgme_error('KEY_NO_DEFAULT'));
}
}, function(error){
reject(error);
});
} else {
// TODO: this is overly 'expensive' in communication
// and probably performance, too
me.getKeys(null,true).then(function(keys){
for (let i=0; i < keys.length; i++){
if (keys[i].get('hasSecret') === true){
resolve(keys[i]);
break;
}
if (i === keys.length -1){
reject(gpgme_error('KEY_NO_DEFAULT'));
}
}
}, function(error){
reject(error);
});
}
}, function(error){
reject(error);
});
});
}
/**
*
* @param {String} armored Armored Key block of the Key(s) to be imported
* into gnupg
* @param {Boolean} prepare_sync prepare the keys for synched use
* (see getKeys()).
*
* @returns {Promise<Object>} result: A summary and an array of Keys
* considered
*
* @returns result.summary: Numerical summary of the result. See the
* feedbackValues variable for available values and the gnupg documentation
* https://www.gnupg.org/documentation/manuals/gpgme/Importing-Keys.html
* for details on their meaning.
* @returns {Array<Object>} result.Keys: Array of objects containing:
* @returns {GPGME_Key} Key.key The resulting key
* @returns {String} Key.status:
* 'nochange' if the Key was not changed,
* 'newkey' if the Key was imported in gpg, and did not exist
* previously,
* 'change' if the key existed, but details were updated. For
* details, Key.changes is available.
* @returns {Boolean} Key.changes.userId: userIds changed
* @returns {Boolean} Key.changes.signature: signatures changed
* @returns {Boolean} Key.changes.subkey: subkeys changed
*/
importKey(armored, prepare_sync) {
let feedbackValues = ['considered', 'no_user_id', 'imported',
'imported_rsa', 'unchanged', 'new_user_ids', 'new_sub_keys',
'new_signatures', 'new_revocations', 'secret_read',
'secret_imported', 'secret_unchanged', 'skipped_new_keys',
'not_imported', 'skipped_v3_keys'];
if (!armored || typeof(armored) !== 'string'){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
let me = this;
return new Promise(function(resolve, reject){
let msg = createMessage('import');
msg.setParameter('data', armored);
msg.post().then(function(response){
let infos = {};
let fprs = [];
for (let res=0; res<response.result.imports.length; res++){
let result = response.result.imports[res];
let status = '';
if (result.status === 0){
status = 'nochange';
} else if ((result.status & 1) === 1){
status = 'newkey';
} else {
status = 'change';
}
let changes = {};
changes.userId = (result.status & 2) === 2;
changes.signature = (result.status & 4) === 4;
changes.subkey = (result.status & 8) === 8;
//16 new secret key: not implemented
fprs.push(result.fingerprint);
infos[result.fingerprint] = {
changes: changes,
status: status
};
}
let resultset = [];
if (prepare_sync === true){
me.getKeys(fprs, true).then(function(result){
for (let i=0; i < result.length; i++) {
resultset.push({
key: result[i],
changes: infos[result[i].fingerprint].changes,
status: infos[result[i].fingerprint].status
});
}
let summary = {};
for (let i=0; i < feedbackValues.length; i++ ){
summary[feedbackValues[i]] =
response[feedbackValues[i]];
}
resolve({
Keys:resultset,
summary: summary
});
}, function(error){
reject(error);
});
} else {
for (let i=0; i < fprs.length; i++) {
resultset.push({
key: createKey(fprs[i]),
changes: infos[fprs[i]].changes,
status: infos[fprs[i]].status
});
}
resolve(resultset);
}
}, function(error){
reject(error);
});
});
}
deleteKey(fingerprint){
if (isFingerprint(fingerprint) === true) {
let key = createKey(fingerprint);
key.delete();
}
}
/**
* Generates a new Key pair directly in gpg, and returns a GPGME_Key
* representing that Key. Please note that due to security concerns, secret
* Keys can not be _deleted_ from inside gpgmejs.
*
* @param {String} userId The user Id, e.g. "Foo Bar <foo@bar.baz>"
* @param {*} algo (optional) algorithm (and optionally key size to be
* used. See {@link supportedKeyAlgos } below for supported values.
* @param {Date} expires (optional) Expiration date. If not set, expiration
* will be set to 'never'
*
* @returns{Promise<Key>}
*/
generateKey(userId, algo = 'default', expires){
if (
typeof(userId) !== 'string' ||
supportedKeyAlgos.indexOf(algo) < 0 ||
(expires && !(expires instanceof Date))
){
return Promise.reject(gpgme_error('PARAM_WRONG'));
}
let me = this;
return new Promise(function(resolve, reject){
let msg = createMessage('createkey');
msg.setParameter('userid', userId);
msg.setParameter('algo', algo );
if (expires){
msg.setParameter('expires',
Math.floor(expires.valueOf()/1000));
}
msg.post().then(function(response){
me.getKeys(response.fingerprint, true).then(
// TODO make prepare_sync (second parameter) optional here.
function(result){
resolve(result);
}, function(error){
reject(error);
});
}, function(error) {
reject(error);
});
});
}
}
/**
* A list of algorithms supported for key generation.
*/
const supportedKeyAlgos = [
'default',
'rsa', 'rsa2048', 'rsa3072', 'rsa4096',
'dsa', 'dsa2048', 'dsa3072', 'dsa4096',
'elg', 'elg2048', 'elg3072', 'elg4096',
'ed25519',
'cv25519',
'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1',
'NIST P-256', 'NIST P-384', 'NIST P-521'
];
\ No newline at end of file
diff --git a/lang/js/src/permittedOperations.js b/lang/js/src/permittedOperations.js
index 312eaa52..e7f53965 100644
--- a/lang/js/src/permittedOperations.js
+++ b/lang/js/src/permittedOperations.js
@@ -1,393 +1,396 @@
/* gpgme.js - Javascript integration for gpgme
* Copyright (C) 2018 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
* SPDX-License-Identifier: LGPL-2.1+
*
* Author(s):
* Maximilian Krambach <mkrambach@intevation.de>
*/
/**
* Definition of the possible interactions with gpgme-json.
* operation: <Object>
required: Array<Object>
<String> name The name of the property
allowed: Array of allowed types. Currently accepted values:
['number', 'string', 'boolean', 'Uint8Array']
array_allowed: Boolean. If the value can be an array of the above
allowed_data: <Array> If present, restricts to the given value
optional: Array<Object>
see 'required', with these parameters not being mandatory for a
complete message
pinentry: boolean If a pinentry dialog is expected, and a timeout of
5000 ms would be too short
answer: <Object>
type: <String< The content type of answer expected
data: <Object>
the properties expected and their type, eg: {'data':'string'}
}
}
*/
export const permittedOperations = {
encrypt: {
pinentry: true, //TODO only with signing_keys
required: {
'keys': {
allowed: ['string'],
array_allowed: true
},
'data': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'signing_keys': {
allowed: ['string'],
array_allowed: true
},
'base64': {
allowed: ['boolean']
},
'mime': {
allowed: ['boolean']
},
'armor': {
allowed: ['boolean']
},
'always-trust': {
allowed: ['boolean']
},
'no-encrypt-to': {
allowed: ['string'],
array_allowed: true
},
'no-compress': {
allowed: ['boolean']
},
'throw-keyids': {
allowed: ['boolean']
},
'want-address': {
allowed: ['boolean']
},
'wrap': {
allowed: ['boolean']
}
},
answer: {
type: ['ciphertext'],
data: {
'data': 'string',
'base64':'boolean'
}
}
},
decrypt: {
pinentry: true,
required: {
'data': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'base64': {
allowed: ['boolean']
}
},
answer: {
type: ['plaintext'],
data: {
'data': 'string',
'base64': 'boolean',
'mime': 'boolean',
'signatures': 'object'
}
}
},
sign: {
pinentry: true,
required: {
'data': {
allowed: ['string']},
'keys': {
allowed: ['string'],
array_allowed: true
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'sender': {
allowed: ['string'],
},
'mode': {
allowed: ['string'],
allowed_data: ['detached', 'clearsign']
// TODO 'opaque' is not used, but available on native app
},
'base64': {
allowed: ['boolean']
},
'armor': {
allowed: ['boolean']
},
},
answer: {
type: ['signature', 'ciphertext'],
data: {
'data': 'string',
'base64':'boolean'
}
}
},
// note: For the meaning of the optional keylist flags, refer to
// https://www.gnupg.org/documentation/manuals/gpgme/Key-Listing-Mode.html
keylist:{
required: {},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'secret': {
allowed: ['boolean']
},
'extern': {
allowed: ['boolean']
},
'local':{
allowed: ['boolean']
},
+ 'locate': {
+ allowed: ['boolean']
+ },
'sigs':{
allowed: ['boolean']
},
'notations':{
allowed: ['boolean']
},
'tofu': {
allowed: ['boolean']
},
'ephemeral': {
allowed: ['boolean']
},
'validate': {
allowed: ['boolean']
},
'keys': {
allowed: ['string'],
array_allowed: true
}
},
answer: {
type: ['keys'],
data: {
'base64': 'boolean',
'keys': 'object'
}
}
},
export: {
required: {},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'keys': {
allowed: ['string'],
array_allowed: true
},
'armor': {
allowed: ['boolean']
},
'extern': {
allowed: ['boolean']
},
'minimal': {
allowed: ['boolean']
},
'raw': {
allowed: ['boolean']
},
'pkcs12':{
allowed: ['boolean']
}
// secret: not yet implemented
},
answer: {
type: ['keys'],
data: {
'data': 'string',
'base64': 'boolean'
}
}
},
import: {
required: {
'data': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'base64': {
allowed: ['boolean']
},
},
answer: {
type: [],
data: {
'result': 'object'
}
}
},
delete: {
pinentry: true,
required:{
'key': {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
// 'secret': { not implemented
// allowed: ['boolean']
// }
},
answer: {
data: {
'success': 'boolean'
}
}
},
version: {
required: {},
optional: {},
answer: {
type: [''],
data: {
'gpgme': 'string',
'info': 'object'
}
}
},
createkey: {
pinentry: true,
required: {
userid: {
allowed: ['string']
}
},
optional: {
algo: {
allowed: ['string']
},
expires: {
allowed: ['number'],
}
},
answer: {
type: [''],
data: {'fingerprint': 'string'}
}
},
verify: {
required: {
data: {
allowed: ['string']
}
},
optional: {
'protocol': {
allowed: ['string'],
allowed_data: ['cms', 'openpgp']
},
'signature': {
allowed: ['string']
},
'base64':{
allowed: ['boolean']
}
},
answer: {
type: ['plaintext'],
data:{
data: 'string',
base64:'boolean',
info: 'object'
// file_name: Optional string of the plaintext file name.
// is_mime: Boolean if the messages claims it is MIME.
// signatures: Array of signatures
}
}
},
config_opt: {
required: {
'component':{
allowed: ['string'],
// allowed_data: ['gpg'] // TODO check all available
},
'option': {
allowed: ['string'],
// allowed_data: ['default-key'] // TODO check all available
}
},
optional: {},
answer: {
type: [],
data: {
option: 'object'
}
}
}
/**
* TBD handling of secrets
* TBD key modification?
*/
};
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:44 PM (16 h, 6 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5c/20/75d4b3fde458440ffef2360e0b57
Attached To
rM GPGME
Event Timeline
Log In to Comment