Skip to content

Commit

Permalink
chore: remove dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
mistakia committed Jun 17, 2024
1 parent 57b09b2 commit 0b31620
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 145 deletions.
11 changes: 8 additions & 3 deletions api/routes/auth/message.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import express from 'express'
import { tools } from 'nanocurrency-web'
import BigNumber from 'bignumber.js'

import { rpc, verify_nano_community_message_signature } from '#common'
import {
rpc,
verify_nano_community_message_signature,
encode_nano_address
} from '#common'
import {
ACCOUNT_TRACKING_MINIMUM_BALANCE,
REPRESENTATIVE_TRACKING_MINIMUM_VOTING_WEIGHT
Expand Down Expand Up @@ -118,7 +121,9 @@ router.post('/?', async (req, res) => {
.select('account')
.where({ public_key })
.whereNull('revoked_at')
const nano_account = tools.publicKeyToAddress(public_key)
const nano_account = encode_nano_address({
public_key_buf: Buffer.from(public_key, 'hex')
})

const all_accounts = [
...linked_accounts.map((row) => row.account),
Expand Down
23 changes: 15 additions & 8 deletions api/routes/auth/register.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import express from 'express'
import nano from 'nanocurrency'
import ed25519 from '@trashman/ed25519-blake2b'

import { verify_nano_community_link_key_signature } from '#common'
import {
verify_nano_community_link_key_signature,
is_nano_address_valid,
decode_nano_address
} from '#common'

const router = express.Router()
const USERNAME_RE = /^[A-Za-z][a-zA-Z0-9_]+$/
const PUBLIC_KEY_RE = /^[0-9a-fA-F]{64}$/
const SIGNATURE_RE = /^[0-9a-fA-F]{128}$/

router.post('/?', async (req, res) => {
const { logger, db } = req.app.locals
Expand All @@ -19,15 +24,15 @@ router.post('/?', async (req, res) => {

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

if (!nano.checkKey(public_key)) {
if (typeof public_key !== 'string' || !PUBLIC_KEY_RE.test(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

if (!USERNAME_RE.test(username)) {
return res.status(401).send({ error: 'invalid username param' })
}

if (!nano.checkSignature(signature)) {
if (typeof signature !== 'string' || !SIGNATURE_RE.test(signature)) {
return res.status(401).send({ error: 'invalid signature' })
}

Expand Down Expand Up @@ -78,19 +83,21 @@ router.post('/key/?', async (req, res) => {

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

if (!nano.checkKey(public_key)) {
if (typeof public_key !== 'string' || !PUBLIC_KEY_RE.test(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

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

if (!nano.checkSignature(signature)) {
if (typeof signature !== 'string' || !SIGNATURE_RE.test(signature)) {
return res.status(401).send({ error: 'invalid signature' })
}

const account_public_key = nano.derivePublicKey(account)
const { public_key: account_public_key } = decode_nano_address({
address: account
})
const valid_signature = verify_nano_community_link_key_signature({
linked_public_key: public_key,
nano_account: account,
Expand Down
21 changes: 14 additions & 7 deletions api/routes/auth/revoke.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import express from 'express'
import nano from 'nanocurrency'
import { verify_nano_community_revoke_key_signature } from '#common'
import {
verify_nano_community_revoke_key_signature,
is_nano_address_valid,
decode_nano_address
} from '#common'

const router = express.Router()
const PUBLIC_KEY_RE = /^[0-9a-fA-F]{64}$/
const SIGNATURE_RE = /^[0-9a-fA-F]{128}$/

router.post('/key/?', async (req, res) => {
const { logger, db } = req.app.locals
Expand All @@ -16,19 +21,21 @@ router.post('/key/?', async (req, res) => {

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

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

if (!nano.checkKey(public_key)) {
if (typeof public_key !== 'string' || !PUBLIC_KEY_RE.test(public_key)) {
return res.status(401).send({ error: 'invalid public_key param' })
}

if (!nano.checkSignature(signature)) {
return res.status(401).send({ error: 'invalid signature' })
if (typeof signature !== 'string' || !SIGNATURE_RE.test(signature)) {
return res.status(401).send({ error: 'invalid signature param' })
}

const account_public_key = nano.derivePublicKey(account)
const { public_key: account_public_key } = decode_nano_address({
address: account
})
const valid_signature = verify_nano_community_revoke_key_signature({
linked_public_key: public_key,
nano_account: account,
Expand Down
31 changes: 13 additions & 18 deletions cli/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,17 @@ import crypto from 'crypto'
import { hideBin } from 'yargs/helpers'
import inquirer from 'inquirer'
import process from 'process'
import nano from 'nanocurrency'

import {
sign_nano_community_link_key,
sign_nano_community_revoke_key,
sign_nano_community_message,
request
} from '#common'
import config from '#config'
import sign_nano_community_link_key from '#common/sign-nano-community-link-key.mjs'
import sign_nano_community_revoke_key from '#common/sign-nano-community-revoke-key.mjs'
import sign_nano_community_message from '#common/sign-nano-community-message.mjs'
import encode_nano_address from '#common/encode-nano-address.mjs'
import request from '#common/request.mjs'
import ed25519 from '@trashman/ed25519-blake2b'

const is_test = process.env.NODE_ENV === 'test'

const base_url = is_test
? `http://localhost:${config.port}`
: 'https://nano.community'
const base_url = is_test ? 'http://localhost:8080' : 'https://nano.community'

const load_private_key = async () => {
let private_key = process.env.NANO_PRIVATE_KEY
Expand All @@ -41,11 +36,11 @@ const load_private_key = async () => {
private_key = answers.private_key
}

const public_key = nano.derivePublicKey(private_key)
const nano_account_address = nano.deriveAddress(public_key)
const public_key_buf = ed25519.publicKey(Buffer.from(private_key, 'hex'))
const nano_account_address = encode_nano_address({ public_key_buf })
return {
private_key,
public_key,
public_key: public_key_buf.toString('hex'),
nano_account_address
}
}
Expand Down Expand Up @@ -153,13 +148,13 @@ const revoke_signing_key = {
const update_rep_meta = {
command: 'update-rep-meta',
describe: 'Update representative metadata',
handler: async () => await sendMessageHandler('update-rep-meta')
handler: async () => await send_message_handler('update-rep-meta')
}

const update_account_meta = {
command: 'update-account-meta',
describe: 'Update account metadata',
handler: async () => await sendMessageHandler('update-account-meta')
handler: async () => await send_message_handler('update-account-meta')
}

const update_block_meta = {
Expand All @@ -171,10 +166,10 @@ const update_block_meta = {
type: 'string'
}),
handler: async ({ block_hash }) =>
await sendMessageHandler('update-block-meta', block_hash)
await send_message_handler('update-block-meta', block_hash)
}

const sendMessageHandler = async (type, block_hash = null) => {
const send_message_handler = async (type, block_hash = null) => {
const { private_key, public_key } = await load_private_key()

let message_content_prompts = []
Expand Down
5 changes: 5 additions & 0 deletions common/binary-to-hex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default function binary_to_hex(buf) {
return Array.prototype.map
.call(new Uint8Array(buf), (x) => ('00' + x.toString(16)).slice(-2))
.join('')
}
13 changes: 13 additions & 0 deletions common/decode-nano-address.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { decode_nano_base32 } from './nano-base-32.mjs'
import binary_to_hex from './binary-to-hex.mjs'

export default function decode_nano_address({ address }) {
const cleaned_address = address.replace('nano_', '').replace('xrb_', '')
const decoded = decode_nano_base32(cleaned_address)
const public_key = binary_to_hex(decoded.subarray(0, 32))
const checksum = binary_to_hex(decoded.subarray(32, 32 + 5))
return {
public_key,
checksum
}
}
13 changes: 13 additions & 0 deletions common/encode-nano-address.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import ed25519 from '@trashman/ed25519-blake2b'

import { encode_nano_base32 } from './nano-base-32.mjs'

export default function encode_nano_address({
public_key_buf,
prefix = 'nano_'
}) {
const encoded_public_key = encode_nano_base32(public_key_buf)
const checksum = ed25519.hash(public_key_buf, 5).reverse()
const encoded_checksum = encode_nano_base32(checksum)
return prefix + encoded_public_key + encoded_checksum
}
6 changes: 6 additions & 0 deletions common/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path, { dirname } from 'path'
import config from '#config'
import { BURN_ACCOUNT } from '#constants'
import request from './request.mjs'
import { encode_nano_base32, decode_nano_base32 } from './nano-base-32.mjs'

export * as cloudflare from './cloudflare.mjs'
export { request }
Expand All @@ -16,6 +17,11 @@ export { default as sign_nano_community_revoke_key } from './sign-nano-community
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'
export { default as encode_nano_address } from './encode-nano-address.mjs'
export { default as decode_nano_address } from './decode-nano-address.mjs'
export { default as binary_to_hex } from './binary-to-hex.mjs'
export { default as is_nano_address_valid } from './is-nano-address-valid.mjs'
export { encode_nano_base32, decode_nano_base32 }

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

import decode_nano_address from './decode-nano-address.mjs'

const NANO_ADDRESS_RE = /^(xrb_|nano_)[13][13-9a-km-uw-z]{59}$/

const compare_arrays = (a, b) => {
if (a.length !== b.length) {
return false
}

return a.every((byte, index) => byte === b[index])
}

export default function is_nano_address_valid(address) {
if (typeof address !== 'string') {
return false
}

if (!NANO_ADDRESS_RE.test(address)) {
return false
}

const { public_key, checksum } = decode_nano_address({ address })

const public_key_bytes = Buffer.from(public_key, 'hex')
const checksum_bytes = Buffer.from(checksum, 'hex')
const computed_checksum_bytes = ed25519.hash(public_key_bytes, 5).reverse()
const valid = compare_arrays(checksum_bytes, computed_checksum_bytes)

return valid
}
103 changes: 103 additions & 0 deletions common/nano-base-32.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
MIT License
Copyright (c) 2018 Gray Olson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

const alphabet = '13456789abcdefghijkmnopqrstuwxyz'

/**
* Encode provided Uint8Array using the Nano-specific Base-32 implementeation.
* @param view Input buffer formatted as a Uint8Array
* @returns
* @hidden
*/
export function encode_nano_base32(view) {
const length = view.length
const leftover = (length * 8) % 5
const offset = leftover === 0 ? 0 : 5 - leftover

let value = 0
let output = ''
let bits = 0

for (let i = 0; i < length; i++) {
value = (value << 8) | view[i]
bits += 8

while (bits >= 5) {
output += alphabet[(value >>> (bits + offset - 5)) & 31]
bits -= 5
}
}

if (bits > 0) {
output += alphabet[(value << (5 - (bits + offset))) & 31]
}

return output
}

function readChar(char) {
const idx = alphabet.indexOf(char)

if (idx === -1) {
throw new Error(`Invalid character found: ${char}`)
}

return idx
}

/**
* Decodes a Nano-implementation Base32 encoded string into a Uint8Array
* @param input A Nano-Base32 encoded string
* @returns
* @hidden
*/
export function decode_nano_base32(input) {
const length = input.length
const leftover = (length * 5) % 8
const offset = leftover === 0 ? 0 : 8 - leftover

let bits = 0
let value = 0

let index = 0
let output = new Uint8Array(Math.ceil((length * 5) / 8))

for (let i = 0; i < length; i++) {
value = (value << 5) | readChar(input[i])
bits += 5

if (bits >= 8) {
output[index++] = (value >>> (bits + offset - 8)) & 255
bits -= 8
}
}
if (bits > 0) {
output[index++] = (value << (bits + offset - 8)) & 255
}

if (leftover !== 0) {
output = output.slice(1)
}
return output
}
Loading

0 comments on commit 0b31620

Please sign in to comment.