Building Zero Knowledge
A ZKA Backend w/ Node.js
@m4d_zWe Need Encryption
Backend has to be agnostic
Stick to the Venerables HTTP Verbs
Design pattern that
enforces Users Privacy
E2E Encryption
Asymetric Keys
at each Ends (for sharing)
Symetric Key Wraping
(IDEA, BlowFish, etc.)
Backend Roles
Client ←→ Server Workflow
Mission Auth
Mission Key Management
How to Securing the data:
JSON Web Tokens
How to Persist
Document Oriented Database
How to Transmit:
Distributed / Federated
Backend Workflow
Why Node.js?
Server Part: restify
restify setup
const restify = require('restify')
const server = restify.createServer()
server.get('/', (req, res) => {
res.send(200)
})
server.listen(8080, () => {
console.log('%s listening at %s', server.name, server.url)
})
Security: node-forge
const { pki } = require('node-forge')
const caStore = pki.createCaStore([ AppRootCertPEM ])
server.post('/users', (req, res, next) => {
// CREATE USER
//
res.send(201, { cert: pki.certificateToPen(caStore[0]), uuid })
})
server.put('/users/:uuid', (req, res) => {
const { uuid } = req.params
const { pubkey, certHash, privkeyhash,
salt, iterations, digest } = req.body
// UPDATE USER
//
res.send(204)
})
Storage
MongoDB/CouchDB
Document Tree Structure
Auth
const users = db.collection('users')
passport.use(new LocalStrategy((username, password, done) => {
users.find({ username }, (err, user) => {
if (!user) return done(null, false)
if (!user.verifyPassword(password)) return done(null, false)
return done(null, user)
})
}))
server.post('/users/:name/login', passport.authenticate('local'), (req, res) => {
res.redirect(`/users/${req.params.name}`)
})
server.get('/users/:name', passport.authenticate('local'), (req, res) => {
// RETURN JWT
})
Protect Requests
const restifyCors = require('restify-cors')
const cors = restifyCors({
origins: [
'https://api.example.org',
'https://web.example.org'
],
allowHeaders: ['API-Token', 'Authorization'],
exposeHeaders: ['API-Token-Expiry']
})
server.pre(cors.preflight)
server.use(cors.actual)
Actions are
identified and protected
with JWT
const jwt = require('restify-jwt-community')
const secret = server.token
server.use(restify.authorizationParser())
server.get('/items', jwt({ secret }), (req, res) => {
if (!req.user.admin) return { res.send(401) }
res.send(200)
})
POST Item
server.use(restify.plugins.bodyParser())
server.post('/items', jwt({ secret }), (req, res) => {
const { salt, recipient, iterations, digest } = req.body
const { user } = req
// CREATE A NEW ITEM FOR USER IN DB
res.send(201, { uuid })
})
PUT Data Chunk
const crypto = require('crypto')
const items = db.collection('items')
server.put('/items/:uuid', jwt({ secret }), (req, res) => {
const { uuid } = req.params
const item = items.find({ uuid })
const { timestamp, signature, data } = req.body
const verify = crypto.createVerify('sha256')
verify.update(data + '.' + timestamp)
if (item.data) return res.send(new ConflictError())
if (!verify.verify(req.user.pubkey, signature, 'base64'))
return res.send(new PreconditionFailedError())
// SAVE DATA, SIGNATURE, TIMESTAMP
res.send(204)
})
GET User
const jwt = require('jsonwebtoken')
server.get('/users/:name', passport.authenticate('local'), (req, res) => {
const { name } = req.params
const user = users.find({ name })
let token = jwt.sign({
uuid: user.uuid,
admin: user.admin
}, secret, { expiresIn: '1m' })
res.send({
token,
username: user.name,
pubkey: user.pubkey,
items: user.items.map(item => server.router.render('items', { uuid: item.uuid }))
})
})
GET Data Chunk
const items = db.collection('items')
server.get('/items/:uuid', jwt({ secret }), (req, res) => {
const { uuid } = req.params
const item = items.find({ uuid })
const verify = crypto.createVerify('sha256')
verify.update(item.data + '.' + item.timestamp)
if (!item) return res.send(new NotFoundError())
if (item.recipient.uuid !== req.user.uuid)
return res.send(new UnauthorizedError())
if (item.expire < Date.now())
return res.send(new PreconditionFailedError())
if (!verify.verify(req.user.pubkey, item.signature, 'base64'))
return res.send(new PreconditionFailedError())
res.send(item)
})
Bonus ZKP
Share payload with a user
users/:name/recipients/:uuid
Just a simple REST Ping Pong!
It’s not that complex
(at least on the backend side)
It’s mostly a REST API
with some Crypto sugar
Your Tools
The complex part
is on the Client
and requires a lot of Crypto
https://talks.m4dz.net/zka-w-nodejs/en/ Available under licence CC BY-SA 4.0
m4dz, CC BY-SA 4.0
Courtesy of Unsplash and Pexels contributors
Powered by Reveal.js
Source code available at
https://git.madslab.net/talks