forked from Mirrors/keyoxide-web
Fix support for signature profiles
This commit is contained in:
parent
f0a748542c
commit
01ba4a3a54
7 changed files with 118 additions and 50 deletions
|
@ -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",
|
||||||
|
|
|
@ -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 })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
||||||
|
|
|
@ -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==
|
||||||
|
|
Loading…
Reference in a new issue