support encryption subkeys with future signature dates

master
Vincent Breitmoser 2018-07-20 11:53:32 +02:00
parent 48383211c3
commit e2505dd308
9 changed files with 63 additions and 20 deletions

View File

@ -48,7 +48,7 @@ import timber.log.Timber;
*/
public class KeychainDatabase {
private static final String DATABASE_NAME = "openkeychain.db";
private static final int DATABASE_VERSION = 31;
private static final int DATABASE_VERSION = 32;
private final SupportSQLiteOpenHelper supportSQLiteOpenHelper;
private static KeychainDatabase sInstance;
@ -348,6 +348,20 @@ public class KeychainDatabase {
case 30:
// ignore. this case only came up in an unreleased beta.
case 31:
addSubkeyValidFromField(db);
}
}
private void addSubkeyValidFromField(SupportSQLiteDatabase db) {
try {
db.beginTransaction();
db.execSQL("ALTER TABLE keys ADD COLUMN validFrom INTEGER NOT NULL DEFAULT 0;");
db.execSQL("UPDATE keys SET validFrom = creation");
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}

View File

@ -313,6 +313,11 @@ public class KeyRepository extends AbstractDao {
return mapSingleRowOrThrow(query, SubKey.FACTORY.selectEffectiveAuthKeyIdByMasterKeyIdMapper());
}
public List<Long> getPublicEncryptionIds(long masterKeyId) {
SqlDelightQuery query = SubKey.FACTORY.selectEffectiveEncryptionKeyIdsByMasterKeyId(masterKeyId);
return mapAllRows(query, SubKey.FACTORY.selectEffectiveEncryptionKeyIdsByMasterKeyIdMapper());
}
public static class NotFoundException extends Exception {
public NotFoundException() {
}

View File

@ -225,6 +225,7 @@ public class KeyWritableRepository extends KeyRepository {
}
Date creation = key.getCreationTime();
Date bindingSignatureTime = key.getBindingSignatureTime();
Date expiry = key.getExpiryTime();
if (expiry != null) {
if (key.isExpired()) {
@ -240,7 +241,7 @@ public class KeyWritableRepository extends KeyRepository {
SubKey subKey = SubKey.create(masterKeyId, rank, key.getKeyId(),
key.getBitStrength(), key.getCurveOid(), key.getAlgorithm(), key.getFingerprint(),
c, s, e, a, key.isRevoked(), SecretKeyType.UNAVAILABLE, key.isSecure(), creation, expiry);
c, s, e, a, key.isRevoked(), SecretKeyType.UNAVAILABLE, key.isSecure(), creation, expiry, bindingSignatureTime);
operations.add(DatabaseBatchInteractor.createInsertSubKey(subKey));
++rank;

View File

@ -31,11 +31,13 @@ public abstract class SubKey implements KeysModel {
public static SubKey create(long masterKeyId, long rank, long keyId, Integer keySize, String keyCurveOid,
int algorithm, byte[] fingerprint, boolean canCertify, boolean canSign, boolean canEncrypt, boolean canAuth,
boolean isRevoked, SecretKeyType hasSecret, boolean isSecure, Date creation, Date expiry) {
boolean isRevoked, SecretKeyType hasSecret, boolean isSecure, Date creation, Date expiry,
Date validFrom) {
long creationUnixTime = creation.getTime() / 1000;
Long expiryUnixTime = expiry != null ? expiry.getTime() / 1000 : null;
long validFromTime = validFrom.getTime() / 1000;
return new AutoValue_SubKey(masterKeyId, rank, keyId, keySize, keyCurveOid, algorithm, fingerprint, canCertify,
canSign, canEncrypt, canAuth, isRevoked, hasSecret, isSecure, creationUnixTime, expiryUnixTime);
canSign, canEncrypt, canAuth, isRevoked, hasSecret, isSecure, creationUnixTime, expiryUnixTime, validFromTime);
}
public static InsertKey createInsertStatement(SupportSQLiteDatabase db) {
@ -53,7 +55,7 @@ public abstract class SubKey implements KeysModel {
public void bindTo(InsertKey statement) {
statement.bind(master_key_id(), rank(), key_id(), key_size(), key_curve_oid(), algorithm(), fingerprint(),
can_certify(), can_sign(), can_encrypt(), can_authenticate(), is_revoked(), has_secret(), is_secure(),
creation(), expiry());
creation(), expiry(), validFrom());
}
@AutoValue

View File

@ -455,7 +455,6 @@ public abstract class OperationResult implements Parcelable {
MSG_KC_SUB_BAD_ERR(LogLevel.WARN, R.string.msg_kc_sub_bad_err),
MSG_KC_SUB_BAD_LOCAL(LogLevel.WARN, R.string.msg_kc_sub_bad_local),
MSG_KC_SUB_BAD_KEYID(LogLevel.WARN, R.string.msg_kc_sub_bad_keyid),
MSG_KC_SUB_BAD_TIME(LogLevel.WARN, R.string.msg_kc_sub_bad_time),
MSG_KC_SUB_BAD_TIME_EARLY(LogLevel.WARN, R.string.msg_kc_sub_bad_time_early),
MSG_KC_SUB_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_sub_bad_type),
MSG_KC_SUB_DUP (LogLevel.DEBUG, R.string.msg_kc_sub_dup),

View File

@ -134,11 +134,33 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
: PGPSignature.SUBKEY_REVOCATION).hasNext();
}
public boolean isExpired () {
public boolean isExpired() {
Date expiry = getExpiryTime();
return expiry != null && expiry.before(new Date());
}
private boolean hasFutureSigningDate() {
if (isMasterKey()) {
return false;
}
WrappedSignature subkeyBindingSignature = getSubkeyBindingSignature();
return subkeyBindingSignature.getCreationTime().after(new Date());
}
private WrappedSignature getSubkeyBindingSignature() {
Iterator subkeyBindingSignatures = mPublicKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING);
PGPSignature singleSubkeyBindingsignature = (PGPSignature) subkeyBindingSignatures.next();
if (subkeyBindingSignatures.hasNext()) {
throw new IllegalStateException();
}
return new WrappedSignature(singleSubkeyBindingsignature);
}
public Date getBindingSignatureTime() {
return isMasterKey() ? getCreationTime() : getSubkeyBindingSignature().getCreationTime();
}
public boolean isSecure() {
return PgpSecurityConstants.checkForSecurityProblems(this) == null;
}
@ -206,7 +228,7 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
/** Returns whether this key is valid, ie not expired or revoked. */
public boolean isValid() {
return !isRevoked() && !isExpired();
return !isRevoked() && !isExpired() && !hasFutureSigningDate();
}
// For use in key export only; returns the public key in a JCA compatible format.

View File

@ -32,7 +32,7 @@ import java.io.UnsupportedEncodingException;
import java.security.SignatureException;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context;
@ -651,7 +651,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
try {
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(encryptMasterKeyId);
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
List<Long> encryptSubKeyIds = mKeyRepository.getPublicEncryptionIds(encryptMasterKeyId);
for (Long subKeyId : encryptSubKeyIds) {
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));

View File

@ -912,13 +912,6 @@ public class UncachedKeyRing {
continue;
}
if (cert.getCreationTime().after(nowPlusOneDay)) {
// Creation date in the future? No way!
log.add(LogType.MSG_KC_SUB_BAD_TIME, indent);
badCerts += 1;
continue;
}
if (cert.getCreationTime().before(keyCreationTime)) {
// Signature is earlier than key creation time
log.add(LogType.MSG_KC_SUB_BAD_TIME_EARLY, indent);

View File

@ -18,6 +18,7 @@ CREATE TABLE IF NOT EXISTS keys (
is_secure INTEGER AS Boolean NOT NULL,
creation INTEGER NOT NULL,
expiry INTEGER,
validFrom INTEGER NOT NULL,
PRIMARY KEY(master_key_id, rank),
FOREIGN KEY(master_key_id) REFERENCES
keyrings_public(master_key_id) ON DELETE CASCADE
@ -27,8 +28,8 @@ insertKey:
INSERT INTO keys (
master_key_id, rank, key_id, key_size, key_curve_oid, algorithm, fingerprint,
can_certify, can_sign, can_encrypt, can_authenticate,
is_revoked, has_secret, is_secure, creation, expiry
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
is_revoked, has_secret, is_secure, creation, expiry, validFrom
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
updateHasSecretByMasterKeyId:
UPDATE keys
@ -91,7 +92,7 @@ SELECT master_key_id
WHERE key_id = ?;
selectSubkeysByMasterKeyId:
SELECT master_key_id, rank, key_id, key_size, key_curve_oid, algorithm, fingerprint, can_certify, can_sign, can_encrypt, can_authenticate, is_revoked, has_secret, is_secure, creation, expiry
SELECT master_key_id, rank, key_id, key_size, key_curve_oid, algorithm, fingerprint, can_certify, can_sign, can_encrypt, can_authenticate, is_revoked, has_secret, is_secure, creation, expiry, validFrom
FROM keys
WHERE master_key_id = ?
ORDER BY rank ASC;
@ -106,6 +107,12 @@ SELECT fingerprint
FROM keys
WHERE key_id = ?;
selectEffectiveEncryptionKeyIdsByMasterKeyId:
SELECT key_id
FROM keys
WHERE is_revoked = 0 AND is_secure = 1 AND ( expiry IS NULL OR expiry >= strftime('%s', 'now') ) AND validFrom <= strftime('%s', 'now')
AND can_encrypt = 1 AND master_key_id = ?;
selectEffectiveSignKeyIdByMasterKeyId:
SELECT key_id
FROM keys