Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@47ng/opaque-server

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@47ng/opaque-server

An implementation of the OPAQUE key exchange protocol in WASM(WebAssembly)

  • 2.1.5
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
30
increased by30.43%
Maintainers
1
Weekly downloads
 
Created
Source

@47ng/opaque-server

The OPAQUE key exchange protocol in WASM (WebAssembly), for Node.js. This implementation is based on facebook/opaque-ke.

Built as CJS for Node.js from 47ng/opaque-wasm (a fork of marucjmar/opaque-wasm using Ristretto rather than the NIST P-256 curve).

Client (browser) counterpart is available in @47ng/opaque-client.

Installation

npm install @47ng/opaque-server
yarn add @47ng/opaque-server
pnpm add @47ng/opaque-server

Usage

This implements the OPAQUE protocol overview for a stateless server and with recommended security practices.

Setup

You'll need to create a ServerSetup first, which is to be treated as a secret.

Changing it will invalidate your existing saved credentials, as it allows clients to also authenticate your server (mutual authentication). You can treat it kind of like a signature private key.

import { ServerSetup } from '@47ng/opaque-server'
import { hex } from '@47ng/codec'

const serverSetup = ServerSetup.serialize(new ServerSetup())

// serverSetup is a byte array, you can convert it to whatever
// string format suits your application (eg: hexadecimal here):
console.info(`Generated OPAQUE server setup: ${hex.encode(serverSetup)}`)

When starting your server, assuming you obtain this serialized ServerSetup from a secure location (a secret management service like Hashicorp Vault, a mounted file or an environment variable), you can hydrate it like so:

import { ServerSetup } from '@47ng/opaque-server'
import { hex } from '@47ng/codec'

const serverSetup = ServerSetup.deserialize(
  hex.decode(OPAQUE_SERIALIZED_SERVER_SETUP)
)

You will then pass this server setup to the Registration and Login handlers.

Registration (signup)

OPAQUE requires two handshakes to perform a signup (technically one and a half, the final response has no cryptographic use to the client):

Pseudo-code:

import { HandleRegistration } from '@47ng/opaque-server'
import crypto from 'node:crypto'

// Request handler 1 (example path: `/registration/request`)
async function opaqueRegistrationRequest(request, response) {
  const registration = new HandleRegistration(serverSetup)
  const registrationResponse = registration.start(
    request.body.username,
    request.body.registrationRequest
  )
  const nonce = crypto.randomBytes(32).toString('hex')
  await keyValueStore.set({
    key: nonce,
    value: request.body.username,
    ttl: 120, // Something short, here 2 minutes
  })
  response.send(registrationResponse)
  registration.free()
}

// Request handler 2 (example path: `/registration/record`)
async function opaqueRegistrationRecord(request, response) {
  const registration = new HandleRegistration(serverSetup)
  // Do not trust client-provided usernames in the request body here:
  // https://github.com/facebook/opaque-ke/issues/276#issuecomment-1162609521
  const username = await keyValueStore.get({
    key: request.body.nonce,
  })
  const passwordFile = registration.finish(request.body.registrationRecord)
  await db.insert({
    username,
    passwordFile,
  })
  await keyValueStore.del({ key: request.body.nonce })
  response.status(204).send()
  // Note: there is no need to free the `registration` object here,
  // it's been taken care of by the call to `finish`.
}

Note: registration doesn't perform key exchange/agreement, so a login step is necessary after signup to establish a shared key.

Login

OPAQUE requires two handshakes to perform a login.

At the end of the second handshake, the server will be able to use the key agreed upon, and the client will already have the same key, so you can start using that key from the second response.

Pseudo-code:

import { HandleLogin } from '@47ng/opaque-server'

// Request handler 1 (example path: `/login/request`)
async function opaqueLoginRequest(request, response) {
  const login = new HandleLogin(serverSetup)
  const { passwordFile } = await db.findOne({
    where: { username: request.body.username },
  })
  const loginResponse = login.start(
    passwordFile,
    request.body.username,
    request.body.loginRequest
  )
  const loginState = login.serialize()
  const nonce = crypto.randomBytes(32).toString('hex')
  await keyValueStore.set({
    key: nonce,
    value: {
      username: request.body.username,
      loginState,
    },
    ttl: 120, // Something short, here 2 minutes
  })
  response.send({
    nonce,
    loginResponse,
  })
  login.free()
}

// Request handler 2 (example path: `/login/final`)
async function opaqueLoginFinal(request, response) {
  const { username, loginState } = await keyValueStore.get({
    key: request.body.nonce,
  })
  const login = HandleLogin.deserialize(loginState)
  const sessionKey = login.finish(request.body.loginFinal)
  await keyValueStore.del({ key: request.body.nonce })
  response.setAuthCookiesFor(username)
  response.send(...)
  // Note: there is no need to free the `login` object here,
  // it's been taken care of by the call to `finish`.
}

Keywords

FAQs

Package last updated on 24 Mar 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc