support encryption subkeys with future signature dates
This commit is contained in:
@@ -48,7 +48,7 @@ import timber.log.Timber;
|
|||||||
*/
|
*/
|
||||||
public class KeychainDatabase {
|
public class KeychainDatabase {
|
||||||
private static final String DATABASE_NAME = "openkeychain.db";
|
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 final SupportSQLiteOpenHelper supportSQLiteOpenHelper;
|
||||||
|
|
||||||
private static KeychainDatabase sInstance;
|
private static KeychainDatabase sInstance;
|
||||||
@@ -348,6 +348,20 @@ public class KeychainDatabase {
|
|||||||
|
|
||||||
case 30:
|
case 30:
|
||||||
// ignore. this case only came up in an unreleased beta.
|
// 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());
|
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 static class NotFoundException extends Exception {
|
||||||
public NotFoundException() {
|
public NotFoundException() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ public class KeyWritableRepository extends KeyRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Date creation = key.getCreationTime();
|
Date creation = key.getCreationTime();
|
||||||
|
Date bindingSignatureTime = key.getBindingSignatureTime();
|
||||||
Date expiry = key.getExpiryTime();
|
Date expiry = key.getExpiryTime();
|
||||||
if (expiry != null) {
|
if (expiry != null) {
|
||||||
if (key.isExpired()) {
|
if (key.isExpired()) {
|
||||||
@@ -240,7 +241,7 @@ public class KeyWritableRepository extends KeyRepository {
|
|||||||
|
|
||||||
SubKey subKey = SubKey.create(masterKeyId, rank, key.getKeyId(),
|
SubKey subKey = SubKey.create(masterKeyId, rank, key.getKeyId(),
|
||||||
key.getBitStrength(), key.getCurveOid(), key.getAlgorithm(), key.getFingerprint(),
|
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));
|
operations.add(DatabaseBatchInteractor.createInsertSubKey(subKey));
|
||||||
|
|
||||||
++rank;
|
++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,
|
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,
|
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 creationUnixTime = creation.getTime() / 1000;
|
||||||
Long expiryUnixTime = expiry != null ? expiry.getTime() / 1000 : null;
|
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,
|
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) {
|
public static InsertKey createInsertStatement(SupportSQLiteDatabase db) {
|
||||||
@@ -53,7 +55,7 @@ public abstract class SubKey implements KeysModel {
|
|||||||
public void bindTo(InsertKey statement) {
|
public void bindTo(InsertKey statement) {
|
||||||
statement.bind(master_key_id(), rank(), key_id(), key_size(), key_curve_oid(), algorithm(), fingerprint(),
|
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(),
|
can_certify(), can_sign(), can_encrypt(), can_authenticate(), is_revoked(), has_secret(), is_secure(),
|
||||||
creation(), expiry());
|
creation(), expiry(), validFrom());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AutoValue
|
@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_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_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_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_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_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_sub_bad_type),
|
||||||
MSG_KC_SUB_DUP (LogLevel.DEBUG, R.string.msg_kc_sub_dup),
|
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();
|
: PGPSignature.SUBKEY_REVOCATION).hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpired () {
|
public boolean isExpired() {
|
||||||
Date expiry = getExpiryTime();
|
Date expiry = getExpiryTime();
|
||||||
return expiry != null && expiry.before(new Date());
|
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() {
|
public boolean isSecure() {
|
||||||
return PgpSecurityConstants.checkForSecurityProblems(this) == null;
|
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. */
|
/** Returns whether this key is valid, ie not expired or revoked. */
|
||||||
public boolean isValid() {
|
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.
|
// 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.security.SignatureException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -651,7 +651,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
|
|||||||
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
|
PGPEncryptedDataGenerator cPk, long encryptMasterKeyId) {
|
||||||
try {
|
try {
|
||||||
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(encryptMasterKeyId);
|
CanonicalizedPublicKeyRing keyRing = mKeyRepository.getCanonicalizedPublicKeyRing(encryptMasterKeyId);
|
||||||
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
|
List<Long> encryptSubKeyIds = mKeyRepository.getPublicEncryptionIds(encryptMasterKeyId);
|
||||||
for (Long subKeyId : encryptSubKeyIds) {
|
for (Long subKeyId : encryptSubKeyIds) {
|
||||||
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
|
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
|
||||||
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
|
cPk.addMethod(key.getPubKeyEncryptionGenerator(data.isHiddenRecipients()));
|
||||||
|
|||||||
@@ -912,13 +912,6 @@ public class UncachedKeyRing {
|
|||||||
continue;
|
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)) {
|
if (cert.getCreationTime().before(keyCreationTime)) {
|
||||||
// Signature is earlier than key creation time
|
// Signature is earlier than key creation time
|
||||||
log.add(LogType.MSG_KC_SUB_BAD_TIME_EARLY, indent);
|
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,
|
is_secure INTEGER AS Boolean NOT NULL,
|
||||||
creation INTEGER NOT NULL,
|
creation INTEGER NOT NULL,
|
||||||
expiry INTEGER,
|
expiry INTEGER,
|
||||||
|
validFrom INTEGER NOT NULL,
|
||||||
PRIMARY KEY(master_key_id, rank),
|
PRIMARY KEY(master_key_id, rank),
|
||||||
FOREIGN KEY(master_key_id) REFERENCES
|
FOREIGN KEY(master_key_id) REFERENCES
|
||||||
keyrings_public(master_key_id) ON DELETE CASCADE
|
keyrings_public(master_key_id) ON DELETE CASCADE
|
||||||
@@ -27,8 +28,8 @@ insertKey:
|
|||||||
INSERT INTO keys (
|
INSERT INTO keys (
|
||||||
master_key_id, rank, key_id, key_size, key_curve_oid, algorithm, fingerprint,
|
master_key_id, rank, key_id, key_size, key_curve_oid, algorithm, fingerprint,
|
||||||
can_certify, can_sign, can_encrypt, can_authenticate,
|
can_certify, can_sign, can_encrypt, can_authenticate,
|
||||||
is_revoked, has_secret, is_secure, creation, expiry
|
is_revoked, has_secret, is_secure, creation, expiry, validFrom
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
updateHasSecretByMasterKeyId:
|
updateHasSecretByMasterKeyId:
|
||||||
UPDATE keys
|
UPDATE keys
|
||||||
@@ -91,7 +92,7 @@ SELECT master_key_id
|
|||||||
WHERE key_id = ?;
|
WHERE key_id = ?;
|
||||||
|
|
||||||
selectSubkeysByMasterKeyId:
|
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
|
FROM keys
|
||||||
WHERE master_key_id = ?
|
WHERE master_key_id = ?
|
||||||
ORDER BY rank ASC;
|
ORDER BY rank ASC;
|
||||||
@@ -106,6 +107,12 @@ SELECT fingerprint
|
|||||||
FROM keys
|
FROM keys
|
||||||
WHERE key_id = ?;
|
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:
|
selectEffectiveSignKeyIdByMasterKeyId:
|
||||||
SELECT key_id
|
SELECT key_id
|
||||||
FROM keys
|
FROM keys
|
||||||
|
|||||||
Reference in New Issue
Block a user