support encryption subkeys with future signature dates
This commit is contained in:
parent
48383211c3
commit
e2505dd308
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue