Fix support for signature profiles

This commit is contained in:
Yarmo Mackenbach 2021-05-03 17:09:10 +02:00
parent f0a748542c
commit 01ba4a3a54
No known key found for this signature in database
GPG key ID: 37367F4AF4087AD1
7 changed files with 118 additions and 50 deletions

View file

@ -5,6 +5,7 @@
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"bent": "^7.3.12", "bent": "^7.3.12",
"body-parser": "^1.19.0",
"dialog-polyfill": "^0.5.6", "dialog-polyfill": "^0.5.6",
"doipjs": "^0.12.4", "doipjs": "^0.12.4",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",

View file

@ -28,41 +28,35 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For
more information on this, and how to apply and follow the GNU AGPL, see <https://www.gnu.org/licenses/>. more information on this, and how to apply and follow the GNU AGPL, see <https://www.gnu.org/licenses/>.
*/ */
const router = require('express').Router() const router = require('express').Router()
const bodyParser = require('body-parser').urlencoded({ extended: false })
const kx = require('../server') const kx = require('../server')
router.get('/sig', (req, res) => { router.get('/sig', (req, res) => {
res.render('profile', { mode: 'sig' }) res.render('profile', { isSignature: true, signature: null })
})
router.post('/sig', bodyParser, async (req, res) => {
const data = await kx.generateSignatureProfile(req.body.signature)
res.render('profile', { data: data, isSignature: true, signature: req.body.signature })
}) })
router.get('/wkd/:id', async (req, res) => { router.get('/wkd/:id', async (req, res) => {
const data = await kx.generateWKDProfile(req.params.id) const data = await kx.generateWKDProfile(req.params.id)
if (data.errors.length > 0) {
return res.render('profile-failed', { data: data })
}
res.render('profile', { data: data }) res.render('profile', { data: data })
}) })
router.get('/hkp/:id', async (req, res) => { router.get('/hkp/:id', async (req, res) => {
const data = await kx.generateHKPProfile(req.params.id) const data = await kx.generateHKPProfile(req.params.id)
if (data.errors.length > 0) {
return res.render('profile-failed', { data: data })
}
res.render('profile', { data: data }) res.render('profile', { data: data })
}) })
router.get('/hkp/:server/:id', async (req, res) => { router.get('/hkp/:server/:id', async (req, res) => {
const data = await kx.generateHKPProfile(req.params.id, req.params.server) const data = await kx.generateHKPProfile(req.params.id, req.params.server)
if (data.errors.length > 0) {
return res.render('profile-failed', { data: data })
}
res.render('profile', { data: data }) res.render('profile', { data: data })
}) })
router.get('/keybase/:username/:fingerprint', async (req, res) => { router.get('/keybase/:username/:fingerprint', async (req, res) => {
const data = await kx.generateKeybaseProfile(req.params.username, req.params.fingerprint) const data = await kx.generateKeybaseProfile(req.params.username, req.params.fingerprint)
if (data.errors.length > 0) {
return res.render('profile-failed', { data: data })
}
res.render('profile', { data: data }) res.render('profile', { data: data })
}) })
@ -73,9 +67,6 @@ router.get('/:id', async (req, res) => {
} else { } else {
data = await kx.generateHKPProfile(req.params.id) data = await kx.generateHKPProfile(req.params.id)
} }
if (data.errors.length > 0) {
return res.render('profile-failed', { data: data })
}
res.render('profile', { data: data }) res.render('profile', { data: data })
}) })

View file

