Public-key cryptography

Sign and verify data

Use digital signatures to bind exact bytes to a private-key holder and verify identity, context, format, and freshness before trusting the result.

A digital signature is created with a private key and verified with the public key. It proves more than a checksum, but less than “this action is safe.”

Unlike an HMAC, verification needs no secret. Anyone holding the public key can check the signature, and nobody without the private key can forge one. That asymmetry gives you non-repudiation: the signer cannot claim a verifier forged the message, because verifiers never held signing ability.

Ed25519 is the common modern choice, and Node supports it directly:

import { generateKeyPairSync, sign, verify } from 'node:crypto'

const { publicKey, privateKey } = generateKeyPairSync('ed25519')

const manifest = Buffer.from(JSON.stringify({
  project: 'shipd', version: '2.4.1',
  artifact: 'sha256:7c2df1a9...', purpose: 'release', ts: 1754236800
}))

const signature = sign(null, manifest, privateKey)
verify(null, manifest, publicKey, signature) // true

Change one byte of the manifest and verify returns false.

Sign exact bytes, with context

Signatures cover bytes, not meaning. Define a canonical or exact byte representation and verify the bytes you received, before any parsing and re-serialization. JSON with unstable key order is a classic way to sign one thing and verify another.

Include purpose and version inside the signed payload. A signature over bare data can be replayed in a different context; a signature over purpose: 'release' cannot be presented as a login token.

A valid signature is not approval

Verify with the expected public key — not a key that arrived with the message. Then keep going, because policy questions remain.

Here is the trap: a deployment manifest has a valid signature but names an old artifact with a known vulnerability. The signature proves who signed those bytes, not that the release is current or approved now. This rollback pattern is a real attack on update systems: the attacker replays your own legitimately signed old release.

So a valid signature from an unexpected signer, or for an old message, should still be rejected by policy. Verification must also enforce the expected signer, project, freshness, and authorization. The cryptography answers “who signed these exact bytes.” Everything after that is your application’s decision.

Practice

Define and save the exact signed bytes for a release manifest containing project, version, artifact digest, purpose, and timestamp. Verify a valid current manifest with the expected public key. Then replay an old valid manifest and prove a freshness or release-policy check rejects it.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →