Skip to content

Commit

Permalink
feat: add /api/auth/revoke/key endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
mistakia committed Apr 11, 2024
1 parent a73d1fd commit 90dc823
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 20 deletions.
2 changes: 2 additions & 0 deletions api/routes/auth/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import express from 'express'

import register from './register.mjs'
import message from './message.mjs'
import revoke from './revoke.mjs'

const router = express.Router()

router.use('/register', register)
router.use('/message', message)
router.use('/revoke', revoke)

export default router
1 change: 1 addition & 0 deletions api/routes/auth/message.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ router.post('/?', async (req, res) => {
const linked_accounts = await db('account_keys')
.select('account')
.where({ public_key })
.whereNull('revoked_at')
const nano_account = tools.publicKeyToAddress(public_key)

const all_accounts = [
Expand Down
15 changes: 9 additions & 6 deletions api/routes/auth/register.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import express from 'express'
import nano from 'nanocurrency'
import ed25519 from '@trashman/ed25519-blake2b'

import { verify_nano_community_link_key_signature } from '#common'

const router = express.Router()
const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/

Expand Down Expand Up @@ -89,11 +91,12 @@ router.post('/key/?', async (req, res) => {
}

const account_public_key = nano.derivePublicKey(account)
const valid_signature = ed25519.verify(
signature,
public_key,
account_public_key
)
const valid_signature = verify_nano_community_link_key_signature({
linked_public_key: public_key,
nano_account: account,
nano_account_public_key: account_public_key,
signature
})
if (!valid_signature) {
return res.status(401).send({ error: 'invalid signature' })
}
Expand All @@ -103,7 +106,7 @@ router.post('/key/?', async (req, res) => {
.insert({
account,
public_key,
signature,
link_signature: signature,
created_at
})
.onConflict()
Expand Down
77 changes: 77 additions & 0 deletions api/routes/auth/revoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import express from 'express'
import nano from 'nanocurrency'
import { verify_nano_community_revoke_key_signature } from '#common'

const router = express.Router()

router.post('/key/?', async (req, res) => {
const { logger, db } = req.app.locals
try {
const required = ['account', 'public_key', 'signature']
for (const prop of required) {
if (!req.body[prop]) {
return res.status(400).send({ error: `missing ${prop} param` })
}
}

const { account, public_key, signature } = req.body

if (!nano.checkAddress(account)) {
return res.status(401).send({ error: 'invalid account param' })
}

if (!nano.checkKey(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

if (!nano.checkSignature(signature)) {
return res.status(401).send({ error: 'invalid signature' })
}

const account_public_key = nano.derivePublicKey(account)
const valid_signature = verify_nano_community_revoke_key_signature({
linked_public_key: public_key,
nano_account: account,
nano_account_public_key: account_public_key,
signature
})
if (!valid_signature) {
return res.status(401).send({ error: 'invalid signature' })
}

const linked_key = await db('account_keys')
.where({ account, public_key })
.first()

if (!linked_key) {
return res
.status(401)
.send({ error: `key ${public_key} not linked to account ${account}` })
}

if (linked_key.revoked_at) {
return res
.status(401)
.send({ error: `key ${public_key} already revoked` })
}

const revoked_at = Math.floor(Date.now() / 1000)
await db('account_keys')
.update({ revoked_at, revoke_signature: signature })
.where({ account, public_key })

res.status(200).send({
account,
public_key,
signature,
created_at: linked_key.created_at,
revoked_at
})
} catch (error) {
console.log(error)
logger(error)
res.status(500).send('Internal server error')
}
})

export default router
4 changes: 4 additions & 0 deletions common/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export { default as convertToCSV } from './convert-to-csv.mjs'
export { default as read_csv } from './read-csv.mjs'
export { default as verify_nano_community_message_signature } from './verify-nano-community-message-signature.mjs'
export { default as sign_nano_community_message } from './sign-nano-community-message.mjs'
export { default as sign_nano_community_revoke_key } from './sign-nano-community-revoke-key.mjs'
export { default as verify_nano_community_revoke_key_signature } from './verify-nano-community-revoke-key-signature.mjs'
export { default as sign_nano_community_link_key } from './sign-nano-community-link-key.mjs'
export { default as verify_nano_community_link_key_signature } from './verify-nano-community-link-key-signature.mjs'

const POST = (data) => ({
method: 'POST',
Expand Down
34 changes: 34 additions & 0 deletions common/sign-nano-community-link-key.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ed25519 from '@trashman/ed25519-blake2b'

export default function sign_nano_community_link_key({
linked_public_key,
nano_account,
nano_account_private_key,
nano_account_public_key
}) {
if (!linked_public_key) {
throw new Error('linked_public_key is required')
}

if (!nano_account) {
throw new Error('nano_account is required')
}

if (!nano_account_private_key) {
throw new Error('nano_account_private_key is required')
}

if (!nano_account_public_key) {
throw new Error('nano_account_public_key is required')
}

const data = Buffer.from(['LINK', nano_account, linked_public_key])

const message_hash = ed25519.hash(data)

return ed25519.sign(
message_hash,
nano_account_private_key,
nano_account_public_key
)
}
34 changes: 34 additions & 0 deletions common/sign-nano-community-revoke-key.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ed25519 from '@trashman/ed25519-blake2b'

export default function sign_nano_community_revoke_key({
linked_public_key,
nano_account,
nano_account_private_key,
nano_account_public_key
}) {
if (!linked_public_key) {
throw new Error('linked_public_key is required')
}

if (!nano_account) {
throw new Error('nano_account is required')
}

if (!nano_account_private_key) {
throw new Error('nano_account_private_key is required')
}

if (!nano_account_public_key) {
throw new Error('nano_account_public_key is required')
}

const data = Buffer.from(['REVOKE', nano_account, linked_public_key])

const message_hash = ed25519.hash(data)

return ed25519.sign(
message_hash,
nano_account_private_key,
nano_account_public_key
)
}
13 changes: 13 additions & 0 deletions common/verify-nano-community-link-key-signature.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ed25519 from '@trashman/ed25519-blake2b'

export default function verify_nano_community_link_key_signature({
linked_public_key,
nano_account,
nano_account_public_key,
signature
}) {
const data = Buffer.from(['LINK', nano_account, linked_public_key])

const message_hash = ed25519.hash(data)
return ed25519.verify(signature, message_hash, nano_account_public_key)
}
29 changes: 29 additions & 0 deletions common/verify-nano-community-revoke-key-signature.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ed25519 from '@trashman/ed25519-blake2b'

export default function verify_nano_community_revoke_key_signature({
linked_public_key,
nano_account,
nano_account_public_key,
signature
}) {
if (!linked_public_key) {
throw new Error('linked_public_key is required')
}

if (!nano_account) {
throw new Error('nano_account is required')
}

if (!nano_account_public_key) {
throw new Error('nano_account_public_key is required')
}

if (!signature) {
throw new Error('signature is required')
}

const data = Buffer.from(['REVOKE', nano_account, linked_public_key])

const message_hash = ed25519.hash(data)
return ed25519.verify(signature, message_hash, nano_account_public_key)
}
4 changes: 3 additions & 1 deletion db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ DROP TABLE IF EXISTS `account_keys`;
CREATE TABLE `account_keys` (
`account` char(65) CHARACTER SET utf8 NOT NULL,
`public_key` varchar(64) NOT NULL,
`signature` varchar(128) NOT NULL,
`link_signature` varchar(128) NOT NULL,
`revoke_signature` varchar(128) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`revoked_at` int(11) DEFAULT NULL,
UNIQUE `account_key` (`account`, `public_key`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci;

Expand Down
5 changes: 0 additions & 5 deletions src/views/pages/live/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ export default function LivePage({
max: unconfirmed_percentile_values[3]
}

console.log({
confirmed_percentile,
unconfirmed_percentile
})

return (
<>
<Seo
Expand Down
4 changes: 4 additions & 0 deletions test/auth.message.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -451,5 +451,9 @@ describe('API /auth/message', function () {
// eslint-disable-next-line no-unused-expressions
expect(saved_message).to.be.undefined
})

it('should not save message if linked public key is already revoked', async () => {
// TODO
})
})
})
24 changes: 16 additions & 8 deletions test/auth.register.key.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import nano from 'nanocurrency'

import server from '#api/server.mjs'
import knex from '#db'
import { sign_nano_community_link_key } from '#common'
import { mochaGlobalSetup } from './global.mjs'

process.env.NODE_ENV = 'test'
Expand Down Expand Up @@ -35,11 +36,12 @@ describe('API /auth/register/key', () => {
nano_account_public_key.toString('hex')
)

const signature = ed25519.sign(
public_key.toString('hex'),
const signature = sign_nano_community_link_key({
linked_public_key: public_key.toString('hex'),
nano_account,
nano_account_private_key,
nano_account_public_key
)
})

const response = await chai
.request(server)
Expand All @@ -60,7 +62,7 @@ describe('API /auth/register/key', () => {
expect(saved_row).to.exist
expect(saved_row.account).to.equal(nano_account)
expect(saved_row.public_key).to.equal(public_key.toString('hex'))
expect(saved_row.signature).to.equal(signature.toString('hex'))
expect(saved_row.link_signature).to.equal(signature.toString('hex'))
expect(saved_row.created_at).to.be.a('number')
expect(saved_row.created_at).to.equal(response.body.created_at)
})
Expand Down Expand Up @@ -123,7 +125,12 @@ describe('API /auth/register/key', () => {
)
const public_key = ed25519.publicKey(private_key)
const account = 'someaccount'
const signature = ed25519.sign(public_key, private_key, public_key)
const signature = sign_nano_community_link_key({
linked_public_key: public_key.toString('hex'),
nano_account: account,
nano_account_private_key: private_key,
nano_account_public_key: public_key
})
const response = await chai
.request(server)
.post('/api/auth/register/key')
Expand Down Expand Up @@ -155,11 +162,12 @@ describe('API /auth/register/key', () => {
const nano_account_public_key = ed25519.publicKey(
nano_account_private_key
)
const signature = ed25519.sign(
public_key,
const signature = sign_nano_community_link_key({
linked_public_key: public_key.toString('hex'),
nano_account,
nano_account_private_key,
nano_account_public_key
)
})

const response = await chai
.request(server)
Expand Down

0 comments on commit 90dc823

Please sign in to comment.