@ -81,6 +81,30 @@ const generateHKPProfile = async (id, keyserverDomain) => {
}) })
} }
const generateSignatureProfile = async (signature) => {
return keys.fetchSignature(signature)
.then(async key => {
let keyData = key.keyData
delete key.keyData
keyData = processKeyData(keyData)
return {
key: key,
keyData: keyData,
extra: await computeExtraData(key, keyData),
errors: []
}
})
.catch(err => {
return {
key: null,
keyData: null,
extra: null,
errors: [err.message]
}
})
}
const generateKeybaseProfile = async (username, fingerprint) => { const generateKeybaseProfile = async (username, fingerprint) => {
return keys.fetchKeybase(id, keyserverDomain) return keys.fetchKeybase(id, keyserverDomain)
.then(async key => { .then(async key => {
@ -146,3 +170,4 @@ const computeExtraData = async (key, keyData) => {
exports.generateWKDProfile = generateWKDProfile exports.generateWKDProfile = generateWKDProfile
exports.generateHKPProfile = generateHKPProfile exports.generateHKPProfile = generateHKPProfile
exports.generateKeybaseProfile = generateKeybaseProfile exports.generateKeybaseProfile = generateKeybaseProfile
exports.generateSignatureProfile = generateSignatureProfile

View file

@ -65,22 +65,22 @@ const fetchWKD = (id) => {
} }
}) })
} catch (error) { } catch (error) {
reject(new Error("No public keys could be fetched using WKD")) reject(new Error(`No public keys could be fetched using WKD`))
} }
} }
if (!plaintext) { if (!plaintext) {
reject(new Error("No public keys could be fetched using WKD")) reject(new Error(`No public keys could be fetched using WKD`))
} }
try { try {
output.publicKey = (await openpgp.key.read(plaintext)).keys[0] output.publicKey = (await openpgp.key.read(plaintext)).keys[0]
} catch(error) { } catch(error) {
reject(new Error("No public keys could be read from the data fetched using WKD")) reject(new Error(`No public keys could be read from the data fetched using WKD`))
} }
if (!output.publicKey) { if (!output.publicKey) {
reject(new Error("No public keys could be read from the data fetched using WKD")) reject(new Error(`No public keys could be read from the data fetched using WKD`))
} }
resolve(output) resolve(output)
@ -107,11 +107,56 @@ const fetchHKP = (id, keyserverDomain) => {
output.publicKey = await doip.keys.fetchHKP(id, keyserverDomain) output.publicKey = await doip.keys.fetchHKP(id, keyserverDomain)
output.fetchURL = `https://${keyserverDomain}/pks/lookup?op=get&options=mr&search=${query}` output.fetchURL = `https://${keyserverDomain}/pks/lookup?op=get&options=mr&search=${query}`
} catch(error) { } catch(error) {
reject(new Error("No public keys could be fetched using HKP")) reject(new Error(`No public keys could be fetched using HKP`))
} }
if (!output.publicKey) { if (!output.publicKey) {
reject(new Error("No public keys could be fetched using HKP")) reject(new Error(`No public keys could be fetched using HKP`))
}
resolve(output)
})
}
const fetchSignature = (signature) => {
return new Promise(async (resolve, reject) => {
let output = {
publicKey: null,
fetchURL: null,
keyData: null
}
// Check validity of signature
let signatureData
try {
signatureData = await openpgp.cleartext.readArmored(signature)
} catch (error) {
reject(new Error(`Signature could not be properly read (${error.message})`))
}
// Process the signature
try {
output.keyData = await doip.signatures.process(signature)
output.publicKey = output.keyData.key.data
// TODO Find the URL to the key
output.fetchURL = null
} catch(error) {
reject(new Error(`Signature could not be properly read (${error.message})`))
}
// Check if a key was fetched
if (!output.publicKey) {
reject(new Error(`No public keys could be fetched`))
}
// Check validity of signature
const verified = await openpgp.verify({
message: signatureData,
publicKeys: output.publicKey
})
const { valid } = verified.signatures[0]
if (!valid) {
reject(new Error('Signature was invalid'))
} }
resolve(output) resolve(output)
@ -129,11 +174,11 @@ const fetchKeybase = (username, fingerprint) => {
output.publicKey = await doip.keys.fetchKeybase(username, fingerprint) output.publicKey = await doip.keys.fetchKeybase(username, fingerprint)
output.fetchURL = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}` output.fetchURL = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}`
} catch(error) { } catch(error) {
reject(new Error("No public keys could be fetched from Keybase")) reject(new Error(`No public keys could be fetched from Keybase`))
} }
if (!output.publicKey) { if (!output.publicKey) {
reject(new Error("No public keys could be fetched from Keybase")) reject(new Error(`No public keys could be fetched from Keybase`))
} }
resolve(output) resolve(output)
@ -142,4 +187,5 @@ const fetchKeybase = (username, fingerprint) => {
exports.fetchWKD = fetchWKD exports.fetchWKD = fetchWKD
exports.fetchHKP = fetchHKP exports.fetchHKP = fetchHKP
exports.fetchSignature = fetchSignature
exports.fetchKeybase = fetchKeybase exports.fetchKeybase = fetchKeybase

View file

@ -1,9 +0,0 @@
extends templates/base.pug
block content
section.profile.narrow
h2 Something went wrong when generating the profile
ul
each error in data.errors
li= error

View file

@ -24,7 +24,7 @@ block content
script. script.
kx = { kx = {
key: { key: {
url: "!{data.key.fetchURL}", url: "!{data && data.key && data.key.fetchURL ? data.key.fetchURL : null}",
object: null object: null
} }
} }
@ -57,21 +57,35 @@ block content
p p
a#qr--altLink a#qr--altLink
#profileHeader.card.card--profileHeader if (isSignature)
a.avatar(href="#") #profileSigInput.card.card--form
img#profileAvatar(src=data.extra.avatarURL alt="avatar") form#formGenerateSignatureProfile(method='post')
label(for="signature") Please enter the raw profile signature below and press "Generate profile".
textarea#signature(name='signature')= signature
input(type='submit', name='submit', value='Generate profile')
p#profileName= data.keyData.users[data.keyData.primaryUserIndex].userData.name
.buttons
button(onClick="document.querySelector('#dialog--encryptMessage').showModal();") Encrypt message
button(onClick="document.querySelector('#dialog--verifySignature').showModal();") Verify signature
#profileProofs.card if (data && 'errors' in data && data.errors.length > 0)
h2 Key h2 Something went wrong while generating the profile
kx-key(data-keydata=data.keyData) ul
each error in data.errors
li= error
else
unless (isSignature && !signature)
#profileHeader.card.card--profileHeader
a.avatar(href="#")
img#profileAvatar(src=data.extra.avatarURL alt="avatar")
+generateUser(data.keyData.users[data.keyData.primaryUserIndex], true) p#profileName= data.keyData.users[data.keyData.primaryUserIndex].userData.name
each user, index in data.keyData.users .buttons
unless index == data.keyData.primaryUserIndex button(onClick="document.querySelector('#dialog--encryptMessage').showModal();") Encrypt message
+generateUser(user, false) button(onClick="document.querySelector('#dialog--verifySignature').showModal();") Verify signature
#profileProofs.card
h2 Key
kx-key(data-keydata=data.keyData)
+generateUser(data.keyData.users[data.keyData.primaryUserIndex], true)
each user, index in data.keyData.users
unless index == data.keyData.primaryUserIndex
+generateUser(user, false)

View file

@ -874,7 +874,7 @@ bn.js@^4.0.0:
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
body-parser@1.19.0: body-parser@1.19.0, body-parser@^1.19.0:
version "1.19.0" version "1.19.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==