wrapped-key-ring: introduce Uncached*Key objects

This commit is contained in:
Vincent Breitmoser 2014-05-04 16:56:44 +02:00
parent 411b4cfeb2
commit 8cf0638f54
5 changed files with 179 additions and 129 deletions

View File

@ -1,139 +1,28 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPOnePassSignature; import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class CachedPublicKey { public class CachedPublicKey extends UncachedPublicKey {
// this is the parent key ring // this is the parent key ring
final CachedKeyRing mRing; final CachedKeyRing mRing;
private final PGPPublicKey mKey;
CachedPublicKey(CachedKeyRing ring, PGPPublicKey key) { CachedPublicKey(CachedKeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring; mRing = ring;
mKey = key;
}
public long getKeyId() {
return mKey.getKeyID();
}
public boolean isRevoked() {
return mKey.isRevoked();
}
public Date getCreationTime() {
return mKey.getCreationTime();
}
public Date getExpiryTime() {
Date creationDate = getCreationTime();
if (mKey.getValidDays() == 0) {
// no expiry
return null;
}
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, mKey.getValidDays());
return calendar.getTime();
}
public boolean isExpired() {
Date creationDate = mKey.getCreationTime();
Date expiryDate = mKey.getValidSeconds() > 0
? new Date(creationDate.getTime() + mKey.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
public boolean isMasterKey() {
return mKey.isMasterKey();
}
public int getAlgorithm() {
return mKey.getAlgorithm();
} }
public IterableIterator<String> getUserIds() { public IterableIterator<String> getUserIds() {
return new IterableIterator<String>(mKey.getUserIDs()); return new IterableIterator<String>(mPublicKey.getUserIDs());
}
private Integer mCacheUsage = null;
@SuppressWarnings("unchecked")
public int getKeyUsage() {
if(mCacheUsage == null) {
mCacheUsage = 0;
if (mKey.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mKey.getSignatures())) {
if (mKey.isMasterKey() && sig.getKeyID() != mKey.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
mCacheUsage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
mCacheUsage |= unhashed.getKeyFlags();
}
}
}
}
return mCacheUsage;
}
public boolean canAuthenticate() {
return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
}
public boolean canCertify() {
return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
}
public boolean canEncrypt() {
if (!mKey.isEncryptionKey()) {
return false;
}
// special cases
if (mKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (mKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
return mKey.getVersion() <= 3 ||
(getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
}
public boolean canSign() {
// special case
if (mKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
return true;
}
return mKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
} }
public CachedKeyRing getKeyRing() { public CachedKeyRing getKeyRing() {
@ -141,21 +30,21 @@ public class CachedPublicKey {
} }
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() { JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
return new JcePublicKeyKeyEncryptionMethodGenerator(mKey); return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
} }
public void initSignature(PGPSignature sig) throws PGPException { public void initSignature(PGPSignature sig) throws PGPException {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider() new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
sig.init(contentVerifierBuilderProvider, mKey); sig.init(contentVerifierBuilderProvider, mPublicKey);
} }
public void initSignature(PGPOnePassSignature sig) throws PGPException { public void initSignature(PGPOnePassSignature sig) throws PGPException {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider() new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
sig.init(contentVerifierBuilderProvider, mKey); sig.init(contentVerifierBuilderProvider, mPublicKey);
} }
/** Verify a signature for this pubkey, after it has been initialized by the signer using /** Verify a signature for this pubkey, after it has been initialized by the signer using
@ -164,18 +53,10 @@ public class CachedPublicKey {
*/ */
public boolean verifySignature(PGPSignature sig, String uid) throws PGPException { public boolean verifySignature(PGPSignature sig, String uid) throws PGPException {
try { try {
return sig.verifyCertification(uid, mKey); return sig.verifyCertification(uid, mPublicKey);
} catch (SignatureException e) { } catch (SignatureException e) {
throw new PGPException("Error!", e); throw new PGPException("Error!", e);
} }
} }
public byte[] getFingerprint() {
return mKey.getFingerprint();
}
// Note that this method has package visibility - no access outside the pgp package!
PGPPublicKey getKey() {
return mKey;
}
} }

View File

@ -72,7 +72,7 @@ public class CachedPublicKeyRing extends CachedKeyRing {
boolean validPrimaryKeyBinding = false; boolean validPrimaryKeyBinding = false;
PGPPublicKey masterKey = getRing().getPublicKey(); PGPPublicKey masterKey = getRing().getPublicKey();
PGPPublicKey subKey = cachedSubkey.getKey(); PGPPublicKey subKey = cachedSubkey.getPublicKey();
// Is this the master key? Match automatically, then. // Is this the master key? Match automatically, then.
if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) { if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {

View File

@ -1,6 +1,5 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKey;
@ -157,7 +156,7 @@ public class CachedSecretKey extends CachedPublicKey {
} }
// get the master subkey (which we certify for) // get the master subkey (which we certify for)
PGPPublicKey publicKey = publicKeyRing.getSubkey().getKey(); PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
// fetch public key ring, add the certification and return it // fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) { for (String userId : new IterableIterator<String>(userIds.iterator())) {
@ -175,4 +174,8 @@ public class CachedSecretKey extends CachedPublicKey {
// the private key is called without a previous call to unlock() // the private key is called without a previous call to unlock()
} }
public UncachedSecretKey getUncached() {
return new UncachedSecretKey(mSecretKey);
}
} }

