OverviewFilesCommitsIssuesDocsReleasesPeers

p2pbuilders / web / js / verify.js

37 lines · javascript ref:
// verify.js — record authenticity. The ONLY thing that makes a record
// trustworthy is a valid Ed25519 signature from the key it claims as author.
// The transport (which outbox relayed it) carries no authority — so an attacker
// who relays a victim-labelled outbox full of fabricated records gains nothing:
// every fabricated record fails this check and is dropped by the merge.
//
//   verifyRecord -> 'ok'           signature valid, signer === claimed author
//                   'bad'          forged / tampered / wrong signer / unsigned (secure mode)
//                   'unverifiable' no crypto backend at all (cooperative dev only)

import { canonical, ownerOf } from './canon.js'
import { verify as edVerify, isSecure, ready as cryptoReady } from './crypto.js'

const HEX64 = /^[0-9a-f]{64}$/i
const NS = 'peerit'

function signedMessage (type, data) {
  // Matches what identity.sign signs: pear.app.<driveKey>:<namespace>:<payload>.
  // _ns is pinned to the constant and _dk is validated as 64-hex by the caller,
  // so neither can contain a ':' that would shift the field boundaries.
  return `pear.app.${data._dk}:${NS}:` + canonical(type, data)
}

export async function verifyRecord (type, data) {
  if (!data || !data._k) return 'unverifiable'
  // The signer must BE the claimed author — no signing as someone else.
  if (data._k !== ownerOf(type, data)) return 'bad'
  await cryptoReady()
  if (!isSecure()) return 'unverifiable' // no platform crypto -> cooperative dev
  if (!data._sig) return 'bad'           // secure mode: unsigned is untrusted
  // Domain pinning: reject anything not signed in peerit's own context, and any
  // malformed driveKey/pubkey that could make the signed envelope ambiguous.
  if (data._ns !== NS) return 'bad'
  if (!HEX64.test(data._dk || '') || !HEX64.test(data._k)) return 'bad'
  return (await edVerify(data._k, signedMessage(type, data), data._sig)) ? 'ok' : 'bad'
}