/* ===================================================================== EINE BENACHRICHTIGUNG AUFMACHEN — so, wie der Browser es tut. RFC 8291, in die andere Richtung gelesen: aus dem verschlüsselten Paket wieder Klartext machen, mit dem privaten Schlüssel des Geräts. WARUM ALS EIGENE DATEI (30.09.2026) Das stand als lokale Funktion in `pruef-push-weg.mjs`. Seit heute braucht `pruef-abzeichen.mjs` dieselbe Rechnung — sie muss in den Inhalt der Nachricht hineinsehen, um zu prüfen, dass die Zahl fürs App-Symbol wirklich mitkommt. Die Rechnung ein zweites Mal abzuschreiben wäre genau die Sorte Doppelung, die sich später auseinanderentwickelt: Ändert sich am Verfahren etwas, wird eine der beiden Abschriften nachgezogen, die andere nicht — und dann meldet eine Prüfung einen Fehler, den es nicht gibt, oder schlimmer: keinen, den es gibt. NUR FÜR PRÜFUNGEN. Im Betrieb macht der Browser das selbst; der Server hat den privaten Schlüssel des Geräts nie gesehen und soll ihn auch nie sehen. ===================================================================== */ import { hkdfSync, createDecipheriv } from "node:crypto"; /** Macht ein aes128gcm-Paket wieder auf. * * @param {Buffer} paket was auf der Leitung lag * @param {object} wer der ECDH des Geräts (mit privatem Teil) * @param {Buffer} geheim16 die 16 Bytes `auth` der Anmeldung * @returns {string} der Klartext * * Wirft, wenn Schlüssel oder Geheimnis nicht passen — und genau das * ist die Gegenprobe in `pruef-push-weg`: Mit dem falschen Schlüssel * MUSS es scheitern. */ export function pushAufmachen(paket, wer, geheim16) { const salz = paket.subarray(0, 16); const len = paket.readUInt8(20); const senderOeff = paket.subarray(21, 21 + len); const rest = paket.subarray(21 + len); const gemeinsam = wer.computeSecret(senderOeff); const prkInfo = Buffer.concat([ Buffer.from("WebPush: info\0"), wer.getPublicKey(), senderOeff, ]); const ikm = Buffer.from(hkdfSync("sha256", gemeinsam, geheim16, prkInfo, 32)); const cek = Buffer.from(hkdfSync("sha256", ikm, salz, Buffer.from("Content-Encoding: aes128gcm\0"), 16)); const nonce = Buffer.from(hkdfSync("sha256", ikm, salz, Buffer.from("Content-Encoding: nonce\0"), 12)); const tag = rest.subarray(rest.length - 16); const daten = rest.subarray(0, rest.length - 16); const dec = createDecipheriv("aes-128-gcm", cek, nonce); dec.setAuthTag(tag); const klar = Buffer.concat([dec.update(daten), dec.final()]); return klar.subarray(0, klar.length - 1).toString("utf8"); // Polsterbyte weg }