mirror of https://codeberg.org/pzp/pzp-db.git
add identity.consent() API
This commit is contained in:
parent
a3fcb641eb
commit
0899ac5818
67
lib/index.js
67
lib/index.js
|
@ -8,7 +8,9 @@ const b4a = require('b4a')
|
|||
const base58 = require('bs58')
|
||||
// @ts-ignore
|
||||
const Obz = require('obz')
|
||||
const Keypair = require('ppppp-keypair')
|
||||
const MsgV3 = require('./msg-v3')
|
||||
const { SIGNATURE_TAG_IDENTITY_ADD, IDENTITY_SELF } = require('./msg-v3/constants')
|
||||
const { ReadyGate } = require('./utils')
|
||||
const { decrypt } = require('./encryption')
|
||||
|
||||
|
@ -272,7 +274,7 @@ function initDB(peer, config) {
|
|||
const tangle = new DBTangle(tangleRootHash, records())
|
||||
|
||||
const pubkeys = new Set()
|
||||
if (msg.metadata.identity && msg.metadata.identity !== 'self') {
|
||||
if (msg.metadata.identity && msg.metadata.identity !== IDENTITY_SELF) {
|
||||
const identityTangle = new DBTangle(msg.metadata.identity, records())
|
||||
if (!identityTangle.has(msg.metadata.identity)) {
|
||||
// prettier-ignore
|
||||
|
@ -323,7 +325,7 @@ function initDB(peer, config) {
|
|||
*/
|
||||
function getIdentityId(rec) {
|
||||
if (!rec.msg) return null
|
||||
if (rec.msg.metadata.identity === 'self') {
|
||||
if (rec.msg.metadata.identity === IDENTITY_SELF) {
|
||||
for (const tangleId in rec.msg.metadata.tangles) {
|
||||
return tangleId
|
||||
}
|
||||
|
@ -343,8 +345,8 @@ function initDB(peer, config) {
|
|||
* @param {CB<string>} cb
|
||||
*/
|
||||
function findIdentity(opts, cb) {
|
||||
if (!opts.domain)
|
||||
return cb(new Error('identity.find() requires a `domain`'))
|
||||
// prettier-ignore
|
||||
if (!opts.domain) return cb(new Error('identity.find() requires a `domain`'))
|
||||
const keypair = opts?.keypair ?? config.keypair
|
||||
const domain = opts.domain
|
||||
|
||||
|
@ -354,7 +356,7 @@ function initDB(peer, config) {
|
|||
if (!rec.msg) continue
|
||||
if (!rec.msg.data) continue
|
||||
if (
|
||||
rec.msg.metadata.identity === 'self' &&
|
||||
rec.msg.metadata.identity === IDENTITY_SELF &&
|
||||
rec.msg.data.add === keypair.public &&
|
||||
rec.msg.metadata.domain === domain
|
||||
) {
|
||||
|
@ -365,7 +367,7 @@ function initDB(peer, config) {
|
|||
// prettier-ignore
|
||||
cb(new Error(`identity.find() failed to find ID in ${JSON.stringify(rec.msg)}`))
|
||||
}
|
||||
break
|
||||
return
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
|
@ -382,8 +384,8 @@ function initDB(peer, config) {
|
|||
* @param {CB<string>} cb
|
||||
*/
|
||||
function createIdentity(opts, cb) {
|
||||
if (!opts.domain)
|
||||
return cb(new Error('identity.create() requires a `domain`'))
|
||||
// prettier-ignore
|
||||
if (!opts.domain) return cb(new Error('identity.create() requires a `domain`'))
|
||||
const keypair = opts?.keypair ?? config.keypair
|
||||
const domain = opts.domain
|
||||
|
||||
|
@ -425,25 +427,59 @@ function initDB(peer, config) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {{ keypair: Keypair; identity: string; }} opts
|
||||
* @param {{
|
||||
* keypair?: Keypair;
|
||||
* identity: string;
|
||||
* }} opts
|
||||
* @returns {string}
|
||||
*/
|
||||
function consentToIdentity(opts) {
|
||||
// prettier-ignore
|
||||
if (!opts.identity) throw new Error('identity.consent() requires an `identity`')
|
||||
const keypair = opts?.keypair ?? config.keypair
|
||||
|
||||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_IDENTITY_ADD + base58.decode(opts.identity),
|
||||
'utf8'
|
||||
)
|
||||
return Keypair.sign(keypair, signableBuf)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* keypair: Keypair;
|
||||
* identity: string;
|
||||
* consent: string
|
||||
* }} opts
|
||||
* @param {CB<Rec>} cb
|
||||
*/
|
||||
function addToIdentity(opts, cb) {
|
||||
if (!opts?.keypair)
|
||||
return cb(new Error('identity.add() requires a `keypair`'))
|
||||
if (!opts?.identity)
|
||||
return cb(new Error('identity.add() requires a `identity`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.keypair) return cb(new Error('identity.add() requires a `keypair`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.identity) return cb(new Error('identity.add() requires a `identity`'))
|
||||
// prettier-ignore
|
||||
if (!opts?.consent) return cb(new Error('identity.add() requires a `consent`'))
|
||||
const addedKeypair = opts.keypair
|
||||
const signingKeypair = config.keypair
|
||||
|
||||
// Verify consent:
|
||||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_IDENTITY_ADD + base58.decode(opts.identity),
|
||||
)
|
||||
if (!Keypair.verify(addedKeypair, signableBuf, opts.consent)) {
|
||||
// prettier-ignore
|
||||
return cb(new Error('identity.add() failed because the consent is invalid'))
|
||||
}
|
||||
|
||||
// Fill-in tangle opts:
|
||||
const tangles = populateTangles([opts.identity])
|
||||
const fullOpts = {
|
||||
identity: 'self',
|
||||
identity: IDENTITY_SELF,
|
||||
identityTips: null,
|
||||
tangles,
|
||||
keypair: signingKeypair,
|
||||
data: { add: addedKeypair.public },
|
||||
data: { add: addedKeypair.public, consent: opts.consent },
|
||||
domain: 'identity',
|
||||
}
|
||||
|
||||
|
@ -652,6 +688,7 @@ function initDB(peer, config) {
|
|||
create: createIdentity,
|
||||
findOrCreate: findOrCreateIdentity,
|
||||
add: addToIdentity,
|
||||
consent: consentToIdentity,
|
||||
},
|
||||
feed: {
|
||||
publish: publishToFeed,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
/** @type {'self'} */
|
||||
IDENTITY_SELF: 'self',
|
||||
|
||||
SIGNATURE_TAG_MSG_V3: ':msg-v3:',
|
||||
SIGNATURE_TAG_IDENTITY_ADD: ':identity-add:',
|
||||
}
|
|
@ -17,6 +17,7 @@ const {
|
|||
validateMsgHash,
|
||||
} = require('./validation')
|
||||
const Tangle = require('./tangle')
|
||||
const { IDENTITY_SELF, SIGNATURE_TAG_MSG_V3 } = require('./constants')
|
||||
|
||||
/**
|
||||
* @typedef {import('ppppp-keypair').Keypair} Keypair
|
||||
|
@ -57,8 +58,6 @@ const Tangle = require('./tangle')
|
|||
* }} CreateOpts
|
||||
*/
|
||||
|
||||
const IDENTITY_SELF = /** @type {const} */ 'self'
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {string} domain
|
||||
|
@ -138,9 +137,11 @@ function create(opts) {
|
|||
}
|
||||
if ((err = validateData(msg))) throw err
|
||||
|
||||
// TODO: add a label prefix to the metadata before signing
|
||||
const metadataBuf = b4a.from(stringify(msg.metadata), 'utf8')
|
||||
msg.sig = Keypair.sign(opts.keypair, metadataBuf)
|
||||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata),
|
||||
'utf8'
|
||||
)
|
||||
msg.sig = Keypair.sign(opts.keypair, signableBuf)
|
||||
|
||||
return msg
|
||||
}
|
||||
|
@ -171,9 +172,11 @@ function createRoot(id, domain, keypair) {
|
|||
sig: '',
|
||||
}
|
||||
|
||||
// TODO: add a label prefix to the metadata before signing
|
||||
const metadataBuf = b4a.from(stringify(msg.metadata), 'utf8')
|
||||
msg.sig = Keypair.sign(keypair, metadataBuf)
|
||||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata),
|
||||
'utf8'
|
||||
)
|
||||
msg.sig = Keypair.sign(keypair, signableBuf)
|
||||
|
||||
return msg
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ const stringify = require('json-canon')
|
|||
const Tangle = require('./tangle')
|
||||
const representData = require('./represent-data')
|
||||
const isFeedRoot = require('./is-feed-root')
|
||||
const { SIGNATURE_TAG_MSG_V3, IDENTITY_SELF } = require('./constants')
|
||||
|
||||
/**
|
||||
* @typedef {import('.').Msg} Msg
|
||||
|
@ -90,7 +91,7 @@ function validateIdentityPubkey(msg, pubkeys) {
|
|||
|
||||
if (
|
||||
msg.metadata.identity &&
|
||||
msg.metadata.identity !== 'self' &&
|
||||
msg.metadata.identity !== IDENTITY_SELF &&
|
||||
!pubkeys.has(msg.pubkey)
|
||||
) {
|
||||
// prettier-ignore
|
||||
|
@ -150,7 +151,10 @@ function validateSignature(msg) {
|
|||
return `invalid message: sig "${sig}" should have been a base58 string\n` + JSON.stringify(msg)
|
||||
}
|
||||
|
||||
const signableBuf = b4a.from(stringify(msg.metadata), 'utf8')
|
||||
const signableBuf = b4a.from(
|
||||
SIGNATURE_TAG_MSG_V3 + stringify(msg.metadata),
|
||||
'utf8'
|
||||
)
|
||||
const keypair = { curve: 'ed25519', public: msg.pubkey }
|
||||
const verified = Keypair.verify(keypair, signableBuf, sig)
|
||||
if (!verified) {
|
||||
|
|
|
@ -26,9 +26,12 @@ test('identity.add()', async (t) => {
|
|||
domain: 'person',
|
||||
})
|
||||
|
||||
const consent = peer.db.identity.consent({ identity: id, keypair: keypair2 })
|
||||
|
||||
const identityRec1 = await p(peer.db.identity.add)({
|
||||
identity: id,
|
||||
keypair: keypair2,
|
||||
consent,
|
||||
})
|
||||
assert.ok(identityRec1, 'identityRec1 exists')
|
||||
const { hash, msg } = identityRec1
|
||||
|
@ -64,9 +67,12 @@ test('publish with a key in the identity', async (t) => {
|
|||
domain: 'person',
|
||||
})
|
||||
const identityMsg0 = peer.db.get(identity)
|
||||
|
||||
const consent = peer.db.identity.consent({ identity, keypair: keypair2 })
|
||||
const identityRec1 = await p(peer.db.identity.add)({
|
||||
identity,
|
||||
keypair: keypair2,
|
||||
consent,
|
||||
})
|
||||
|
||||
const postRec = await p(peer.db.feed.publish)({
|
||||
|
|
|
@ -95,7 +95,7 @@ test('MsgV3.create()', (t) => {
|
|||
)
|
||||
assert.equal(
|
||||
msg1.sig,
|
||||
'2FhnKsDKCxEV4JUM1nmPp3oSFJ8zL7r4SjMNogDHeAzCWQLgVmKiexgUDSE4k9C3eT4Uy3SZbBhRY75WJAqvtHHf',
|
||||
'3ucLkFxXJkbX6N7qZQm5PNop2tQ5Z1E9oCVB4HCZjeD3Mn7EXMrgZzCDZfpLTVUUBRqSBQJFxL1j5jNWKFeidHgV',
|
||||
'sig'
|
||||
)
|
||||
|
||||
|
@ -155,7 +155,7 @@ test('MsgV3.create()', (t) => {
|
|||
)
|
||||
assert.equal(
|
||||
msg2.sig,
|
||||
'3B6WQvDdKvRhZeZDfE9LY4HrnhZTJHRJ86FazBg1xxco2S1eHG44UwR9TSpthiXQ1X51h2VeDeGPV6Fdma69BMN9',
|
||||
'RtHPPccZNp6c65SnCrfjsNVB6We6G4Ja1oi68AdLuzxSjWNayxepagYJQwgP635E4b55xNGckMiFvJF9Vsn3oAi',
|
||||
'sig'
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue