Skip to content

Commit 5684cff

Browse files
committed
test(e2ee): use real GnuPG-produced fixture for raw private key import
1 parent 8b75f6b commit 5684cff

2 files changed

Lines changed: 42 additions & 17 deletions

File tree

apps/fluux/src/e2ee/WebOpenPGPPlugin.test.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* realm-sensitive `instanceof Uint8Array` checks that fail under
1010
* jsdom (which uses a different realm than Node's globals).
1111
*/
12+
import { readFileSync } from 'node:fs'
13+
import { resolve } from 'node:path'
1214
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
1315
import {
1416
InMemoryStorageBackend,
@@ -24,6 +26,8 @@ import { WebOpenPGPPlugin } from './WebOpenPGPPlugin'
2426
import { clearSessionPassphrase, setSessionPassphrase } from './webPassphraseStore'
2527
import type { KeyBundle } from './OpenPGPPluginBase'
2628

29+
const FIXTURES_DIR = resolve(__dirname, 'fixtures')
30+
2731
// Expose the crypto-layer protected methods so we can round-trip
2832
// without going through the full XEP-0373 envelope handling.
2933
class TestableWebOpenPGPPlugin extends WebOpenPGPPlugin {
@@ -1243,33 +1247,36 @@ describe('WebOpenPGPPlugin', () => {
12431247
).rejects.toMatchObject({ code: 'unsupported-key-algorithm', kind: 'permanent' })
12441248
})
12451249

1246-
it('imports a raw armored TSK', async () => {
1247-
// Produce a raw armored PRIVATE KEY BLOCK — exactly what
1248-
// `gpg --export-secret-keys --armor` emits. The key is encrypted
1249-
// with a user passphrase via S2K, NOT wrapped in an SKESK message.
1250-
const { generateKey } = await import('openpgp')
1251-
const { privateKey } = await generateKey({
1252-
type: 'ecc',
1253-
curve: 'curve25519Legacy' as const,
1254-
userIDs: [{ name: 'Alice', email: 'alice@example.com' }],
1255-
passphrase: 'gnupg-secret',
1256-
format: 'object',
1257-
})
1258-
const armoredPrivateKey = privateKey.armor()
1250+
it('imports a real GnuPG-produced armored TSK (interop fixture)', async () => {
1251+
// Fixture: `gnupg_modern_key.asc` was generated with real GnuPG
1252+
// (gpg 2.5.18) via:
1253+
// gpg --quick-gen-key '…' ed25519 default 0
1254+
// gpg --quick-add-key <FP> cv25519 encr 0
1255+
// gpg --export-secret-keys --armor <FP>
1256+
// Primary key: ed25519 (sign+certify), subkey: cv25519 (encrypt).
1257+
// Passphrase set at generation: 'fluux-fixture-passphrase'. Proves
1258+
// openpgp.js can parse and unlock secret keys produced by a real
1259+
// GnuPG export, not just keys we round-trip through ourselves.
1260+
const armoredPrivateKey = readFileSync(
1261+
resolve(FIXTURES_DIR, 'gnupg_modern_key.asc'),
1262+
'utf-8',
1263+
)
12591264
expect(armoredPrivateKey.startsWith('-----BEGIN PGP PRIVATE KEY BLOCK-----')).toBe(true)
12601265

12611266
const dest = new TestableWebOpenPGPPlugin()
1262-
const { ctx } = makeCtx('alice@example.com')
1267+
const { ctx } = makeCtx('fixture@fluux.test')
12631268
await dest.init(ctx)
12641269

12651270
const bundles = await dest.callBackupImportAll(
1266-
'alice@example.com',
1271+
'fixture@fluux.test',
12671272
armoredPrivateKey,
1268-
'gnupg-secret',
1273+
'fluux-fixture-passphrase',
12691274
)
12701275

12711276
expect(bundles).toHaveLength(1)
1272-
expect(bundles[0].fingerprint).toBe(privateKey.getFingerprint())
1277+
expect(bundles[0].fingerprint.toUpperCase()).toBe(
1278+
'E7DFB4979F5F2745B2B113B65DD61AB5E88A475B',
1279+
)
12731280
expect(bundles[0].publicArmored).toContain('BEGIN PGP PUBLIC KEY BLOCK')
12741281
})
12751282
})
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-----BEGIN PGP PRIVATE KEY BLOCK-----
2+
3+
lIYEag3JgBYJKwYBBAHaRw8BAQdAe6VMD/PIrrPtE+EvvheOciY2JWpvwM1LuqWY
4+
dE5UZir+BwMCp4iQnqT5183/Ly0Qt3OWnQ9d6+B+vrJIY+DVoi3QTbCdMjrc4cgF
5+
H5w5vPnlBlpOcmJurRYexc3YHPKwKdXyt8X7t/By+U5ZXlMR28b9j7QnRmx1dXgg
6+
VGVzdCBGaXh0dXJlIDxmaXh0dXJlQGZsdXV4LnRlc3Q+iK8EExYKAFcWIQTn37SX
7+
n18nRbKxE7Zd1hq16IpHWwUCag3JgBsUgAAAAAAEAA5tYW51MiwyLjUrMS4xMiww
8+
LDMCGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQXdYateiKR1u5XwEA
9+
2L0ERZIVXa0zVmRD5schGBzt47FfwR5DtAMKuNn70BoA+wWrjA6TlPIc4oh6iadY
10+
TESvrLi4fyKID9oTI9JZzmgAnIsEag3JkRIKKwYBBAGXVQEFAQEHQAmEWNJKjU2c
11+
cfpWMt/XV/oOBMukqGqkIesGXXvEXVpwAwEIB/4HAwKvBb9UQqo4Wv+VE86nvdN1
12+
Wf5XiANZBiC9hF2Yvj0s1BFVsOvW07g9cAuFTQRl5TCc/yGnUrjdS3uj5iE/3QrG
13+
0lRb+Q0tjzJsH70BFXI8iJQEGBYKADwWIQTn37SXn18nRbKxE7Zd1hq16IpHWwUC
14+
ag3JkRsUgAAAAAAEAA5tYW51MiwyLjUrMS4xMiwwLDMCGwwACgkQXdYateiKR1t0
15+
4AEAowhxHV+dghwrBHiWE3h/2CXYFevgD7Vrj3xLBJuAfkoBAOtdiLb1uY4MFbzB
16+
jT/X02jUR1VV5hZGO5QLiHTUEqAL
17+
=4ay1
18+
-----END PGP PRIVATE KEY BLOCK-----

0 commit comments

Comments
 (0)