mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-30 12:32:17 -05:00
support for master key modifications, among other stuff
This commit is contained in:
parent
1fa77d57d2
commit
a943bebfdf
@ -376,6 +376,20 @@ public class PgpKeyOperationTest {
|
|||||||
ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
|
ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // change expiry
|
||||||
|
expiry += 60*60*24;
|
||||||
|
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry));
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
Assert.assertNotNull("modified key must have an expiry date",
|
||||||
|
modified.getPublicKey(keyId).getExpiryTime());
|
||||||
|
Assert.assertEquals("modified key must have expected expiry date",
|
||||||
|
expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
|
||||||
|
Assert.assertEquals("modified key must have same flags as before",
|
||||||
|
ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS;
|
int flags = KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_COMMS;
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
@ -422,16 +436,114 @@ public class PgpKeyOperationTest {
|
|||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
|
||||||
|
|
||||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel);
|
||||||
assertModifyFailure("setting subkey expiry to a past date should fail", secretRing, parcel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // modifying nonexistent keyring should fail
|
{ // modifying nonexistent subkey should fail
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null));
|
parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null));
|
||||||
|
|
||||||
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
|
assertModifyFailure("modifying non-existent subkey should fail", ring, parcel);
|
||||||
assertModifyFailure("modifying non-existent subkey should fail", secretRing, parcel);
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMasterModify() throws Exception {
|
||||||
|
|
||||||
|
long expiry = new Date().getTime()/1000 + 1024;
|
||||||
|
long keyId = ring.getMasterKeyId();
|
||||||
|
|
||||||
|
UncachedKeyRing modified = ring;
|
||||||
|
|
||||||
|
// to make this check less trivial, we add a user id, change the primary one and revoke one
|
||||||
|
parcel.mAddUserIds.add("aloe");
|
||||||
|
parcel.mChangePrimaryUserId = "aloe";
|
||||||
|
parcel.mRevokeUserIds.add("pink");
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
{
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry));
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
// this implies that only the two non-revoked signatures were changed!
|
||||||
|
Assert.assertEquals("two extra packets in original", 2, onlyA.size());
|
||||||
|
Assert.assertEquals("two extra packets in modified", 2, onlyB.size());
|
||||||
|
|
||||||
|
Assert.assertEquals("first original packet must be a signature",
|
||||||
|
PacketTags.SIGNATURE, onlyA.get(0).tag);
|
||||||
|
Assert.assertEquals("second original packet must be a signature",
|
||||||
|
PacketTags.SIGNATURE, onlyA.get(1).tag);
|
||||||
|
Assert.assertEquals("first new packet must be signature",
|
||||||
|
PacketTags.SIGNATURE, onlyB.get(0).tag);
|
||||||
|
Assert.assertEquals("first new packet must be signature",
|
||||||
|
PacketTags.SIGNATURE, onlyB.get(1).tag);
|
||||||
|
|
||||||
|
Assert.assertNotNull("modified key must have an expiry date",
|
||||||
|
modified.getPublicKey().getExpiryTime());
|
||||||
|
Assert.assertEquals("modified key must have expected expiry date",
|
||||||
|
expiry, modified.getPublicKey().getExpiryTime().getTime() / 1000);
|
||||||
|
Assert.assertEquals("modified key must have same flags as before",
|
||||||
|
ring.getPublicKey().getKeyUsage(), modified.getPublicKey().getKeyUsage());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // change expiry
|
||||||
|
expiry += 60*60*24;
|
||||||
|
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, expiry));
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
Assert.assertNotNull("modified key must have an expiry date",
|
||||||
|
modified.getPublicKey(keyId).getExpiryTime());
|
||||||
|
Assert.assertEquals("modified key must have expected expiry date",
|
||||||
|
expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
|
||||||
|
Assert.assertEquals("modified key must have same flags as before",
|
||||||
|
ring.getPublicKey(keyId).getKeyUsage(), modified.getPublicKey(keyId).getKeyUsage());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int flags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, flags, null));
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
Assert.assertEquals("modified key must have expected flags",
|
||||||
|
flags, modified.getPublicKey(keyId).getKeyUsage());
|
||||||
|
Assert.assertNotNull("key must retain its expiry",
|
||||||
|
modified.getPublicKey(keyId).getExpiryTime());
|
||||||
|
Assert.assertEquals("key expiry must be unchanged",
|
||||||
|
expiry, modified.getPublicKey(keyId).getExpiryTime().getTime()/1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // expiry of 0 should be "no expiry"
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, 0L));
|
||||||
|
modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB);
|
||||||
|
|
||||||
|
Assert.assertNull("key must not expire anymore", modified.getPublicKey(keyId).getExpiryTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // if we revoke everything, nothing is left to properly sign...
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mRevokeUserIds.add("twi");
|
||||||
|
parcel.mRevokeUserIds.add("pink");
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.CERTIFY_OTHER, null));
|
||||||
|
|
||||||
|
assertModifyFailure("master key modification with all user ids revoked should fail", ring, parcel);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // any flag not including CERTIFY_OTHER should fail
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, KeyFlags.SIGN_DATA, null));
|
||||||
|
|
||||||
|
assertModifyFailure("setting master key flags without certify should fail", ring, parcel);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // a past expiry should fail
|
||||||
|
parcel.reset();
|
||||||
|
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
|
||||||
|
|
||||||
|
assertModifyFailure("setting subkey expiry to a past date should fail", ring, parcel);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,11 @@ public class UncachedKeyringCanonicalizeTest {
|
|||||||
|
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, 0L));
|
||||||
|
|
||||||
parcel.mAddUserIds.add("twi");
|
parcel.mAddUserIds.add("twi");
|
||||||
parcel.mAddUserIds.add("pink");
|
parcel.mAddUserIds.add("pink");
|
||||||
@ -277,7 +277,7 @@ public class UncachedKeyringCanonicalizeTest {
|
|||||||
|
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
parcel.mAddUserIds.add("trix");
|
parcel.mAddUserIds.add("trix");
|
||||||
PgpKeyOperation op = new PgpKeyOperation(null);
|
PgpKeyOperation op = new PgpKeyOperation(null);
|
||||||
|
|
||||||
|
@ -64,9 +64,9 @@ public class UncachedKeyringMergeTest {
|
|||||||
{
|
{
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L));
|
||||||
|
|
||||||
parcel.mAddUserIds.add("twi");
|
parcel.mAddUserIds.add("twi");
|
||||||
parcel.mAddUserIds.add("pink");
|
parcel.mAddUserIds.add("pink");
|
||||||
@ -83,7 +83,7 @@ public class UncachedKeyringMergeTest {
|
|||||||
{
|
{
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
|
|
||||||
parcel.mAddUserIds.add("shy");
|
parcel.mAddUserIds.add("shy");
|
||||||
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
// passphrase is tested in PgpKeyOperationTest, just use empty here
|
||||||
@ -189,7 +189,7 @@ public class UncachedKeyringMergeTest {
|
|||||||
|
|
||||||
parcel.reset();
|
parcel.reset();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L));
|
||||||
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||||
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
|
||||||
|
|
||||||
|
@ -37,11 +37,11 @@ public class UncachedKeyringTest {
|
|||||||
|
|
||||||
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
SaveKeyringParcel parcel = new SaveKeyringParcel();
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.CERTIFY_OTHER, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.SIGN_DATA, 0L));
|
||||||
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
|
||||||
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, null));
|
PublicKeyAlgorithmTags.RSA_GENERAL, 1024, KeyFlags.ENCRYPT_COMMS, 0L));
|
||||||
|
|
||||||
parcel.mAddUserIds.add("twi");
|
parcel.mAddUserIds.add("twi");
|
||||||
parcel.mAddUserIds.add("pink");
|
parcel.mAddUserIds.add("pink");
|
||||||
|
@ -253,7 +253,7 @@ public class PgpKeyOperation {
|
|||||||
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
|
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
|
||||||
|
|
||||||
subProgressPush(50, 100);
|
subProgressPush(50, 100);
|
||||||
return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
|
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
|
||||||
|
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
|
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
|
||||||
@ -319,14 +319,17 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
// read masterKeyFlags, and use the same as before.
|
// read masterKeyFlags, and use the same as before.
|
||||||
// since this is the master key, this contains at least CERTIFY_OTHER
|
// since this is the master key, this contains at least CERTIFY_OTHER
|
||||||
int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
|
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
|
||||||
|
int masterKeyFlags = readKeyFlags(masterPublicKey) | KeyFlags.CERTIFY_OTHER;
|
||||||
|
long masterKeyExpiry = masterPublicKey.getValidSeconds() == 0L ? 0L :
|
||||||
|
masterPublicKey.getCreationTime().getTime() / 1000 + masterPublicKey.getValidSeconds();
|
||||||
|
|
||||||
return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
|
return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
|
private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
|
||||||
int masterKeyFlags,
|
int masterKeyFlags, long masterKeyExpiry,
|
||||||
SaveKeyringParcel saveParcel, String passphrase,
|
SaveKeyringParcel saveParcel, String passphrase,
|
||||||
OperationLog log) {
|
OperationLog log) {
|
||||||
|
|
||||||
@ -351,189 +354,196 @@ public class PgpKeyOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// work on master secret key
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
PGPPublicKey modifiedPublicKey = masterPublicKey;
|
{ // work on master secret key
|
||||||
|
|
||||||
// 2a. Add certificates for new user ids
|
PGPPublicKey modifiedPublicKey = masterPublicKey;
|
||||||
subProgressPush(15, 25);
|
|
||||||
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
|
|
||||||
|
|
||||||
progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size()));
|
// 2a. Add certificates for new user ids
|
||||||
String userId = saveParcel.mAddUserIds.get(i);
|
subProgressPush(15, 25);
|
||||||
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
|
for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
|
||||||
|
|
||||||
if (userId.equals("")) {
|
progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
|
String userId = saveParcel.mAddUserIds.get(i);
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
|
||||||
}
|
|
||||||
|
|
||||||
// this operation supersedes all previous binding and revocation certificates,
|
if (userId.equals("")) {
|
||||||
// so remove those to retain assertions from canonicalization for later operations
|
log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
|
|
||||||
if (it != null) {
|
|
||||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
|
|
||||||
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
|
||||||
// foreign certificate?! error error error
|
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
|
||||||
}
|
|
||||||
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|
|
||||||
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
|
|
||||||
|| cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
|
|
||||||
|| cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
|
|
||||||
|| cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
|
|
||||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
|
||||||
modifiedPublicKey, userId, cert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if it's supposed to be primary, we can do that here as well
|
|
||||||
boolean isPrimary = saveParcel.mChangePrimaryUserId != null
|
|
||||||
&& userId.equals(saveParcel.mChangePrimaryUserId);
|
|
||||||
// generate and add new certificate
|
|
||||||
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
|
|
||||||
masterPublicKey, userId, isPrimary, masterKeyFlags);
|
|
||||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
|
||||||
}
|
|
||||||
subProgressPop();
|
|
||||||
|
|
||||||
// 2b. Add revocations for revoked user ids
|
|
||||||
subProgressPush(25, 40);
|
|
||||||
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
|
|
||||||
|
|
||||||
progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mRevokeUserIds.size()));
|
|
||||||
String userId = saveParcel.mRevokeUserIds.get(i);
|
|
||||||
log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
|
|
||||||
// Make sure the user id exists (yes these are 10 LoC in Java!)
|
|
||||||
boolean exists = false;
|
|
||||||
for (String uid : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
|
|
||||||
if (userId.equals(uid)) {
|
|
||||||
exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!exists) {
|
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
|
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// a duplicate revocation will be removed during canonicalization, so no need to
|
|
||||||
// take care of that here.
|
|
||||||
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
|
|
||||||
masterPublicKey, userId);
|
|
||||||
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
|
||||||
}
|
|
||||||
subProgressPop();
|
|
||||||
|
|
||||||
// 3. If primary user id changed, generate new certificates for both old and new
|
|
||||||
if (saveParcel.mChangePrimaryUserId != null) {
|
|
||||||
progress(R.string.progress_modify_primaryuid, 40);
|
|
||||||
|
|
||||||
// keep track if we actually changed one
|
|
||||||
boolean ok = false;
|
|
||||||
log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
|
|
||||||
indent += 1;
|
|
||||||
|
|
||||||
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
|
|
||||||
// noinspection unchecked
|
|
||||||
for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
|
|
||||||
boolean isRevoked = false;
|
|
||||||
PGPSignature currentCert = null;
|
|
||||||
// noinspection unchecked
|
|
||||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(
|
|
||||||
modifiedPublicKey.getSignaturesForID(userId))) {
|
|
||||||
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
|
||||||
// foreign certificate?! error error error
|
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
|
||||||
}
|
|
||||||
// we know from canonicalization that if there is any revocation here, it
|
|
||||||
// is valid and not superseded by a newer certification.
|
|
||||||
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
|
|
||||||
isRevoked = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// we know from canonicalization that there is only one binding
|
|
||||||
// certification here, so we can just work with the first one.
|
|
||||||
if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
|
|
||||||
cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
|
|
||||||
cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
|
|
||||||
cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
|
|
||||||
currentCert = cert;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentCert == null) {
|
|
||||||
// no certificate found?! error error error
|
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we definitely should not update certifications of revoked keys, so just leave it.
|
// this operation supersedes all previous binding and revocation certificates,
|
||||||
if (isRevoked) {
|
// so remove those to retain assertions from canonicalization for later operations
|
||||||
// revoked user ids cannot be primary!
|
@SuppressWarnings("unchecked")
|
||||||
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
|
if (it != null) {
|
||||||
|
for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
|
||||||
|
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
||||||
|
// foreign certificate?! error error error
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|
||||||
|
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
|
||||||
|
|| cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
|
||||||
|
|| cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
|
||||||
|
|| cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
|
||||||
|
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||||
|
modifiedPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's supposed to be primary, we can do that here as well
|
||||||
|
boolean isPrimary = saveParcel.mChangePrimaryUserId != null
|
||||||
|
&& userId.equals(saveParcel.mChangePrimaryUserId);
|
||||||
|
// generate and add new certificate
|
||||||
|
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
|
||||||
|
masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry);
|
||||||
|
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
subProgressPop();
|
||||||
|
|
||||||
|
// 2b. Add revocations for revoked user ids
|
||||||
|
subProgressPush(25, 40);
|
||||||
|
for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
|
||||||
|
|
||||||
|
progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
|
||||||
|
String userId = saveParcel.mRevokeUserIds.get(i);
|
||||||
|
log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
|
||||||
|
|
||||||
|
// Make sure the user id exists (yes these are 10 LoC in Java!)
|
||||||
|
boolean exists = false;
|
||||||
|
//noinspection unchecked
|
||||||
|
for (String uid : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
|
||||||
|
if (userId.equals(uid)) {
|
||||||
|
exists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exists) {
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a duplicate revocation will be removed during canonicalization, so no need to
|
||||||
|
// take care of that here.
|
||||||
|
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
|
||||||
|
masterPublicKey, userId);
|
||||||
|
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
subProgressPop();
|
||||||
|
|
||||||
|
// 3. If primary user id changed, generate new certificates for both old and new
|
||||||
|
if (saveParcel.mChangePrimaryUserId != null) {
|
||||||
|
progress(R.string.progress_modify_primaryuid, 40);
|
||||||
|
|
||||||
|
// keep track if we actually changed one
|
||||||
|
boolean ok = false;
|
||||||
|
log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
|
||||||
|
indent += 1;
|
||||||
|
|
||||||
|
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
|
||||||
|
// noinspection unchecked
|
||||||
|
for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
|
||||||
|
boolean isRevoked = false;
|
||||||
|
PGPSignature currentCert = null;
|
||||||
|
// noinspection unchecked
|
||||||
|
for (PGPSignature cert : new IterableIterator<PGPSignature>(
|
||||||
|
modifiedPublicKey.getSignaturesForID(userId))) {
|
||||||
|
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
||||||
|
// foreign certificate?! error error error
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
// we know from canonicalization that if there is any revocation here, it
|
||||||
|
// is valid and not superseded by a newer certification.
|
||||||
|
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
|
||||||
|
isRevoked = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// we know from canonicalization that there is only one binding
|
||||||
|
// certification here, so we can just work with the first one.
|
||||||
|
if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
|
||||||
|
currentCert = cert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentCert == null) {
|
||||||
|
// no certificate found?! error error error
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is~ the/a primary user id
|
// we definitely should not update certifications of revoked keys, so just leave it.
|
||||||
if (currentCert.getHashedSubPackets() != null
|
if (isRevoked) {
|
||||||
&& currentCert.getHashedSubPackets().isPrimaryUserID()) {
|
// revoked user ids cannot be primary!
|
||||||
// if it's the one we want, just leave it as is
|
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
||||||
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
|
||||||
ok = true;
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// otherwise, generate new non-primary certification
|
|
||||||
log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
|
// if this is~ the/a primary user id
|
||||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
if (currentCert.getHashedSubPackets() != null
|
||||||
modifiedPublicKey, userId, currentCert);
|
&& currentCert.getHashedSubPackets().isPrimaryUserID()) {
|
||||||
PGPSignature newCert = generateUserIdSignature(
|
// if it's the one we want, just leave it as is
|
||||||
masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
|
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
||||||
modifiedPublicKey = PGPPublicKey.addCertification(
|
ok = true;
|
||||||
modifiedPublicKey, userId, newCert);
|
continue;
|
||||||
continue;
|
}
|
||||||
|
// otherwise, generate new non-primary certification
|
||||||
|
log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
|
||||||
|
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||||
|
modifiedPublicKey, userId, currentCert);
|
||||||
|
PGPSignature newCert = generateUserIdSignature(
|
||||||
|
masterPrivateKey, masterPublicKey, userId, false,
|
||||||
|
masterKeyFlags, masterKeyExpiry);
|
||||||
|
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||||
|
modifiedPublicKey, userId, newCert);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are here, this is not currently a primary user id
|
||||||
|
|
||||||
|
// if it should be
|
||||||
|
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
||||||
|
// add shiny new primary user id certificate
|
||||||
|
log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
|
||||||
|
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||||
|
modifiedPublicKey, userId, currentCert);
|
||||||
|
PGPSignature newCert = generateUserIdSignature(
|
||||||
|
masterPrivateKey, masterPublicKey, userId, true,
|
||||||
|
masterKeyFlags, masterKeyExpiry);
|
||||||
|
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||||
|
modifiedPublicKey, userId, newCert);
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// user id is not primary and is not supposed to be - nothing to do here.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we are here, this is not currently a primary user id
|
indent -= 1;
|
||||||
|
|
||||||
// if it should be
|
if (!ok) {
|
||||||
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
|
||||||
// add shiny new primary user id certificate
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
|
|
||||||
modifiedPublicKey = PGPPublicKey.removeCertification(
|
|
||||||
modifiedPublicKey, userId, currentCert);
|
|
||||||
PGPSignature newCert = generateUserIdSignature(
|
|
||||||
masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);
|
|
||||||
modifiedPublicKey = PGPPublicKey.addCertification(
|
|
||||||
modifiedPublicKey, userId, newCert);
|
|
||||||
ok = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// user id is not primary and is not supposed to be - nothing to do here.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
indent -= 1;
|
// Update the secret key ring
|
||||||
|
if (modifiedPublicKey != masterPublicKey) {
|
||||||
if (!ok) {
|
masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
|
masterPublicKey = modifiedPublicKey;
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update the secret key ring
|
|
||||||
if (modifiedPublicKey != masterPublicKey) {
|
|
||||||
masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
|
|
||||||
masterPublicKey = modifiedPublicKey;
|
|
||||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4a. For each subkey change, generate new subkey binding certificate
|
// 4a. For each subkey change, generate new subkey binding certificate
|
||||||
@ -545,28 +555,47 @@ public class PgpKeyOperation {
|
|||||||
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
|
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
|
||||||
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
||||||
|
|
||||||
// TODO allow changes in master key? this implies generating new user id certs...
|
|
||||||
if (change.mKeyId == masterPublicKey.getKeyID()) {
|
|
||||||
Log.e(Constants.TAG, "changing the master key not supported");
|
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
|
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
|
||||||
if (sKey == null) {
|
if (sKey == null) {
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
|
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
|
||||||
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
}
|
}
|
||||||
PGPPublicKey pKey = sKey.getPublicKey();
|
|
||||||
|
|
||||||
// expiry must not be in the past
|
// expiry must not be in the past
|
||||||
if (change.mExpiry != null && change.mExpiry != 0 &&
|
if (change.mExpiry != null && change.mExpiry != 0 &&
|
||||||
new Date(change.mExpiry*1000).before(new Date())) {
|
new Date(change.mExpiry*1000).before(new Date())) {
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY,
|
||||||
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this is the master key, update uid certificates instead
|
||||||
|
if (change.mKeyId == masterPublicKey.getKeyID()) {
|
||||||
|
int flags = change.mFlags == null ? masterKeyFlags : change.mFlags;
|
||||||
|
long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry;
|
||||||
|
|
||||||
|
if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPPublicKey pKey =
|
||||||
|
updateMasterCertificates(masterPrivateKey, masterPublicKey,
|
||||||
|
flags, expiry, indent, log);
|
||||||
|
if (pKey == null) {
|
||||||
|
// error log entry has already been added by updateMasterCertificates itself
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, pKey);
|
||||||
|
masterPublicKey = pKey;
|
||||||
|
sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, continue working on the public key
|
||||||
|
PGPPublicKey pKey = sKey.getPublicKey();
|
||||||
|
|
||||||
// keep old flags, or replace with new ones
|
// keep old flags, or replace with new ones
|
||||||
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
|
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
|
||||||
long expiry;
|
long expiry;
|
||||||
@ -583,7 +612,7 @@ public class PgpKeyOperation {
|
|||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
|
||||||
// special case: if there is a revocation, don't use expiry from before
|
// special case: if there is a revocation, don't use expiry from before
|
||||||
if (change.mExpiry == null
|
if ( (change.mExpiry == null || change.mExpiry == 0L)
|
||||||
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
|
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
|
||||||
expiry = 0;
|
expiry = 0;
|
||||||
}
|
}
|
||||||
@ -631,8 +660,13 @@ public class PgpKeyOperation {
|
|||||||
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
|
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
|
||||||
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
|
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
|
||||||
|
|
||||||
if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
|
if (add.mExpiry == null) {
|
||||||
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
|
||||||
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) {
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
|
||||||
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +686,7 @@ public class PgpKeyOperation {
|
|||||||
PGPPublicKey pKey = keyPair.getPublicKey();
|
PGPPublicKey pKey = keyPair.getPublicKey();
|
||||||
PGPSignature cert = generateSubkeyBindingSignature(
|
PGPSignature cert = generateSubkeyBindingSignature(
|
||||||
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
|
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
|
||||||
add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
|
add.mFlags, add.mExpiry);
|
||||||
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
||||||
|
|
||||||
PGPSecretKey sKey; {
|
PGPSecretKey sKey; {
|
||||||
@ -713,21 +747,104 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Update all (non-revoked) uid signatures with new flags and expiry time. */
|
||||||
|
private static PGPPublicKey updateMasterCertificates(
|
||||||
|
PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey,
|
||||||
|
int flags, long expiry, int indent, OperationLog log)
|
||||||
|
throws PGPException, IOException, SignatureException {
|
||||||
|
|
||||||
|
// keep track if we actually changed one
|
||||||
|
boolean ok = false;
|
||||||
|
log.add(LogLevel.DEBUG, LogType.MSG_MF_MASTER, indent);
|
||||||
|
indent += 1;
|
||||||
|
|
||||||
|
PGPPublicKey modifiedPublicKey = masterPublicKey;
|
||||||
|
|
||||||
|
// we work on the modifiedPublicKey here, to respect new or newly revoked uids
|
||||||
|
// noinspection unchecked
|
||||||
|
for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
|
||||||
|
boolean isRevoked = false;
|
||||||
|
PGPSignature currentCert = null;
|
||||||
|
// noinspection unchecked
|
||||||
|
for (PGPSignature cert : new IterableIterator<PGPSignature>(
|
||||||
|
modifiedPublicKey.getSignaturesForID(userId))) {
|
||||||
|
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
|
||||||
|
// foreign certificate?! error error error
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// we know from canonicalization that if there is any revocation here, it
|
||||||
|
// is valid and not superseded by a newer certification.
|
||||||
|
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
|
||||||
|
isRevoked = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// we know from canonicalization that there is only one binding
|
||||||
|
// certification here, so we can just work with the first one.
|
||||||
|
if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
|
||||||
|
cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
|
||||||
|
currentCert = cert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentCert == null) {
|
||||||
|
// no certificate found?! error error error
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we definitely should not update certifications of revoked keys, so just leave it.
|
||||||
|
if (isRevoked) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add shiny new user id certificate
|
||||||
|
modifiedPublicKey = PGPPublicKey.removeCertification(
|
||||||
|
modifiedPublicKey, userId, currentCert);
|
||||||
|
PGPSignature newCert = generateUserIdSignature(
|
||||||
|
masterPrivateKey, masterPublicKey, userId, true, flags, expiry);
|
||||||
|
modifiedPublicKey = PGPPublicKey.addCertification(
|
||||||
|
modifiedPublicKey, userId, newCert);
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
// might happen, theoretically, if there is a key with no uid..
|
||||||
|
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modifiedPublicKey;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static PGPSignature generateUserIdSignature(
|
private static PGPSignature generateUserIdSignature(
|
||||||
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
|
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
|
||||||
|
int flags, long expiry)
|
||||||
throws IOException, PGPException, SignatureException {
|
throws IOException, PGPException, SignatureException {
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
pKey.getAlgorithm(), PGPUtil.SHA1)
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
|
||||||
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
|
PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
|
{
|
||||||
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
|
hashedPacketsGen.setSignatureCreationTime(false, new Date());
|
||||||
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
|
hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
|
||||||
subHashedPacketsGen.setPrimaryUserID(false, primary);
|
hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
|
||||||
subHashedPacketsGen.setKeyFlags(false, flags);
|
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
|
||||||
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
hashedPacketsGen.setPrimaryUserID(false, primary);
|
||||||
|
hashedPacketsGen.setKeyFlags(false, flags);
|
||||||
|
if (expiry > 0) {
|
||||||
|
hashedPacketsGen.setKeyExpirationTime(
|
||||||
|
false, expiry - pKey.getCreationTime().getTime() / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||||
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
||||||
return sGen.generateCertification(userId, pKey);
|
return sGen.generateCertification(userId, pKey);
|
||||||
}
|
}
|
||||||
@ -784,14 +901,15 @@ public class PgpKeyOperation {
|
|||||||
throws IOException, PGPException, SignatureException {
|
throws IOException, PGPException, SignatureException {
|
||||||
|
|
||||||
// date for signing
|
// date for signing
|
||||||
Date todayDate = new Date();
|
Date creationTime = new Date();
|
||||||
|
|
||||||
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
|
||||||
// If this key can sign, we need a primary key binding signature
|
// If this key can sign, we need a primary key binding signature
|
||||||
if ((flags & KeyFlags.SIGN_DATA) > 0) {
|
if ((flags & KeyFlags.SIGN_DATA) > 0) {
|
||||||
// cross-certify signing keys
|
// cross-certify signing keys
|
||||||
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
|
subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
pKey.getAlgorithm(), PGPUtil.SHA1)
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
@ -805,13 +923,12 @@ public class PgpKeyOperation {
|
|||||||
PGPSignatureSubpacketGenerator hashedPacketsGen;
|
PGPSignatureSubpacketGenerator hashedPacketsGen;
|
||||||
{
|
{
|
||||||
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
hashedPacketsGen.setSignatureCreationTime(false, todayDate);
|
hashedPacketsGen.setSignatureCreationTime(false, creationTime);
|
||||||
hashedPacketsGen.setKeyFlags(false, flags);
|
hashedPacketsGen.setKeyFlags(false, flags);
|
||||||
}
|
if (expiry > 0) {
|
||||||
|
hashedPacketsGen.setKeyExpirationTime(false,
|
||||||
if (expiry > 0) {
|
expiry - pKey.getCreationTime().getTime() / 1000);
|
||||||
long creationTime = pKey.getCreationTime().getTime() / 1000;
|
}
|
||||||
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
@ -359,12 +359,15 @@ public class OperationResultParcel implements Parcelable {
|
|||||||
MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),
|
MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),
|
||||||
MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),
|
MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),
|
||||||
MSG_MF_ERROR_INTEGRITY (R.string.msg_mf_error_integrity),
|
MSG_MF_ERROR_INTEGRITY (R.string.msg_mf_error_integrity),
|
||||||
|
MSG_MF_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify),
|
||||||
MSG_MF_ERROR_NOEXIST_PRIMARY (R.string.msg_mf_error_noexist_primary),
|
MSG_MF_ERROR_NOEXIST_PRIMARY (R.string.msg_mf_error_noexist_primary),
|
||||||
MSG_MF_ERROR_NOEXIST_REVOKE (R.string.msg_mf_error_noexist_revoke),
|
MSG_MF_ERROR_NOEXIST_REVOKE (R.string.msg_mf_error_noexist_revoke),
|
||||||
MSG_MF_ERROR_NULL_EXPIRY (R.string.msg_mf_error_null_expiry),
|
MSG_MF_ERROR_NULL_EXPIRY (R.string.msg_mf_error_null_expiry),
|
||||||
|
MSG_MF_ERROR_PAST_EXPIRY(R.string.msg_mf_error_past_expiry),
|
||||||
MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),
|
MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),
|
||||||
MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),
|
MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),
|
||||||
MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),
|
MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),
|
||||||
|
MSG_MF_MASTER (R.string.msg_mf_master),
|
||||||
MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase),
|
MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase),
|
||||||
MSG_MF_PRIMARY_REPLACE_OLD (R.string.msg_mf_primary_replace_old),
|
MSG_MF_PRIMARY_REPLACE_OLD (R.string.msg_mf_primary_replace_old),
|
||||||
MSG_MF_PRIMARY_NEW (R.string.msg_mf_primary_new),
|
MSG_MF_PRIMARY_NEW (R.string.msg_mf_primary_new),
|
||||||
@ -372,7 +375,6 @@ public class OperationResultParcel implements Parcelable {
|
|||||||
MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing),
|
MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing),
|
||||||
MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id),
|
MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id),
|
||||||
MSG_MF_SUBKEY_NEW (R.string.msg_mf_subkey_new),
|
MSG_MF_SUBKEY_NEW (R.string.msg_mf_subkey_new),
|
||||||
MSG_MF_SUBKEY_PAST_EXPIRY (R.string.msg_mf_subkey_past_expiry),
|
|
||||||
MSG_MF_SUBKEY_REVOKE (R.string.msg_mf_subkey_revoke),
|
MSG_MF_SUBKEY_REVOKE (R.string.msg_mf_subkey_revoke),
|
||||||
MSG_MF_SUCCESS (R.string.msg_mf_success),
|
MSG_MF_SUCCESS (R.string.msg_mf_success),
|
||||||
MSG_MF_UID_ADD (R.string.msg_mf_uid_add),
|
MSG_MF_UID_ADD (R.string.msg_mf_uid_add),
|
||||||
@ -436,6 +438,15 @@ public class OperationResultParcel implements Parcelable {
|
|||||||
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
|
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean containsType(LogType type) {
|
||||||
|
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
|
||||||
|
if (entry.mType == type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean containsWarnings() {
|
public boolean containsWarnings() {
|
||||||
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
|
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
|
||||||
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {
|
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {
|
||||||
|
@ -588,7 +588,7 @@
|
|||||||
<string name="msg_mf_subkey_missing">¡Intentó operar sobre una subclave ausente %s!</string>
|
<string name="msg_mf_subkey_missing">¡Intentó operar sobre una subclave ausente %s!</string>
|
||||||
<string name="msg_mf_subkey_new">Generando nueva subclave %2$s de %1$s bits</string>
|
<string name="msg_mf_subkey_new">Generando nueva subclave %2$s de %1$s bits</string>
|
||||||
<string name="msg_mf_subkey_new_id">Nueva identidad de subclave: %s</string>
|
<string name="msg_mf_subkey_new_id">Nueva identidad de subclave: %s</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">¡La fecha de expiración no puede ser del pasado!</string>
|
<string name="msg_mf_error_past_expiry">¡La fecha de expiración no puede ser del pasado!</string>
|
||||||
<string name="msg_mf_subkey_revoke">Revocando subclave %s</string>
|
<string name="msg_mf_subkey_revoke">Revocando subclave %s</string>
|
||||||
<string name="msg_mf_success">Juego de claves modificado con éxito</string>
|
<string name="msg_mf_success">Juego de claves modificado con éxito</string>
|
||||||
<string name="msg_mf_uid_add">Añadiendo identidad de usuario %s</string>
|
<string name="msg_mf_uid_add">Añadiendo identidad de usuario %s</string>
|
||||||
|
@ -588,7 +588,7 @@
|
|||||||
<string name="msg_mf_subkey_missing">Une action a été tentée sur la sous-clef manquante %s !</string>
|
<string name="msg_mf_subkey_missing">Une action a été tentée sur la sous-clef manquante %s !</string>
|
||||||
<string name="msg_mf_subkey_new">Génération d\'une nouvelle sous-clef %2$s de %1$s bit</string>
|
<string name="msg_mf_subkey_new">Génération d\'une nouvelle sous-clef %2$s de %1$s bit</string>
|
||||||
<string name="msg_mf_subkey_new_id">ID de la nouvelle sous-clef : %s</string>
|
<string name="msg_mf_subkey_new_id">ID de la nouvelle sous-clef : %s</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">La date d\'expiration ne peut pas être dans le passé !</string>
|
<string name="msg_mf_error_past_expiry">La date d\'expiration ne peut pas être dans le passé !</string>
|
||||||
<string name="msg_mf_subkey_revoke">Révocation de la sous-clef %s</string>
|
<string name="msg_mf_subkey_revoke">Révocation de la sous-clef %s</string>
|
||||||
<string name="msg_mf_success">Trousseau modifié avec succès</string>
|
<string name="msg_mf_success">Trousseau modifié avec succès</string>
|
||||||
<string name="msg_mf_uid_add">Ajout de l\'ID d\'utilisateur %s</string>
|
<string name="msg_mf_uid_add">Ajout de l\'ID d\'utilisateur %s</string>
|
||||||
|
@ -528,7 +528,7 @@
|
|||||||
<string name="msg_mf_subkey_missing">Tentativo di operare su sottochiave mancante %s!</string>
|
<string name="msg_mf_subkey_missing">Tentativo di operare su sottochiave mancante %s!</string>
|
||||||
<string name="msg_mf_subkey_new">Generazione nuovi %1$s bit %2$s sottochiave</string>
|
<string name="msg_mf_subkey_new">Generazione nuovi %1$s bit %2$s sottochiave</string>
|
||||||
<string name="msg_mf_subkey_new_id">Nuovo ID sottochiave: %s</string>
|
<string name="msg_mf_subkey_new_id">Nuovo ID sottochiave: %s</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">La data di scadenza non può essere passata!</string>
|
<string name="msg_mf_error_past_expiry">La data di scadenza non può essere passata!</string>
|
||||||
<string name="msg_mf_subkey_revoke">Revoca sottochiave %s</string>
|
<string name="msg_mf_subkey_revoke">Revoca sottochiave %s</string>
|
||||||
<string name="msg_mf_success">Portachiavi modificato con successo</string>
|
<string name="msg_mf_success">Portachiavi modificato con successo</string>
|
||||||
<string name="msg_mf_uid_add">Aggiunta id utente %s</string>
|
<string name="msg_mf_uid_add">Aggiunta id utente %s</string>
|
||||||
|
@ -574,7 +574,7 @@
|
|||||||
<string name="msg_mf_subkey_missing">遺失した副鍵 %s の操作をしようとした!</string>
|
<string name="msg_mf_subkey_missing">遺失した副鍵 %s の操作をしようとした!</string>
|
||||||
<string name="msg_mf_subkey_new">新しい %1$s ビットの %2$s 副鍵の生成中</string>
|
<string name="msg_mf_subkey_new">新しい %1$s ビットの %2$s 副鍵の生成中</string>
|
||||||
<string name="msg_mf_subkey_new_id">新しい副鍵 ID: %s</string>
|
<string name="msg_mf_subkey_new_id">新しい副鍵 ID: %s</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">期限切れ日を過去にはできません!</string>
|
<string name="msg_mf_error_past_expiry">期限切れ日を過去にはできません!</string>
|
||||||
<string name="msg_mf_subkey_revoke">副鍵 %s を破棄中</string>
|
<string name="msg_mf_subkey_revoke">副鍵 %s を破棄中</string>
|
||||||
<string name="msg_mf_success">鍵輪の変更に成功</string>
|
<string name="msg_mf_success">鍵輪の変更に成功</string>
|
||||||
<string name="msg_mf_uid_add">ユーザID %s を追加中</string>
|
<string name="msg_mf_uid_add">ユーザID %s を追加中</string>
|
||||||
|
@ -384,7 +384,7 @@
|
|||||||
<string name="msg_mf_error_pgp">Внутренняя ошибка PGP!</string>
|
<string name="msg_mf_error_pgp">Внутренняя ошибка PGP!</string>
|
||||||
<string name="msg_mf_error_sig">Ошибка подписи!</string>
|
<string name="msg_mf_error_sig">Ошибка подписи!</string>
|
||||||
<string name="msg_mf_passphrase">Изменение пароля</string>
|
<string name="msg_mf_passphrase">Изменение пароля</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">Срок годности не может быть в прошлом!</string>
|
<string name="msg_mf_error_past_expiry">Срок годности не может быть в прошлом!</string>
|
||||||
<string name="msg_mf_success">Связка успешно изменена</string>
|
<string name="msg_mf_success">Связка успешно изменена</string>
|
||||||
<string name="msg_mf_uid_add">Добавление id %s</string>
|
<string name="msg_mf_uid_add">Добавление id %s</string>
|
||||||
<string name="msg_mf_uid_primary">Изменение основного uid на %s</string>
|
<string name="msg_mf_uid_primary">Изменение основного uid на %s</string>
|
||||||
|
@ -639,12 +639,14 @@
|
|||||||
<string name="msg_mf_error_fingerprint">Actual key fingerprint does not match the expected one!</string>
|
<string name="msg_mf_error_fingerprint">Actual key fingerprint does not match the expected one!</string>
|
||||||
<string name="msg_mf_error_keyid">No key ID. This is an internal error, please file a bug report!</string>
|
<string name="msg_mf_error_keyid">No key ID. This is an internal error, please file a bug report!</string>
|
||||||
<string name="msg_mf_error_integrity">Internal error, integrity check failed!</string>
|
<string name="msg_mf_error_integrity">Internal error, integrity check failed!</string>
|
||||||
|
<string name="msg_mf_error_noexist_master">No master certificate found to modify!</string>
|
||||||
<string name="msg_mf_error_noexist_primary">Bad primary user id specified!</string>
|
<string name="msg_mf_error_noexist_primary">Bad primary user id specified!</string>
|
||||||
<string name="msg_mf_error_noexist_revoke">Bad user id for revocation specified!</string>
|
<string name="msg_mf_error_noexist_revoke">Bad user id for revocation specified!</string>
|
||||||
<string name="msg_mf_error_revoked_primary">Revoked user ids cannot be primary!</string>
|
<string name="msg_mf_error_revoked_primary">Revoked user ids cannot be primary!</string>
|
||||||
<string name="msg_mf_error_null_expiry">Expiry time cannot be "same as before" on subkey creation. This is a programming error, please file a bug report!</string>
|
<string name="msg_mf_error_null_expiry">Expiry time cannot be "same as before" on subkey creation. This is a programming error, please file a bug report!</string>
|
||||||
<string name="msg_mf_error_pgp">PGP internal exception!</string>
|
<string name="msg_mf_error_pgp">PGP internal exception!</string>
|
||||||
<string name="msg_mf_error_sig">Signature exception!</string>
|
<string name="msg_mf_error_sig">Signature exception!</string>
|
||||||
|
<string name="msg_mf_master">Modifying master certifications</string>
|
||||||
<string name="msg_mf_passphrase">Changing passphrase</string>
|
<string name="msg_mf_passphrase">Changing passphrase</string>
|
||||||
<string name="msg_mf_primary_replace_old">Replacing certificate of previous primary user id</string>
|
<string name="msg_mf_primary_replace_old">Replacing certificate of previous primary user id</string>
|
||||||
<string name="msg_mf_primary_new">Generating new certificate for new primary user id</string>
|
<string name="msg_mf_primary_new">Generating new certificate for new primary user id</string>
|
||||||
@ -652,7 +654,7 @@
|
|||||||
<string name="msg_mf_subkey_missing">Tried to operate on missing subkey %s!</string>
|
<string name="msg_mf_subkey_missing">Tried to operate on missing subkey %s!</string>
|
||||||
<string name="msg_mf_subkey_new">Generating new %1$s bit %2$s subkey</string>
|
<string name="msg_mf_subkey_new">Generating new %1$s bit %2$s subkey</string>
|
||||||
<string name="msg_mf_subkey_new_id">New subkey ID: %s</string>
|
<string name="msg_mf_subkey_new_id">New subkey ID: %s</string>
|
||||||
<string name="msg_mf_subkey_past_expiry">Expiry date cannot be in the past!</string>
|
<string name="msg_mf_error_past_expiry">Expiry date cannot be in the past!</string>
|
||||||
<string name="msg_mf_subkey_revoke">Revoking subkey %s</string>
|
<string name="msg_mf_subkey_revoke">Revoking subkey %s</string>
|
||||||
<string name="msg_mf_success">Keyring successfully modified</string>
|
<string name="msg_mf_success">Keyring successfully modified</string>
|
||||||
<string name="msg_mf_uid_add">Adding user id %s</string>
|
<string name="msg_mf_uid_add">Adding user id %s</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user