Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Tests for RnpPrivateKeyUnlockTracker, and for the operations that must
* refuse to run on a key whose secret key material isn't available
* locally. Such a key is tracked as "not locked", so that automatic
* handle releasing works for code that sometimes tracks a secret key and
* sometimes only a public key, which means isUnlocked() must additionally
* require that the secret key material is available.
*/
"use strict";
const { RNP, RnpPrivateKeyUnlockTracker } = ChromeUtils.importESModule(
);
const { EnigmailKeyRing } = ChromeUtils.importESModule(
);
const { OpenPGPTestUtils } = ChromeUtils.importESModule(
);
const keyDir = "../../../../../test/browser/openpgp/data/keys";
// Ofelia's secret key was exported with "gpg --export-secret-subkeys",
// so the secret key material of the primary key is missing, while the
// secret key material of the subkeys is available.
const OFELIA_KEY_ID = "0x97DCDA5E56EBB822";
const BOB_KEY_ID = "0xFBFCC82A015E7330";
add_setup(async function () {
do_get_profile();
await OpenPGPTestUtils.initOpenPGP();
await OpenPGPTestUtils.importPublicKey(
null,
do_get_file(`${keyDir}/bob@openpgp.example-0xfbfcc82a015e7330-pub.asc`)
);
await OpenPGPTestUtils.importPrivateKey(
null,
do_get_file(`${keyDir}/ofelia-secret-subkeys.asc`)
);
});
add_task(async function testPublicKeyIsNeverUnlocked() {
const keyObj = EnigmailKeyRing.getKeyById(BOB_KEY_ID);
Assert.ok(keyObj, "should have found Bob's key");
Assert.ok(!keyObj.secretAvailable, "should have Bob's public key only");
const tracker = RnpPrivateKeyUnlockTracker.constructFromFingerprint(
keyObj.fpr
);
try {
Assert.ok(!tracker.isSecret(), "a public key isn't a secret key");
Assert.ok(!tracker.available(), "a public key has no secret key material");
Assert.ok(
!tracker.isUnlocked(),
"a public key must not report that it is unlocked"
);
await tracker.unlock();
Assert.ok(
!tracker.isUnlocked(),
"a public key must not report that it is unlocked after unlock()"
);
} finally {
tracker.release();
}
});
add_task(async function testOfflinePrimaryKeyCannotBeUnlocked() {
const keyObj = EnigmailKeyRing.getKeyById(OFELIA_KEY_ID);
Assert.ok(keyObj, "should have found Ofelia's key");
Assert.ok(keyObj.secretAvailable, "should be a secret key");
Assert.ok(
!keyObj.secretMaterial,
"the primary key's secret key material should be missing"
);
const primaryKey = RnpPrivateKeyUnlockTracker.constructFromFingerprint(
keyObj.fpr
);
try {
Assert.ok(primaryKey.isSecret(), "should be flagged as a secret key");
Assert.ok(
!primaryKey.available(),
"the primary key's secret key material isn't available"
);
Assert.ok(
!primaryKey.isUnlocked(),
"a key that cannot be unlocked must not report that it is unlocked"
);
await primaryKey.unlock();
Assert.ok(
!primaryKey.isUnlocked(),
"unlock() must not make it report that it is unlocked"
);
} finally {
primaryKey.release();
}
Assert.greater(keyObj.subKeys.length, 0, "should have subkeys");
for (const subKeyObj of keyObj.subKeys) {
const subKey = RnpPrivateKeyUnlockTracker.constructFromFingerprint(
subKeyObj.fpr
);
try {
Assert.ok(
subKey.available(),
`secret key material of subkey ${subKeyObj.keyId} should be available`
);
await subKey.unlock();
Assert.ok(
subKey.isUnlocked(),
`subkey ${subKeyObj.keyId} should be unlocked`
);
} finally {
subKey.release();
}
}
});
add_task(async function testOfflinePrimaryKeyRefusesExpirationChange() {
let keyObj = EnigmailKeyRing.getKeyById(OFELIA_KEY_ID);
const originalExpiry = keyObj.expiryTime;
const later = new Date();
later.setDate(later.getDate() + 100);
const changed = await RNP.changeKeyExpiration(keyObj, null, later);
Assert.ok(
!changed,
"changing the expiration date should fail without the primary secret key material"
);
EnigmailKeyRing.clearCache();
keyObj = EnigmailKeyRing.getKeyById(OFELIA_KEY_ID);
Assert.equal(
keyObj.expiryTime,
originalExpiry,
"the expiration date should be unchanged"
);
});
add_task(async function testOfflinePrimaryKeyRefusesRevocation() {
await Assert.rejects(
RNP.unlockAndGetNewRevocation(OFELIA_KEY_ID, null),
/Couldn't unlock key/,
"creating a revocation should fail without the primary secret key material"
);
});