mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-02-25 16:01:52 -05:00
modifyKey: make SubkeyChange operations work
This commit is contained in:
parent
e7efd2c539
commit
7b195ac2e3
@ -65,10 +65,8 @@ import java.security.NoSuchProviderException;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is the single place where ALL operations that actually modify a PGP public or secret
|
* This class is the single place where ALL operations that actually modify a PGP public or secret
|
||||||
@ -264,7 +262,7 @@ 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 = readMasterKeyFlags(masterSecretKey.getPublicKey());
|
int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
|
||||||
|
|
||||||
return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log, indent);
|
return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log, indent);
|
||||||
|
|
||||||
@ -450,6 +448,13 @@ public class PgpKeyOperation {
|
|||||||
for (SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
|
for (SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
|
||||||
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 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,
|
||||||
@ -458,16 +463,39 @@ public class PgpKeyOperation {
|
|||||||
}
|
}
|
||||||
PGPPublicKey pKey = sKey.getPublicKey();
|
PGPPublicKey pKey = sKey.getPublicKey();
|
||||||
|
|
||||||
if (change.mExpiry != null && new Date(change.mExpiry).before(new Date())) {
|
// expiry must not be in the past
|
||||||
|
if (change.mExpiry != null && 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_SUBKEY_PAST_EXPIRY,
|
||||||
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate and add new signature. we can be sloppy here and just leave the old one,
|
// keep old flags, or replace with new ones
|
||||||
// it will be removed during canonicalization
|
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
|
||||||
|
long expiry;
|
||||||
|
if (change.mExpiry == null) {
|
||||||
|
long valid = pKey.getValidSeconds();
|
||||||
|
expiry = valid == 0
|
||||||
|
? 0
|
||||||
|
: pKey.getCreationTime().getTime() / 1000 + pKey.getValidSeconds();
|
||||||
|
} else {
|
||||||
|
expiry = change.mExpiry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop all old signatures, they will be superseded by the new one
|
||||||
|
//noinspection unchecked
|
||||||
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
|
||||||
|
// special case: if there is a revocation, don't use expiry from before
|
||||||
|
if (change.mExpiry == null
|
||||||
|
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
|
||||||
|
expiry = 0;
|
||||||
|
}
|
||||||
|
pKey = PGPPublicKey.removeCertification(pKey, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate and add new signature
|
||||||
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
|
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
|
||||||
sKey, pKey, change.mFlags, change.mExpiry, passphrase);
|
sKey, pKey, flags, expiry, passphrase);
|
||||||
pKey = PGPPublicKey.addCertification(pKey, sig);
|
pKey = PGPPublicKey.addCertification(pKey, sig);
|
||||||
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
|
||||||
}
|
}
|
||||||
@ -509,7 +537,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);
|
add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
|
||||||
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
|
||||||
|
|
||||||
PGPSecretKey sKey; {
|
PGPSecretKey sKey; {
|
||||||
@ -624,7 +652,7 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
private static PGPSignature generateSubkeyBindingSignature(
|
private static PGPSignature generateSubkeyBindingSignature(
|
||||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||||
PGPSecretKey sKey, PGPPublicKey pKey, int flags, Long expiry, String passphrase)
|
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase)
|
||||||
throws IOException, PGPException, SignatureException {
|
throws IOException, PGPException, SignatureException {
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
@ -636,7 +664,7 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
private static PGPSignature generateSubkeyBindingSignature(
|
private static PGPSignature generateSubkeyBindingSignature(
|
||||||
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||||
PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, Long expiry)
|
PGPPrivateKey subPrivateKey, PGPPublicKey pKey, int flags, long expiry)
|
||||||
throws IOException, PGPException, SignatureException {
|
throws IOException, PGPException, SignatureException {
|
||||||
|
|
||||||
// date for signing
|
// date for signing
|
||||||
@ -665,17 +693,9 @@ public class PgpKeyOperation {
|
|||||||
hashedPacketsGen.setKeyFlags(false, flags);
|
hashedPacketsGen.setKeyFlags(false, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expiry != null) {
|
if (expiry > 0) {
|
||||||
Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
long creationTime = pKey.getCreationTime().getTime() / 1000;
|
||||||
creationDate.setTime(pKey.getCreationTime());
|
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
|
||||||
|
|
||||||
// (Just making sure there's no programming error here, this MUST have been checked above!)
|
|
||||||
if (new Date(expiry).before(todayDate)) {
|
|
||||||
throw new RuntimeException("Bad subkey creation date, this is a bug!");
|
|
||||||
}
|
|
||||||
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
|
|
||||||
} else {
|
|
||||||
hashedPacketsGen.setKeyExpirationTime(false, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
@ -690,10 +710,16 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int readMasterKeyFlags(PGPPublicKey masterKey) {
|
/** Returns all flags valid for this key.
|
||||||
int flags = KeyFlags.CERTIFY_OTHER;
|
*
|
||||||
|
* This method does not do any validity checks on the signature, so it should not be used on
|
||||||
|
* a non-canonicalized key!
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static int readKeyFlags(PGPPublicKey key) {
|
||||||
|
int flags = 0;
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
for(PGPSignature sig : new IterableIterator<PGPSignature>(masterKey.getSignatures())) {
|
for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
|
||||||
if (!sig.hasSubpackets()) {
|
if (!sig.hasSubpackets()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,10 @@ public class UncachedKeyRing {
|
|||||||
return new UncachedPublicKey(mRing.getPublicKey());
|
return new UncachedPublicKey(mRing.getPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UncachedPublicKey getPublicKey(long keyId) {
|
||||||
|
return new UncachedPublicKey(mRing.getPublicKey(keyId));
|
||||||
|
}
|
||||||
|
|
||||||
public Iterator<UncachedPublicKey> getPublicKeys() {
|
public Iterator<UncachedPublicKey> getPublicKeys() {
|
||||||
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
|
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
|
||||||
return new Iterator<UncachedPublicKey>() {
|
return new Iterator<UncachedPublicKey>() {
|
||||||
|
@ -9,6 +9,7 @@ import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
|||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -44,14 +45,19 @@ public class UncachedPublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Date getExpiryTime() {
|
public Date getExpiryTime() {
|
||||||
Date creationDate = getCreationTime();
|
long seconds = mPublicKey.getValidSeconds();
|
||||||
if (mPublicKey.getValidDays() == 0) {
|
if (seconds > Integer.MAX_VALUE) {
|
||||||
|
Log.e(Constants.TAG, "error, expiry time too large");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (seconds == 0) {
|
||||||
// no expiry
|
// no expiry
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Date creationDate = getCreationTime();
|
||||||
Calendar calendar = GregorianCalendar.getInstance();
|
Calendar calendar = GregorianCalendar.getInstance();
|
||||||
calendar.setTime(creationDate);
|
calendar.setTime(creationDate);
|
||||||
calendar.add(Calendar.DATE, mPublicKey.getValidDays());
|
calendar.add(Calendar.SECOND, (int) seconds);
|
||||||
|
|
||||||
return calendar.getTime();
|
return calendar.getTime();
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ public class SaveKeyringParcel implements Parcelable {
|
|||||||
public static class SubkeyChange implements Serializable {
|
public static class SubkeyChange implements Serializable {
|
||||||
public long mKeyId;
|
public long mKeyId;
|
||||||
public Integer mFlags;
|
public Integer mFlags;
|
||||||
|
// this is a long unix timestamp, in seconds (NOT MILLISECONDS!)
|
||||||
public Long mExpiry;
|
public Long mExpiry;
|
||||||
public SubkeyChange(long keyId, Integer flags, Long expiry) {
|
public SubkeyChange(long keyId, Integer flags, Long expiry) {
|
||||||
mKeyId = keyId;
|
mKeyId = keyId;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user