pg-manual
GitHub · JavaScript · WASM Example
A webpack-bundled browser example that uses the low-level @e4a/pg-wasm module directly, without the @e4a/pg-js SDK. Part of the postguard-examples repository.
Use this example when you want to see what @e4a/pg-js wraps. It calls the WASM bindings, the PKG HTTP API, and a Yivi popup directly. For application code, prefer the SDK shown in pg-sveltekit; the manual flow is here for reference and for projects that cannot use the SDK.
It shows two patterns:
- String encryption — encrypt and decrypt an in-memory string with
seal/Unsealer. - File streaming — encrypt and decrypt a file via
sealStream/StreamUnsealer, using the browser'sReadableStreamand StreamSaver for the output.
Running
cd pg-manual
npm install
npm run devThe dev server runs on localhost:9000. Open string.html or file.html from the index page.
Setup
The example uses webpack with the asyncWebAssembly and topLevelAwait experiments enabled, plus Node.js polyfills for https, http, url, and util:
module.exports = {
experiments: {
asyncWebAssembly: true,
topLevelAwait: true,
},
resolve: {
fallback: {
https: require.resolve('https-browserify'),
http: require.resolve('stream-http'),
url: require.resolve('url/'),
util: require.resolve('util/'),
events: false,
},
},
plugins: [
new webpack.ProvidePlugin({ process: 'process/browser' }),
],
}The PKG URL and a small Yivi-session helper live in utils.js. The helper performs an IRMA session and returns either an encryption key (USK) or a signing key pair, depending on the sort argument:
export const KeySorts = {
Encryption: 'key',
Signing: 'sign/key',
}
export const PKG_URL = 'https://main.postguard.ihub.ru.nl/pkg'Encrypt a string
Fetch the master public key, build a recipient policy and a signing identity, fetch a signing key via Yivi, then call seal:
const { seal } = await import('@e4a/pg-wasm')
const mpk = await fetch(`${PKG_URL}/v2/parameters`)
.then((r) => r.json())
.then((j) => j.publicKey)
const policy = {
Bob: {
ts: Math.round(Date.now() / 1000),
con: [{ t: 'irma-demo.sidn-pbdf.email.email', v: 'bob@example.com' }],
},
}
const pubSignId = [{ t: 'irma-demo.gemeente.personalData.fullname', v: 'Alice' }]
const privSignId = [{ t: 'irma-demo.gemeente.personalData.bsn', v: '1234' }]
const { pubSignKey, privSignKey } = await fetchKey(
KeySorts.Signing,
{ con: [...pubSignId, ...privSignId] },
undefined,
{ pubSignId, privSignId }
)
const encoded = new TextEncoder().encode(input)
const ct = await seal(mpk, { policy, pubSignKey, privSignKey }, encoded)Decrypt a string
Fetch the verification key, build an Unsealer, request a USK matching one of the recipients in the header, then unseal:
const { Unsealer } = await import('@e4a/pg-wasm')
const vk = await fetch(`${PKG_URL}/v2/sign/parameters`)
.then((r) => r.json())
.then((j) => j.publicKey)
const unsealer = await Unsealer.new(ct, vk)
const header = unsealer.inspect_header()
const keyRequest = {
con: [{ t: 'irma-demo.sidn-pbdf.email.email', v: 'bob@example.com' }],
}
const timestamp = header.get('Bob').ts
const usk = await fetchKey(KeySorts.Encryption, keyRequest, timestamp)
const [plain, policy] = await unsealer.unseal('Bob', usk)
const original = new TextDecoder().decode(plain)Encrypt and decrypt files
For files, use sealStream and StreamUnsealer with the browser's native streams. The output is written via StreamSaver:
import { createWriteStream } from 'streamsaver'
const fileWritable = createWriteStream(outFileName)
const readable = inFile.stream()
if (decrypt) {
const { StreamUnsealer } = await import('@e4a/pg-wasm')
const unsealer = await StreamUnsealer.new(readable, vk)
// …fetch USK as in the string example…
await unsealer.unseal('Bob', usk, fileWritable)
} else {
const { sealStream } = await import('@e4a/pg-wasm')
await sealStream(mpk, sealOptions, readable, fileWritable)
}A WritableStream polyfill is installed for older browsers:
import { PolyfilledWritableStream } from 'web-streams-polyfill'
if (window.WritableStream == undefined) {
window.WritableStream = PolyfilledWritableStream
}Demo credentials
The example uses the public IRMA demo issuers — anyone can issue test attributes from https://privacybydesign.foundation/attribute-index/en/irma-demo.gemeente.personalData.html. Replace these with production attributes in real applications.