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'
1214import { afterEach , beforeEach , describe , expect , it } from 'vitest'
1315import {
1416 InMemoryStorageBackend ,
@@ -24,6 +26,8 @@ import { WebOpenPGPPlugin } from './WebOpenPGPPlugin'
2426import { clearSessionPassphrase , setSessionPassphrase } from './webPassphraseStore'
2527import 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.
2933class 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 } )
0 commit comments