diff --git a/apps/desktop/scripts/linux-preview-release.test.mjs b/apps/desktop/scripts/linux-preview-release.test.mjs index 942fb5e6c..a67e5d679 100644 --- a/apps/desktop/scripts/linux-preview-release.test.mjs +++ b/apps/desktop/scripts/linux-preview-release.test.mjs @@ -435,7 +435,7 @@ process.exit(42); encoding: 'utf8', env: { ...process.env, PATH: bin, PROPR_DESKTOP_RPM_PRIVATE_KEY: 'private-test-sentinel', PROPR_DESKTOP_RPM_PASSPHRASE: 'password-test-sentinel', PROPR_DESKTOP_RPM_PUBLIC_KEY: '-----BEGIN PGP PUBLIC KEY BLOCK-----', - PROPR_DESKTOP_RPM_FINGERPRINT: 'DD4E7F65CE2345A1F4597D643CF856B53093D3A3' }, + PROPR_DESKTOP_RPM_FINGERPRINT: '80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599' }, }); assert.equal(result.status, 1); assert.deepEqual(JSON.parse(await readFile(evidence, 'utf8')), { key: false, passphrase: false }); diff --git a/apps/desktop/scripts/rpm-signing.mjs b/apps/desktop/scripts/rpm-signing.mjs index 2851118cd..aae5d600c 100644 --- a/apps/desktop/scripts/rpm-signing.mjs +++ b/apps/desktop/scripts/rpm-signing.mjs @@ -8,7 +8,8 @@ import { promisify } from 'node:util'; import { pathToFileURL } from 'node:url'; const execFile = promisify(execFileCallback); -export const OWNER_FINGERPRINT = 'DD4E7F65CE2345A1F4597D643CF856B53093D3A3'; +// Owner replacement key effective 2026-10-19; the previous key is historical, not cross-signed. +export const OWNER_FINGERPRINT = '80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599'; const fingerprintPattern = /^[A-F0-9]{40}$/; export function requireRpmIdentity(identity) { diff --git a/apps/desktop/scripts/rpm-signing.test.mjs b/apps/desktop/scripts/rpm-signing.test.mjs index 347d9ca09..99e1f1c37 100644 --- a/apps/desktop/scripts/rpm-signing.test.mjs +++ b/apps/desktop/scripts/rpm-signing.test.mjs @@ -7,7 +7,7 @@ import { tmpdir } from 'node:os'; import { dirname, join, resolve } from 'node:path'; import { test } from 'node:test'; import { inspectSignedPreviewPayload } from './rpm-preview-payload.mjs'; -import { OWNER_FINGERPRINT, withKey, assertRpmSignatures, requireRpmIdentity, rpmContentDigest, signRpm, verifyRpm } from './rpm-signing.mjs'; +import { OWNER_FINGERPRINT, rpmIdentityFromEnvironment, withKey, assertRpmSignatures, requireRpmIdentity, rpmContentDigest, signRpm, verifyRpm } from './rpm-signing.mjs'; import { linuxPreviewTag, prepareLinuxPreview, readLinuxPreviewBundle, signLinuxPreviewBundle, stageLinuxPreviewDraft, publishLinuxPreviewDraft } from './linux-preview-release.mjs'; import { aptAcceptance } from './apt-auth-acceptance.mjs'; import { authAssets, aptAssets, signMetadata, verifyMetadata, verifyApt } from './linux-package-auth.mjs'; @@ -138,17 +138,76 @@ test('signed mode requires exact public identity inputs', async () => { } }); -test('documented owner public export has the exact complete RSA4096 identity and expiry', { - skip: missing.length ? `Missing tools: ${missing}` : false, -}, async () => { - const publicKey = await readFile(join(repositoryRoot, 'docs/static/keys/propr-desktop-release.asc'), 'utf8'); - await withKey({ fingerprint: OWNER_FINGERPRINT, publicKey }, async home => { +const PREVIOUS_FINGERPRINT = 'DD4E7F65CE2345A1F4597D643CF856B53093D3A3'; +const ownerExport = 'docs/static/keys/propr-desktop-release.asc'; +const historicalExport = `docs/static/keys/propr-desktop-release-historical-${PREVIOUS_FINGERPRINT}.asc`; + +async function inspectExport(path, fingerprint) { + const publicKey = await readFile(join(repositoryRoot, path), 'utf8'); + assert.doesNotMatch(publicKey, /PRIVATE KEY/); + return withKey({ fingerprint, publicKey }, async home => { const records = run('gpg', ['--homedir', home, '--with-colons', '--list-keys']).split('\n').map(l => l.split(':')); + assert.equal(records.filter(r => r[0] === 'pub').length, 1); + assert.equal(records.filter(r => r[0] === 'sub').length, 0); + assert.equal(run('gpg', ['--homedir', home, '--with-colons', '--list-secret-keys']).trim(), ''); const primary = records.find(r => r[0] === 'pub'); assert.equal(primary[2], '4096'); - assert.equal(new Date(Number(primary[5]) * 1000).toISOString(), '2026-10-12T13:07:27.000Z'); - assert.equal(new Date(Number(primary[6]) * 1000).toISOString(), '2028-10-11T13:11:25.000Z'); + assert.equal(primary[3], '1'); + return { publicKey, created: new Date(Number(primary[5]) * 1000).toISOString(), + expires: new Date(Number(primary[6]) * 1000).toISOString(), uids: records.filter(r => r[0] === 'uid').map(r => r[9]) }; + }); +} + +test('documented owner public export has the exact complete RSA4096 identity and expiry', { + skip: missing.length ? `Missing tools: ${missing}` : false, +}, async () => { + assert.equal(OWNER_FINGERPRINT, '80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599'); + const owner = await inspectExport(ownerExport, OWNER_FINGERPRINT); + assert.deepEqual(owner.uids, ['ProPR Desktop Release Signing']); + assert.equal(owner.created, '2026-10-19T14:09:59.000Z'); + assert.equal(owner.expires, '2028-10-18T14:09:59.000Z'); + // The previous export stays published unchanged as historical public material only. + const previous = await inspectExport(historicalExport, PREVIOUS_FINGERPRINT); + assert.deepEqual(previous.uids, ['ProPR Desktop RPM Signing']); + assert.equal(previous.created, '2026-10-12T13:07:27.000Z'); + assert.equal(previous.expires, '2028-10-11T13:11:25.000Z'); + // Neither export can stand in for the other under either fingerprint. + await assert.rejects(withKey({ fingerprint: OWNER_FINGERPRINT, publicKey: previous.publicKey }, async () => {}), /pinned, usable RSA/); + await assert.rejects(withKey({ fingerprint: PREVIOUS_FINGERPRINT, publicKey: owner.publicKey }, async () => {}), /pinned, usable RSA/); +}); + +test('production environment pin accepts only the replacement owner fingerprint with its public export', { + skip: missing.length ? `Missing tools: ${missing}` : false, +}, async t => { + const saved = { fingerprint: process.env.PROPR_DESKTOP_RPM_FINGERPRINT, publicKey: process.env.PROPR_DESKTOP_RPM_PUBLIC_KEY }; + t.after(() => { + for (const [name, value] of [['PROPR_DESKTOP_RPM_FINGERPRINT', saved.fingerprint], ['PROPR_DESKTOP_RPM_PUBLIC_KEY', saved.publicKey]]) { + if (value === undefined) delete process.env[name]; else process.env[name] = value; + } }); + const ownerKey = await readFile(join(repositoryRoot, ownerExport), 'utf8'); + const previousKey = await readFile(join(repositoryRoot, historicalExport), 'utf8'); + const root = await mkdtemp(join(tmpdir(), 'propr-rpm-pin-')); + t.after(() => rm(root, { recursive: true, force: true })); + const disposable = await key(root, 'wrong-signer'); + for (const [fingerprint, publicKey] of [[PREVIOUS_FINGERPRINT, previousKey], [PREVIOUS_FINGERPRINT, ownerKey], + [disposable.identity.fingerprint, disposable.identity.publicKey], [OWNER_FINGERPRINT.toLowerCase(), ownerKey], + [OWNER_FINGERPRINT.slice(-16), ownerKey], [undefined, ownerKey]]) { + if (fingerprint === undefined) delete process.env.PROPR_DESKTOP_RPM_FINGERPRINT; + else process.env.PROPR_DESKTOP_RPM_FINGERPRINT = fingerprint; + process.env.PROPR_DESKTOP_RPM_PUBLIC_KEY = publicKey; + assert.throws(() => rpmIdentityFromEnvironment(), /differs from owner pin/); + } + process.env.PROPR_DESKTOP_RPM_FINGERPRINT = OWNER_FINGERPRINT; + process.env.PROPR_DESKTOP_RPM_PUBLIC_KEY = ownerKey; + const identity = rpmIdentityFromEnvironment(); + assert.deepEqual(identity, { fingerprint: OWNER_FINGERPRINT, publicKey: ownerKey }); + assert.equal(await withKey(identity, async () => 'accepted'), 'accepted'); + // A matching pin with mismatched public settings (old export or another signer) fails before signing. + for (const publicKey of [previousKey, disposable.identity.publicKey]) { + process.env.PROPR_DESKTOP_RPM_PUBLIC_KEY = publicKey; + await assert.rejects(withKey(rpmIdentityFromEnvironment(), async () => {}), /pinned, usable RSA/); + } }); test('signature status parsing ignores filename tokens but requires both pinned signatures', () => { diff --git a/docs/docs/operations/linux-release-key.md b/docs/docs/operations/linux-release-key.md index e5d6221ad..1675dadf7 100644 --- a/docs/docs/operations/linux-release-key.md +++ b/docs/docs/operations/linux-release-key.md @@ -1,16 +1,20 @@ # Linux desktop release key -The owner-provisioned Linux desktop release identity is: +The current owner-provisioned Linux desktop release identity, effective for new +Linux previews signed on or after **2026-10-19**, is: -**DD4E7F65CE2345A1F4597D643CF856B53093D3A3** +**80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599** [Download the ASCII-armored public key](/keys/propr-desktop-release.asc). This is public information; the export contains no private key material. -The RSA4096 primary signing key was issued on **2026-10-12** and expires on -**2028-10-11**. Its historical user ID is `ProPR Desktop RPM Signing`; the same -identity authenticates the new DEB/RPM preview checksums and APT repository metadata. -It does not authenticate older unsigned DEBs retroactively. +The RSA4096 primary signing key has user ID `ProPR Desktop Release Signing`. It was +issued on **2026-10-19** and expires on **2028-10-18**. It has no subkeys; the +primary key itself signs. For new previews it signs native RPM headers and +payloads, the release `SHA256SUMS` (as `SHA256SUMS.asc`), and the APT repository +metadata (`APT-InRelease` and `APT-Release.gpg`). DEB payloads are unchanged and +carry no embedded signature; APT authenticates them through the signed metadata +and package hashes. This key does not authenticate older releases retroactively. Inspect the downloaded export with GnuPG without installing host trust: @@ -18,18 +22,68 @@ Inspect the downloaded export with GnuPG without installing host trust: gpg --show-keys --with-colons --with-fingerprint propr-desktop-release.asc ``` -Require exactly one `pub` record, RSA4096, and the full fingerprint above in its -following `fpr` record. Compare this notice through independently reviewed project -documentation, not only a key delivered alongside an installer. Reject expired, -revoked, or different keys. See [Linux package authentication](linux-rpm-signing.md) -for checksum verification and scoped APT trust. APT verifies repository metadata -and hashes, not an embedded signature inside each DEB. +Require exactly one `pub` record, RSA4096, no `sub` records, and the full +fingerprint above in its following `fpr` record. Compare this notice through +independently reviewed project documentation, not only a key delivered alongside +an installer. Reject expired, revoked, or different keys. See +[Linux package authentication](linux-rpm-signing.md) for checksum verification and +scoped APT trust. APT verifies repository metadata and hashes, not an embedded +signature inside each DEB. -The owner must keep encrypted offline backups and a revocation certificate under -restricted custody; this notice specifies the required procedure, not an audit of -those backups. Never put private exports, passwords, backup locations, or recovery -material in issues, documentation, artifacts, or CI logs. Test restoration using a -controlled owner process; implementation tests use disposable keys only. +## 2026-10-19 rotation from the previous identity + +The previous identity was: + +- Fingerprint **DD4E7F65CE2345A1F4597D643CF856B53093D3A3** +- User ID `ProPR Desktop RPM Signing`, RSA4096 primary signing key +- Issued **2026-10-12**, expires **2028-10-11** +- [Historical public export](/keys/propr-desktop-release-historical-DD4E7F65CE2345A1F4597D643CF856B53093D3A3.asc) + (byte-for-byte the export previously published as `propr-desktop-release.asc`) + +The owner no longer has the passphrase for the previous private key and generated +a replacement key locally. **No compromise has been reported**, and the previous +key has **not** been revoked. Because the old key cannot sign, the replacement is **not +cross-signed** by it. The transition is instead authenticated only by this +reviewed project documentation and the code pin in the authenticated +`integry/propr` repository: compare the new fingerprint here with the pin in +`apps/desktop/scripts/rpm-signing.mjs` on the reviewed main branch. Do not expect +or accept a "transition statement" signed by the old key. + +The previous key signs nothing new after this rotation. Releases that were +signed with it keep their original signatures, assets and trust classification +unchanged; verify them only with the historical export and its own fingerprint. +Verification against the new code pin says nothing about those older releases, +and nothing here re-labels or re-signs them. + +### Consumer trust updates + +To verify a new preview, obtain the new public export from this notice and confirm +its full fingerprint as above. Then: + +- **RPM**: import the new public key into the RPM database you use for ProPR + previews (`sudo rpmkeys --import propr-desktop-release.asc`, or the equivalent + `rpm --import`). `rpmkeys --checksig --verbose` must report both the header and + the header+payload signature with key ID `cc4e1599` (the fingerprint suffix). + Keep the old key only if you still need to verify older RPMs signed with it; + otherwise remove it (for example `sudo rpm -e gpg-pubkey-3093d3a3` on RPM 4.x). + It is not revoked, so removal is housekeeping rather than an emergency. +- **Direct downloads**: verify `SHA256SUMS.asc` in a fresh isolated keyring that + contains only the new export, then check `SHA256SUMS`. +- **APT repository bundle**: replace the source-scoped keyring named in + `signed-by=` with a binary export of the new key (or create a new keyring file + and point the new preview's `deb` line at it). Do not add either key to + `/etc/apt/trusted.gpg` or `trusted.gpg.d`. A repository bundle signed by the new + key fails `apt update` with a keyring containing only the old key; that failure + is expected and must not be overridden. + +## Custody, compromise and future rotation + +The owner must keep encrypted offline backups, the passphrase, and a revocation +certificate under restricted custody; this notice specifies the required +procedure, not an audit of those backups. Never put private exports, passwords, +backup locations, or recovery material in issues, documentation, artifacts, or CI +logs. Test restoration using a controlled owner process; implementation tests use +disposable keys only. For a suspected compromise or a key question, contact the repository maintainers through [integry/propr on GitHub](https://github.com/integry/propr/issues). @@ -42,11 +96,15 @@ must stop trusting the affected key; local RPM databases and scoped APT keyrings do not discover revocations automatically. Consult the latest reviewed notice before installing. -Before expiry or rotation, publish both complete fingerprints, effective dates, -and the replacement public export, authenticating the transition with the old -key where safe. Review the code pin and public settings together. Preserve -historical notices and immutable assets with their original trust classification; -never relabel them or bypass verification to accommodate expiry. +Before expiry or a future rotation, publish both complete fingerprints, effective +dates, and the replacement public export, authenticating the transition with the +old key where the owner still controls it. When the old key is unavailable, as in +the 2026-10-19 rotation, say so explicitly and rely on reviewed documentation. +Review the code pin and public settings together: the protected +`PROPR_DESKTOP_RPM_PUBLIC_KEY` and `PROPR_DESKTOP_RPM_FINGERPRINT` settings must +match the reviewed pin before any signing run. Preserve historical notices and +immutable assets with their original trust classification; never relabel them or +bypass verification to accommodate expiry. This notice and download are delivered through the normal reviewed documentation pipeline. They do not establish a live production APT endpoint. diff --git a/docs/docs/operations/linux-rpm-signing.md b/docs/docs/operations/linux-rpm-signing.md index 5ba4a1a16..323a3c8c7 100644 --- a/docs/docs/operations/linux-rpm-signing.md +++ b/docs/docs/operations/linux-rpm-signing.md @@ -1,8 +1,13 @@ # Linux package authentication New public Linux previews authenticate **both DEB and RPM packages, on x64 and -arm64**. The owner identity is **DD4E7F65CE2345A1F4597D643CF856B53093D3A3**. -See the independent [release-key notice and public download](linux-release-key.md). +arm64**. The owner identity for previews signed on or after 2026-10-19 is +**80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599** (`ProPR Desktop Release Signing`, +RSA4096, expires 2028-10-18). It replaces +DD4E7F65CE2345A1F4597D643CF856B53093D3A3, whose passphrase is no longer available; +no compromise has been reported, the old key is not revoked, and it has not +cross-signed the new one. See the independent [release-key notice, rotation details and public +downloads](linux-release-key.md). The main-only `desktop-linux-signed-preview.yml` consumes the exact finalized four-package artifact from a successful main-dispatched unsigned preview run. @@ -25,6 +30,16 @@ Its compatibility names remain unchanged: | `PROPR_DESKTOP_RPM_PUBLIC_KEY` | Repository variable containing the public export | | `PROPR_DESKTOP_RPM_FINGERPRINT` | Repository variable with the complete owner fingerprint | +The code pin `OWNER_FINGERPRINT` in `apps/desktop/scripts/rpm-signing.mjs` is the +reviewed source of truth. Signing refuses to start unless +`PROPR_DESKTOP_RPM_FINGERPRINT` equals it exactly, the public export imports as +exactly that one unexpired, unrevoked RSA signing primary key, and the private key +matches it. For the 2026-10-19 rotation a human administrator separately replaces +the protected private-key and passphrase secrets and sets both public settings to +`80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599` and the export in +`docs/static/keys/propr-desktop-release.asc`. Until then signing fails closed; +there is no fallback to the previous identity. + Private credentials are confined to the protected read-only signing job. The entry point removes them from the environment before package parsers run. GnuPG uses temporary private files, scrubbed subprocess environments, exact-primary @@ -78,7 +93,10 @@ explicitly authenticates direct DEB downloads; `apt install ./package.deb` alone does not establish that provenance. RPM users additionally require both native signatures with `rpmkeys --checksig --verbose`. Use `dnf --setopt=localpkg_gpgcheck=1` or ordinary `zypper install` after bootstrapping -the confirmed RPM key. Never override a missing key or failed signature. +the confirmed RPM key. After the 2026-10-19 rotation, import the new public key +(both signature lines must report key ID `cc4e1599`); a database holding only the +previous key reports `NOKEY` for new RPMs. Never override a missing key or failed +signature. ## Exported APT repository @@ -93,6 +111,8 @@ recovered from InRelease. Use a source-scoped binary or armored public keyring: deb [signed-by=/absolute/path/propr-desktop-release.gpg] file:/absolute/path/apt-repository preview main ``` +After the 2026-10-19 rotation, point `signed-by=` at a keyring containing the new +key only; a keyring holding just the previous key correctly rejects new metadata. The keyring and repository must be readable by APT. A locally served copy can use a local HTTP URL with the same scoped keyring. There is no production URL to configure. Run `apt update` before downloading, and stop on authentication/hash @@ -115,6 +135,13 @@ Resume failed staging with the same `propr-desktop-linux-signed-` artifa Do not re-sign or overwrite an existing candidate: signatures can change bytes. Unavailable inputs require a new explicitly selected source/run, not a fallback. +Rotation does not change history. Any preview already signed by +DD4E7F65CE2345A1F4597D643CF856B53093D3A3 keeps its immutable assets, signatures and +original classification; verify it with the [historical public +export](/keys/propr-desktop-release-historical-DD4E7F65CE2345A1F4597D643CF856B53093D3A3.asc). +The new code pin and key verify only previews signed on or after 2026-10-19 and do +not authenticate older releases. + Unsigned schema 2 remains private validation evidence and cannot be newly published. Historical schema 3 (`rpm-signed-preview`, `-rpm-signed`) authenticated RPMs only; its DEBs and claimed checksums were unsigned. Historical immutable @@ -187,7 +214,7 @@ key notice. Run from the repository root, with a new output directory: gh run download "$SIGNING_RUN_ID" \ --name "propr-desktop-linux-signed-$SIGNING_RUN_ID" --dir signed-preview export PROPR_DESKTOP_RPM_PUBLIC_KEY="$(cat propr-desktop-release.asc)" -export PROPR_DESKTOP_RPM_FINGERPRINT=DD4E7F65CE2345A1F4597D643CF856B53093D3A3 +export PROPR_DESKTOP_RPM_FINGERPRINT=80FE0A7667A92A0AD3CEAE6D4C2FF8C3CC4E1599 PROPR_DESKTOP_REAL_LINUX_PACKAGE_ACCEPTANCE=1 \ node apps/desktop/scripts/run-signed-installed-rpm-acceptance.mjs \ --rpm "signed-preview/ProPR-Desktop-$VERSION-linux-x64.rpm" \ diff --git a/docs/static/keys/propr-desktop-release-historical-DD4E7F65CE2345A1F4597D643CF856B53093D3A3.asc b/docs/static/keys/propr-desktop-release-historical-DD4E7F65CE2345A1F4597D643CF856B53093D3A3.asc new file mode 100644 index 000000000..5b0bbb8bb --- /dev/null +++ b/docs/static/keys/propr-desktop-release-historical-DD4E7F65CE2345A1F4597D643CF856B53093D3A3.asc @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGrM248BEAC54UTrJXUw/iDesahSJLZ8vN8Z8zFtWrBjkuHWYPzqeT/b+dSY +VUmgbhyzPta1LWp6tJpLK54s0aGM+7WpElBA1GiVLttSVshqLGTxy0TYl1jcxN02 +HfqRJS+Je+RHqxPT1yky6wgpXcUHx73Tby06SypHl33xd0g89Sv3sFivQbwYHcvF +mzzpUbuWbYBIa35Soc7OG6nKxcBIn8aN1obXTq8Da/CXmrXh/2RveoMrhVI98T8b +cAlCZ2EFU+uKY5IGQltnQojU68ZNm8RKtmTnhq2bZskhfltSzx0hy7gRKRdzozfx +fdSN2xJaqNzuA5qBHnj4/V45jJcgEOVsnXISP4ro1NP0h96dAFmI+hjmCmh4HN9m +zS3EH53XQ/Vn03IlOf2tFe4Ywxfl5hgjhqNyBuqYXX5xUeUFw6p+s8mrrfEBWL1V +9Yvq+C/a+sjxfEzOUcXDJisiqszY3AuZcPVs6noVIvIlFTxw31INoA0oduqOeNQM +9waYd5pbq90IIM1y2yHIAj9cH1O163arHET6X/6NlNWjImb2sVgY49yuhmVggF30 +Gd1FrJrHHBW+IDCeh2LvVDras/M03NuQBY3O2Luxz8IiRe3V6d6OMSpQaqBVVmzm +M17C3V+jr6sDG/Hi4FCT/M5HuHi5ebnv8hPp/R8FDJyCbR3dVM26qJm7xQARAQAB +tBlQcm9QUiBEZXNrdG9wIFJQTSBTaWduaW5niQJXBBMBCABBAhsDBQsJCAcCAiIC +BhUKCQgLAgQWAgMBAh4HAheAFiEE3U5/Zc4jRaH0WX1kPPhWtTCT06MFAmrM3H0F +CQPCZ+4ACgkQPPhWtTCT06Mi0hAAmzhXnwWXYODbf5+9/bSMJhE/ise7wL0yslSP +Sr+3WG0/7PJ824l9zF81Z4dAd8fp7nFTfGTK6E3RcvInRWgmAhWV2+4r0xGOkB0f +EQrEh3URsSo2be82FeY185CkmbP3j3g3+ieMxVHCCzm9AGObhRq/EQlwZJ3B/wpS +h6vbBZ3gHJL9TKxNHMS3HWd17gaDAbizfJL8k+7MgeTRyTPdhDddhYDtm2Y3VFyO +IBDPFQE0e3wUFXmCWY7LIMkdWhGd0Q878PtBAf+L6fyo0f2ztIQ4MQp64KpCI3PG +gzzm2RvSZlkpTvxZNnHgD0CzccVDhTub/YieSkn18K6znL4wfjFHM5RmUGDjwA/x +A2dU8jo9HBC/ucoJBw1XNM/enPzGBUZtXOakHGmzE5v6cw9ESIkAFh5reFCMZPFu +n5HFZoABmw7v/iFN3i05c1TazVqxehIY4u1pDJMcM6yNQfHDMHf1FH25EKVzL46n +UYfNyicu8N8fiA+BOJ+Wp+PMDZNtxMmOyTYCGbFJcxmTgTYxwK/Ns99QBIsXoexS +uL/dnGTZQitFc0aC96EH4Q4yWMXuquiPNBpx+jih5sjWrESADMLP1U0tNogU7xyK +dA8U1MikayEV0iVsz3IKbipMg86CyC/YNfsmXAVOJCKfUEvEJNlHNbv7eJbW9qKr +SmHB3TE= +=U47I +-----END PGP PUBLIC KEY BLOCK----- diff --git a/docs/static/keys/propr-desktop-release.asc b/docs/static/keys/propr-desktop-release.asc index 5b0bbb8bb..3900413e8 100644 --- a/docs/static/keys/propr-desktop-release.asc +++ b/docs/static/keys/propr-desktop-release.asc @@ -1,29 +1,29 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -mQINBGrM248BEAC54UTrJXUw/iDesahSJLZ8vN8Z8zFtWrBjkuHWYPzqeT/b+dSY -VUmgbhyzPta1LWp6tJpLK54s0aGM+7WpElBA1GiVLttSVshqLGTxy0TYl1jcxN02 -HfqRJS+Je+RHqxPT1yky6wgpXcUHx73Tby06SypHl33xd0g89Sv3sFivQbwYHcvF -mzzpUbuWbYBIa35Soc7OG6nKxcBIn8aN1obXTq8Da/CXmrXh/2RveoMrhVI98T8b -cAlCZ2EFU+uKY5IGQltnQojU68ZNm8RKtmTnhq2bZskhfltSzx0hy7gRKRdzozfx -fdSN2xJaqNzuA5qBHnj4/V45jJcgEOVsnXISP4ro1NP0h96dAFmI+hjmCmh4HN9m -zS3EH53XQ/Vn03IlOf2tFe4Ywxfl5hgjhqNyBuqYXX5xUeUFw6p+s8mrrfEBWL1V -9Yvq+C/a+sjxfEzOUcXDJisiqszY3AuZcPVs6noVIvIlFTxw31INoA0oduqOeNQM -9waYd5pbq90IIM1y2yHIAj9cH1O163arHET6X/6NlNWjImb2sVgY49yuhmVggF30 -Gd1FrJrHHBW+IDCeh2LvVDras/M03NuQBY3O2Luxz8IiRe3V6d6OMSpQaqBVVmzm -M17C3V+jr6sDG/Hi4FCT/M5HuHi5ebnv8hPp/R8FDJyCbR3dVM26qJm7xQARAQAB -tBlQcm9QUiBEZXNrdG9wIFJQTSBTaWduaW5niQJXBBMBCABBAhsDBQsJCAcCAiIC -BhUKCQgLAgQWAgMBAh4HAheAFiEE3U5/Zc4jRaH0WX1kPPhWtTCT06MFAmrM3H0F -CQPCZ+4ACgkQPPhWtTCT06Mi0hAAmzhXnwWXYODbf5+9/bSMJhE/ise7wL0yslSP -Sr+3WG0/7PJ824l9zF81Z4dAd8fp7nFTfGTK6E3RcvInRWgmAhWV2+4r0xGOkB0f -EQrEh3URsSo2be82FeY185CkmbP3j3g3+ieMxVHCCzm9AGObhRq/EQlwZJ3B/wpS -h6vbBZ3gHJL9TKxNHMS3HWd17gaDAbizfJL8k+7MgeTRyTPdhDddhYDtm2Y3VFyO -IBDPFQE0e3wUFXmCWY7LIMkdWhGd0Q878PtBAf+L6fyo0f2ztIQ4MQp64KpCI3PG -gzzm2RvSZlkpTvxZNnHgD0CzccVDhTub/YieSkn18K6znL4wfjFHM5RmUGDjwA/x -A2dU8jo9HBC/ucoJBw1XNM/enPzGBUZtXOakHGmzE5v6cw9ESIkAFh5reFCMZPFu -n5HFZoABmw7v/iFN3i05c1TazVqxehIY4u1pDJMcM6yNQfHDMHf1FH25EKVzL46n -UYfNyicu8N8fiA+BOJ+Wp+PMDZNtxMmOyTYCGbFJcxmTgTYxwK/Ns99QBIsXoexS -uL/dnGTZQitFc0aC96EH4Q4yWMXuquiPNBpx+jih5sjWrESADMLP1U0tNogU7xyK -dA8U1MikayEV0iVsz3IKbipMg86CyC/YNfsmXAVOJCKfUEvEJNlHNbv7eJbW9qKr -SmHB3TE= -=U47I +mQINBGrWJLcBEAC5xx+vmLZ7WTWDtbBsxFygpAZh4zHkDYlNCG/6rDgXDdIpAlHe +7ifmcx9e2ahRKazLucdAsrjODh6UvnSn82w4itqnyHo+NdqbfO13bUFodtuJDzhh +c4VO1dYes/eim1rgwiL6kFUon7so6kG8P3/aBHUt7fMFAC1l2KnsoIzn3771jZfl +joLdkUPMeU++ETggEw12iO////R/ozpNikHrYfLrHcCbBGR8LxJdnl97NV8cLzD3 +ho7DJW1ms4uBtorc5gs6HvtyCgTyKZ8928ZE/IBAxS8toiqjJr9w5C8zBZYo5Qps +yDo+vS63ew2s072tx8X1TEQGILW9t52cUg36W/+MTyhqBMZ/h/5uqpGCN1YaOS1h +ES7t5VH7kFGGfytGPEjCbhDJgPJdgHmm+/ZVhV0h5Qb5MuSi4rb34HHu93Xxhm9s +EWGbolU/tcTTgF0MlQjxXhEtWUrs/93J7hHyylnWvcb5LWucU/11Sh++GP6of16v +WFDwM/pCS3bp9YsrRuKVKIiRSP+Vv5u2op5Ni7FaagP3ACoad07EyfWcnQaVeXKx +LzTw0PtBkw5+nw8TkI80sS1K8MSb+FGBzbP3B3r9coL6bvqAUEwFmn6izJg2wC80 +w6FDLcVj6frDOL7Dj5OMzOlYT2nP1p0AqJF404vLMu1DLYkn4UtC76BFcwARAQAB +tB1Qcm9QUiBEZXNrdG9wIFJlbGVhc2UgU2lnbmluZ4kCVwQTAQgAQRYhBID+CnZn +qSoK086ubUwv+MPMThWZBQJq1iS3AhsDBQkDwmcABQsJCAcCAiICBhUKCQgLAgQW +AgMBAh4HAheAAAoJEEwv+MPMThWZnsEP/2yG3zpnMensASh7U5XCtKNOh9fTaCss +3Ch/F/GtcBoNDbKwOlG9uVT5S3uFk79Dr4NG7v8wj40kxZMS6Hy9XbTjo8PQtWE+ +NR3+rdIuskND/jZrBcG/WQaLz3NQ62RWuq+gf1lYbKdnpM/opOO11/CRCL090vWG +/C+nhfi+mWhhpCExVY+nUZ4lpnT0tPGml36jQ1R+8fwqhgH8i3KkqXxg57O2AZHa +O+W7cIkN5ARElxbrXf8IhJhfGA2dfZqcFiK3M1BX2J+MNC+eufPC/iMqfFXoq8R9 +/uAi5WmWD/alhNvRz/Whm5OvX4Ky+Lqh+sul3Lb73khHQ1b9cUYXMMZdU1ec9ISH +LVr5WNOazi/QZirU2cNQku0oLYZBBGdhPeMmRiv/aql8CUUMbo1Ude6I/CesPUUu +hiNQj0Z+5q+SXmKhcTvxs792bWbaCO2FBQlFvd1yzvbDAKHJRh7WtmRnTKURe5F2 +JySijpGmuwvuZ4bNBObv6q1jWaUpJm3yZGQ/J1t1l7lL2KAZw87X0o6DmRliPmh0 +hw6tf0/o1iF+kXY1eaX/2PTjfIw0Lz3l9j/u5cMaYpQsd4iDfMfef5Hh/34M4vmi +D1mQ4LbZ103KYi7t0eWsZtVstrNiWkjL1KJILz/dMeT+sWP1u9Jz8Kw/hmn0OAjK +rI+mn5ht6WkR +=QZi8 -----END PGP PUBLIC KEY BLOCK-----