View File

@ -0,0 +1,140 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class UncachedPublicKey {
protected final PGPPublicKey mPublicKey;
private Integer mCacheUsage = null;
public UncachedPublicKey(PGPPublicKey key) {
mPublicKey = key;
}
public long getKeyId() {
return mPublicKey.getKeyID();
}
public boolean isRevoked() {
return mPublicKey.isRevoked();
}
public Date getCreationTime() {
return mPublicKey.getCreationTime();
}
public Date getExpiryTime() {
Date creationDate = getCreationTime();
if (mPublicKey.getValidDays() == 0) {
// no expiry
return null;
}
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, mPublicKey.getValidDays());
return calendar.getTime();
}
public boolean isExpired() {
Date creationDate = mPublicKey.getCreationTime();
Date expiryDate = mPublicKey.getValidSeconds() > 0
? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
public boolean isMasterKey() {
return mPublicKey.isMasterKey();
}
public int getAlgorithm() {
return mPublicKey.getAlgorithm();
}
public boolean isElGamalEncrypt() {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
public boolean isDSA() {
return getAlgorithm() == PGPPublicKey.DSA;
}
@SuppressWarnings("unchecked")
public int getKeyUsage() {
if(mCacheUsage == null) {
mCacheUsage = 0;
if (mPublicKey.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
mCacheUsage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
mCacheUsage |= unhashed.getKeyFlags();
}
}
}
}
return mCacheUsage;
}
public boolean canAuthenticate() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
}
public boolean canCertify() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
}
public boolean canEncrypt() {
if (!mPublicKey.isEncryptionKey()) {
return false;
}
// special cases
if (mPublicKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
return mPublicKey.getVersion() <= 3 ||
(getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
}
public boolean canSign() {
// special case
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
return true;
}
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
}
public byte[] getFingerprint() {
return mPublicKey.getFingerprint();
}
// Note that this method has package visibility - no access outside the pgp package!
PGPPublicKey getPublicKey() {
return mPublicKey;
}
}

View File

@ -0,0 +1,26 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPSecretKey;
import java.io.IOException;
import java.io.OutputStream;
public class UncachedSecretKey extends UncachedPublicKey {
final PGPSecretKey mSecretKey;
public UncachedSecretKey(PGPSecretKey secretKey) {
super(secretKey.getPublicKey());
mSecretKey = secretKey;
}
@Deprecated
public PGPSecretKey getSecretKeyExternal() {
return mSecretKey;
}
public void encodeSecretKey(OutputStream os) throws IOException {
mSecretKey.encode(os);
}
}